I am writing a server program which is used to run unit tests of an API (displaying lots of information and providing web access to control / monitor the whole thing)...
This API is known to the server during compile time and is provided as a JAR.
To be able to compare between unit test results of different versions of the API (without restarting the server), I want to be able to unload the 'current' version of the API, and to reload a newer one (or an older one).
I don't want to use URLClassLoader and invoke every single
method by name
( using getDeclaredMethod("someMethod")
),
because the server heavily depends on the API and it would be
complicated to 'wrap' every method call in such dirty way.
I was thinking: Since all interfaces of all versions of the JAR are same, couldn't I do it by somehow reloading an other version of the JAR (without that by-name-invokation?).
Note: I am using latest Java SE (6) and Java EE (5).
If you think, what I'm trying to achieve is not possible, please suggest a 'workaround' or a different concept.
Dynamically loading a jar package through a specified link or path can be done using the addURL method of the URLClassLoader , with the following sample code. When creating the URLClassLoader , specify the current system ClassLoader as the parent class loader ClassLoader.
A class is always identified using its fully qualified name (package. classname). So when a class is loaded into JVM, you have an entry as (package, classname, classloader). Therefore the same class can be loaded twice by two different ClassLoader instances.
The Bootstrap class loader loads the basic runtime classes provided by the JVM, plus any classes from JAR files present in the system extensions directory. It is parent to the System class loader. To add JAR files to the system extensions, directory, see Using the Java Optional Package Mechanism.
Once a Java class has been loaded by a class loader, it's immutable and will last as long as the class loader itself. The identity is the class name and class loader identity, so to reload an application, you'll need to create a new class loader which in turn will load the latest version of the app classes.
I think if you load a class using
Class.forName(clsname, init, classloader);
(Javadoc here) you will get an instance of the class provided by the given classloader. Everything loaded because of that class will also be loaded via the same classloader.
As long as you're very careful with the objects instantiated from this point on (to allow for GC), you should be able to reload different versions. I did this once before with Java 1.3, it took a lot of debugging, but at the end I had a "bootstrap" application that loaded a Runnable
class by name and was able to "soft-restart" by instantiating a new classloader against a different URL and going again.
You can use the opensource package : JclLoader which helps in loading different versions of the same jar. This was also a need in one of our systems to do testing .
Link: http://sourceforge.net/projects/jcloader/
You could programatically modify your classpath to reflect your JAR changes. Here is how I would do it:
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
m.setAccessible(true);
m.invoke(urlClassLoader, jarFile.toURI().toURL());
String cp = System.getProperty("java.class.path");
if (cp != null) {
cp += File.pathSeparatorChar + jarFile.getCanonicalPath();
} else {
cp = jarFile.toURI().getPath();
}
System.setProperty("java.class.path", cp);
where jarFile is the version of the jar you want to use/overwrite.
OSGi is a framework that will allow you to do it. JSR 277 the Java Module System is designed for doing that as well (I think). I have not followed the OSGi -vs- JSR 277 debate, so I don't know f they are trying to marge them at all.
You can roll your own with class loaders, but it'll be less "fun".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With