Thursday, August 12, 2010

Java - some gotchas with NetBeans generated JAR files

The problem
JAR files are a great way to distribute Java applications. A single file contains everything and is easy to use with a double-click. However making everything work from the JAR is another question and here are two problem areas. NetBeans 6.9.1 is being used to build the project.

Library classes
Class files in a library (often a JAR or ZIP file) are placed by the NetBeans build process in a folder named lib alongside the application JAR. The JAR manifest adds the location to its classpath entry and hence, when the JAR is executed Java knows where to find the library code. I could not find a way to force NetBeans to simply add these classes (as if they had just been compiled!) to the root of the JAR.

Everything is fine though if you use the Java jar program to update the JAR before distributing with a couple of provisos to do with the way that jar handles things:

  • We are using  jar -uf    ... to update the JAR file with the entire contents of the directories of classes. You have to execute this from the directory in which dir1 etc are stored - jar does not correctly handle paths added to the locations of directories to be added. Since NetBeans uses a dist directory within the project folder for its distribution JAR it makes sense to do all this from the "project root" and to place the directories of library classes right there
  • An example - we want to add the entire tree of classes under sunsoft to the application JAR file at dist/netsim.jar:
    • jar -uf dist\netsim.jar sunsoft
Resources from the application JAR file
This is much written about but a few things do not work as they ought. The basic idea is to use the class getResource() method to access whatever it is (picture, sound etc) - by using this call, which will include Java's knowledge of how the class was loaded, the same code should work from a JAR or with normal execution.

getResource() returns the URL that describes the resource's location which can then be passed on to the appropriate loader for a sound or an image or whatever. getResource() takes a single parameter with is the "path" to the resource. For a program that will simply work from a JAR or from the file system this "path" needs to make sense within the JAR file as well as in the file system.

The critical thing is use a leading forward slash - so for example getResource("/card.jpg") will return the URL of the JPG whether it is in the root of the JAR file or simply in the file system alongside the directories containing the application classes (the "root" of the program I suppose)

There is one more NetBeans problem. NetBean is smart enough to place resources from its src directory in the root of the application JAR file when it builds -but they not then seen by getResource().
To make things work they must be manually re-added using jar as above. If the resources are small you can live with the duplicate copies - if they are large delete the NetBeans copies first - in NetBeans file view.

No comments: