Skip

Properly Putting Data Into a .jar for the Major Platforms
March 28, 2010 5:13 PM   Subscribe

NetbeansFilter: I need to know exactly how to do a very simple thing - package a .jar file correctly so that the Java App inside can read critical data files, across the three major platforms.

I have this app I am making with NetBeans. It generates drop down 'choice' menus such that it first reads the menu items from simple text and .csv files and then adds those items to the menus. The text and .csv files are to ensure that customization of the menus is easy when I pass this on, as this hopefully will have long term use.

Googling for solutions has led to me using the InputStreamReader with a Scanner, yet no complete solution. I have also previously used a File input and Scanner combo on Windows 7, packaged the .jar, and put the data files in the folder with the .jar file to no problem whatsoever.

The problem occurs when trying to cross platforms into Linux or Mac.

Thus the question, how to package the .jar file correctly so that across Windows, Linux, and Mac, the app reads the files correctly and thus works.

Many thanks! Recommendations of better alternative methods of achieving this (easily customizable file/source with list -> menu) are welcome.
posted by JoeXIII007 to Computers & Internet (4 answers total) 1 user marked this as a favorite
 
Put the config files in the same directory as the jar. Or delve deep into the permissions file.

http://java.sun.com/j2se/1.4.2/docs/api/java/io/FilePermission.html

Please note: Code can always read a file from the same directory it's in (or a subdirectory of that directory); it does not need explicit permission to do so.
posted by sammyo at 5:58 PM on March 28, 2010


You can load the resource a couple of ways...
URL urlToResource = this.getClass().getResource("/custom.csv");
InputStream isToResource = this.getClass().getResourceAsStream("/custom.csv");
As long as the resource is in the root of the JAR and you make sure to use a leading slash you should be able to resolve the resource consistently across platforms.
posted by Civil_Disobedient at 6:11 PM on March 28, 2010


Here's some code that may help you do what you are trying to do. This code is adapted from something on javaworld.com, and it's for loading properties, but it can easily be modified to load your csv files instead.

public abstract class PropertiesUtil {
  public static final boolean THROW_ON_LOAD_FAILURE = true;
  private static final boolean LOAD_AS_RESOURCE_BUNDLE = false;
  private static final String SUFFIX = ".properties";
/**
  * Looks up a resource named 'name' in the classpath. The resource must map
  * to a file with .properties extention. The name is assumed to be absolute
  * and can use either "/" or "." for package segment separation with an
  * optional leading "/" and optional ".properties" suffix. Thus, the
  * following names refer to the same resource:
  * 
  * some.pkg.Resource
  * some.pkg.Resource.properties
  * some/pkg/Resource
  * some/pkg/Resource.properties
  * /some/pkg/Resource
  * /some/pkg/Resource.properties
  * 
  * 
  * @param name classpath resource name [may not be null]
  * @param loader classloader through which to load the resource [null
  * is equivalent to the system class loader]
  * 
  * @return resource converted to java.util.Properties [may be null if the
  * resource was not found and THROW_ON_LOAD_FAILURE is false]
  * @throws IllegalArgumentException if the resource was not found and
  * THROW_ON_LOAD_FAILURE is true
  */
  public static Properties loadProperties(String name, ClassLoader loader) {
    if (name == null) {
      throw new IllegalArgumentException("null input: name");
    }

    if (name.startsWith("/")) {
      name = name.substring(1);
    }
    if (name.endsWith(SUFFIX)) {
      name = name.substring(0, name.length() - SUFFIX.length());
    }
    Properties result = null;

    InputStream in = null;
    try {
      if (loader == null) {
        loader = ClassLoader.getSystemClassLoader();
      }
      if (LOAD_AS_RESOURCE_BUNDLE) {
        name = name.replace('/', '.');
        // Throws MissingResourceException on lookup failures:
        final ResourceBundle rb = ResourceBundle.getBundle(name,
                Locale.getDefault(), loader);

        result = new Properties();
        for (Enumeration keys = rb.getKeys(); keys.hasMoreElements();) {
          final String key = keys.nextElement();
          final String value = rb.getString(key);

          result.put(key, value);
        }
      } else {
        name = name.replace('.', '/');

        if (!name.endsWith(SUFFIX)) {
          name = name.concat(SUFFIX);        // Returns null on lookup failures:
        }
        //logger.info(name);
        in = loader.getResourceAsStream(name);
        if (in != null) {
          result = new Properties();
          result.load(in); // Can throw IOException
        }
      }
    } catch (Exception e) {
      logger.log(Level.WARNING, "Error loading properties", e);
      result = null;
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (Throwable ignore) {
        }
      }
    }

    if (THROW_ON_LOAD_FAILURE && (result == null)) {
      throw new IllegalArgumentException("could not load [" + name + "]" +
              " as " + (LOAD_AS_RESOURCE_BUNDLE
              ? "a resource bundle"
              : "a classloader resource"));
    }

    return result;
  }
}

posted by axiom at 2:24 PM on March 29, 2010


Thanks everybody.

Unfortunately, at the expense of whatever hivemind power, I found out that this article, despite it being 2 years old, is still quite true. Following that worked quite flawlessly.

Civil_Disobedient's response likely helped... I also found popping the data files into the src folder of the Netbeans project folder on the HD pretty effective (likely a duh moment).
posted by JoeXIII007 at 8:06 PM on March 29, 2010


« Older What is travel like in New Zea...   |  I would like to know how you h... Newer »
This thread is closed to new comments.


Post