Thursday, 13th October 2011
Follow WikiJava on twitter now. @Wikijava

Packing library packages into a jar file

From WikiJava

Jump to: navigation, search
The author suggests:

buy this book

This article shows how to create jars containing also all the referenced libraries, this is a standard practice in J2EE programming, where in most of the cases Java programs come packaged into single .war or .ear files, that contain all the necessary code (your program and the referenced libraries). It is a bit more tricky when it comes to do the same in a JSE .jar file. I'll explain below how the jar file should look like. Refer to the article include libraries in a jar file using ANT.


Contents

What we want to achieve

Whenever I choose to use a new program one of the factors I always take in consideration is its complexity and the difficulty of installation. In general I tend to prefer programs that are standalone and portable. The perfect program for me does not require installation and I can move it to different directories or even to a USB stick, and it still works keeping the configuration as well. If additionally the program is in Java then I'm very happy, because I can install, move and use the program freely even across different platforms.

Creating such software is not too complex for the programmer, and with very few adjustments it becomes very easy to make your software portable and standalone.

What we want to achieve is a unique jar file: say program.jar which is the only file needed to start your program.

program.jar we would like is a zip file (every jar file is a zip file) containing a file structure similar to the following:

+- uncompressed
 +- lib
  - library1.jar
  - library2.jar
  - ...  
 +- META-INF
  - MANIFEST.MF
 +- org
 +- wikijava
  +- package structure .... 
    - classFile1.class
    - classFile2.class
    - ...


The problem

Unfortunately the directory structure mentioned just above is typical of J2ee, and not implemented in the Java Standard edition class loader. See | this page for details about how to use the manifest in a JSE environment.

Note: The Class-Path header points to classes or JAR files on the local network, 
not JAR files within the JAR file or classes accessible over internet protocols. 
To load classes in JAR files within a JAR file into the class path, you must write 
custom code to load those classes. For example, if MyJar.jar contains another 
JAR file called MyUtils.jar, you cannot use the Class-Path header in MyJar.jar's 
manifest to load classes in MyUtils.jar into the class path. 

Basically any classpath mentioned in the manifest file points only to files external to the jar. (either a path relative to the jar is or a fully qualified URL). If you want to use internal Jar files you need to implement your own way to load them.

So are we stuck? Is there any trick?

The answer is: yes and not ....

  • yes, there is something we can do to have a single jar file to distribute.
  • no, the solution is not going to look nice from a clean development standpoint.

The solutions

So far I can imagine two ways to solve this:

  1. decorate the class loader in order to load classes in the way we like
  2. include the library .class files in the jar in a way that they look like they are our code.

As per the first option, this is complex, and error prone solution. we don't want to mess up with the classloader, which is such a complex and delicate component of the standard Java API.

My preferred solution is the second, where I just decompress the library jars into my jar, in order to get .class files just as they were my own code.

This approach is probably not universal, and may not work in some cases, for example it's to be seen if this works also with classes that are encrypted in the jar. But it's general enough to be considered useful in many, probably most, cases.

Basically what we can do is to decompress each library file and include its files in our jars, in order to look just like follows:

+- program.jar
 +- org
   +- wikijava
    +- package path ....
      - myclassfile1.class
      - myclassfile2.class
      - ...
  +- META-INF
    - MANIFEST.MF
  +- org
   +- apache
    +- poi
     +- uncompressed package path 
       - LibClassFile1.class
       - LibClassFile2.class
       - ...
   +- another uncompressed package path
     - LibClassFile3.class
     - LibClassFile4.class
     - ... 

In this way when the JVM runs your program, the referenced libraries will be automatically in the classpath, exactly as they were code written and compiled by you. The library code is basically merged into yours and (from the JVM's standpoint) completely indistinguishable.

I don't reckon this approach too neat, as unpacking the jar files is not guaranteed to work in all cases, and I don't like too much the idea of mixing library code with proprietary code. But it should be a sufficient solution for most cases. Read the include libraries in a jar file using ANT article for details on how to generate files in this way using ANT builder, see generating a Jar file to see what's the standard way to generate jar files.


Comments from the users

To be notified via mail on the updates of this discussion you can login and click on watch at the top of the page


Comments on wikijava are disabled now, cause excessive spam.