|
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.
|