Class JarClassLoader

Description
The class loader to load classes, native libraries and resources from the top JAR and from JARs inside top JAR. The loading process looks through the hierarchy of JARs and allows for their tree structure (i.e. nested JARs). The class loader delegates class loading to the parent class loader and successfully loads classes, native libraries and resources when it works not in a JAR environment.

Using this class loader gives you the option to deploy your application in a single deployment JAR. This means that all third party libraries in JARs (even signed JARs) and native libraries could be put into this single deployment JAR in their original state. Access to any resource in this single deployment JAR is transparent. All JARs in the deployment JAR and all nested JARs are considered to be added to the application classpath regardless of their names and locations. All native libraries in the deployment JAR and nested JARs are considered to be added to the native library path regardless of their names and locations.

The class size is only ~800 lines (including all instructions in JavaDoc).

There are multiple benefits of using this class loader instead of using default system class loader:

  • Simplifies deployment. Only a single JAR is deployed.
  • Obfuscates dependencies. All dependent JARs are hidden into a single deployment JAR.
  • Simplifies application support. Reduces risk of lost, replaced or misused dependent JARs.
  • Simplifies classpath. All dependent JARs in deployment JAR are considered in the classpath.
  • Simplifies native libraries loading. All native libraries in deployment JAR are considered in the native libraries path.
  • Eliminates native libraries loading issues. Reduces risk of lost, replaced or misused native libraries.
  • Convenient in development stage. Uses transparent loading classes, native libraries and resources from a JAR or file system.

It is possible to use this class loader without any special knowledge of class loading in Java and without customizing or learning internals of this class loader. It works for you out of the box. Instructions for how to use this class loader are below and in the class JavaDoc. See also discussion Using JARs.

The class loader is very simple to use in comparison with other available JAR class loaders. This class loader does not require JAR manifest or system environment modifications, special application build, strict JAR structure or any kind of properties or settings. Just add this class loader and adjusted trivial Launcher class to your application. The Launcher class has only 12 lines of code.

The class loader was tested with Java 5 and Java 6 on WinXP and Linux platforms.

Similar products
There are many free JAR class loaders available on the web. None of them are perfect or easy to use.

  • Classworlds The package is titled advanced classloader framework. The version 1.0 R3 was published in 2003. The final release is not available. The package consists of 17 files with a total size of ~3600 lines. It does not support loading native libraries. It also requires an external configuration file. There are no clear instructions for how to use the package.
  • Meta Jar Utilities The version 1.7.1 was published in 2002. The package consists of 6 files with a total size of ~2200 lines. The package does not support loading native libraries. There are two class loaders in the package. These class loaders have code that is duplicate and commented out. Instructions are not clear as to how and when the class loaders should be used.
  • One-JAR The version 0.96 RC5 was published in 2007. The package consists of 4 files with a total size of ~1700 lines. This is the most mature product in comparison with others. The package supports loading native libraries. The web site contains too many confusing JARs to download: sdk, example, dll, ant-task, boot. Nested JARs are not supported. The class loader requires a strictly specified structure of the main root JAR. The class loader is controlled by MANIFEST.MF attributes. The package is over-engineered.
  • Embedded jar classloader in under 100 lines Simple JAR class loader which essentially repeats our JarClassLoader. The class loader is controlled by MANIFEST.MF attribute. It cannot load native libraries and external LookAndFeel classes. Documentation is minimal.

How to use the JarClassLoader
Follow these steps:

1. Create your main root JAR. Put in it all classes, resources, native libraries and other JARs required by your application. JARs could be nested, i.e. you may have JARs in other JARs with any level of nesting. JarClassLoader ignores location of JARs and native libraries in the main root JAR. This gives you an option to organize main root JAR content.

2. Add class com.jdotsoft.jarloader.JarClassLoader to the main root JAR.

3. Create a launcher class using the following template and add it to the main root JAR:

package com.mycompany;
import com.jdotsoft.jarloader.JarClassLoader;

public class Launcher {

    public static void main(String[] args) {
        JarClassLoader jcl = new JarClassLoader();
        try {
            jcl.invokeMain("com.mycompany.MyApp", args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    } // main()
    
} // class Launcher
Update the package declaration in the example above. Update the parameter in the method JarClassLoader.invokeMain() with package + classname of your application main class. Pick any name for the package and class name.

4. Start your application using the launcher class com.mycompany.Launcher.main() created in the previous step with one of the following commands:

    (1) java -cp MyApp.jar com.mycompany.Launcher Hello World
    (2) java -jar MyApp.jar Hello World

The command #2 is available if you have the following entry in the manifest file:
    Main-Class: com.mycompany.Launcher

Any parameters in the examples of command lines given above like Hello World are optional and passed to your class com.mycompany.MyApp.main() method by JarClassLoader.

Using the launcher class is transparent in application development stages when all resources are not yet packed into a single JAR. The JarClassLoader checks its location: JAR or file system. If JarClassLoader finds that it is loaded from a file system it passes all control to the system class loader. The system class loader than loads classes from a classpath and handles native libraries accordingly.

Loading external LookAndFeel
The UIManager registers UI classes by their string names. This means that LookAndFeel class does not keep references to the UI classes and they are not loaded by the class loader at the time LookAndFeel class is loaded.
When a JComponent needs a UI class, it asks the UIManager for it. The UIManager uses JComponent class loader, not the LookAndFeel class loader. The JComponent class loader is usually the system class loader, which fails to load the requested UI class from a JAR.

A workaround is to preload LookAndFeel UI classes from a JAR. There are two options to do this.
Option 1. Default implicit preloading by JarClassLoader on UIManager propertyChange event. This option does not require any application code modification but it has a drawback: it does not catch ClassNotFoundException which is converted into RuntimeException.
Option 2. Explicit preloading in the application code with an option to catch ClassNotFoundException:

    try {
        UIManager.setLookAndFeel("com.myexternal.LaF");
    } catch (UnsupportedLookAndFeelException e) {
        System.out.println("Cannot load LaF: " + e);
    }
    try {
        JarClassLoader.loadLookAndFeel();
    } catch (ClassNotFoundException e) {
        System.out.println("Cannot load LaF: " + e);
    }
See more details in the forum.

The call JarClassLoader.loadLookAndFeel() does nothing for standard LookAndFeel UI classes.

Design constraints and known limitations
See also class JavaDoc for details of how the class loader works.

  Due to Java API limitations inner JARs are extracted into temporary files. These temporary files are used to load JARs. The temporary files are created by java.io.File.createTempFile() and marked with a flag deleteOnExit. As a result they are deleted by the system at the time they are closed on application shutdown. The application shutdown event is intercepted by Runtime.addShutdownHook(). Using temporary files for loading JARs could be avoided if one of the following constructors is available in Java API:

    java.util.jar.JarFile(java.io.InputStream)
    java.util.jar.JarFile(java.util.jar.JarEntry)
    java.io.File(java.util.jar.JarEntry)

  Due to Java API limitations native libraries are extracted into temporary files. These temporary files are used to load native libraries. Using temporary files for loading native libraries could be avoided if java.lang.ClassLoader class and VM have an option to load native library from a stream. Currently VM loads native libraries using method String findLibrary(String) where String parameter is an absolute path of the native library. For example, the absolute path of some native library Native.dll on the Windows platform in the main root JAR is defined as

    jar:file:/C:/.../MyApp.jar!/lib/Native.dll

The URL form of the absolute path does not work for loading native libraries and it is not defined at all for nested resources. Temporary files with native libraries are created by java.io.File.createTempFile() and marked with a flag deleteOnExit. VM (as of Java 1.6.0_05) loads them and it is expected that VM will close them on application shutdown and they will be deleted. VM fails to close them because native libraries are unloaded in package accessed inner class java.lang.ClassLoader$NativeLibrary.finalize() that is never called. From JavaDoc: finalize() is called by the garbage collector on an object when garbage collector determines that there are no more references to the object. GC for sure is not launched on application shutdown, thus native libraries are never unloaded during the application life span. Attempting to explicitly delete these temporary files on shutdown by calling File.delete() fails because VM does not release these files' handles. The class java.lang.ClassLoader does not give any options to unload native libraries manually by declaring private and package accessed methods and private members. As a result temporary files with native libraries remain hanging in the file system after the application shutdown. As a workaround the list with the temporary files of these native libraries is preserved in [user.home]/.JarClassLoader file that are deleted next time the application starts.

Download
Click here to download the class, here to download Eclipse project with demo and unit tests.

License
This Java class is licensed under GNU General Public License version 2 as published by the Free Software Foundation. Commercial license is available.

Comments


Posted by Troy on April 10, 2008, 11:49 PM

It works! Thanks again.

Posted by Gianfranco on July 4, 2008, 1:13 AM

I must say this is so much simpler then One-Jar.

Posted by Martin on September 15, 2008, 5:13 am

I tried to use your class in combination with SUNs Webservice development kit. I get a class not found exception for the class com.sun.xml.ws.spi.ProviderImpl. When I try to load this class explicitly by Class.forName I get no error. So I guess your class works fine but in my special case the system classloader tries to load this class and can\'t find it. Any suggestions?

Posted by admin on September 15, 2008, 9:53 am

I cannot test or reproduce the problem because I do not have your environment. I would suggest to preload the problematic class in a Launcher. I guess the problem is very similar to loading LnF classes. See Loading external LookAndFeel section on this page and JarClassLoader.loadLookAndFeel() JavaDoc.

Leave a comment

  CAPTCHA Image

We record your IP address 38.103.63.56 (located here) and will report abuse of this form to authorities.

Submit Comment