Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I dynamically unload and reload (other versions of the same) JAR?

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.

like image 288
ivan_ivanovich_ivanoff Avatar asked Apr 08 '09 00:04

ivan_ivanovich_ivanoff


People also ask

How do I load a dynamic jar?

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.

Is it possible to load a class by two 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.

Which ClassLoader loads JAR files from JDK?

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.

How do you refresh a class in Java?

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.


4 Answers

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.

like image 174
Steve Reed Avatar answered Oct 20 '22 00:10

Steve Reed


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/

like image 42
techzen Avatar answered Oct 19 '22 23:10

techzen


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.

like image 26
Flueras Bogdan Avatar answered Oct 19 '22 22:10

Flueras Bogdan


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

like image 28
TofuBeer Avatar answered Oct 19 '22 22:10

TofuBeer