javanullpointerexceptionurlclassloader

Problem reloading a jar using URLClassLoader


I need to add plugin functionality to an existing application for certain parts of the application. I want to be able to add a jar at runtime and the application should be able to load a class from the jar without restarting the app. So far so good. I found some samples online using URLClassLoader and it works fine.

I also wanted the ability to reload the same class when an updated version of the jar is available. I again found some samples and the key to achieving this as I understand is that I need to use a new classloader instance for each new load.

I wrote some sample code but hit a NullPointerException. First let me show you guys the code:

package test.misc;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import plugin.misc.IPlugin;

public class TestJarLoading {

    public static void main(String[] args) {

        IPlugin plugin = null;

        while(true) {
            try {
                File file = new File("C:\\plugins\\test.jar");
                String classToLoad = "jartest.TestPlugin";
                URL jarUrl = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");
                URLClassLoader cl = new URLClassLoader(new URL[] {jarUrl}, TestJarLoading.class.getClassLoader());
                Class loadedClass = cl.loadClass(classToLoad);
                plugin = (IPlugin) loadedClass.newInstance();
                plugin.doProc();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

IPlugin is a simple interface with just one method doProc:

public interface IPlugin {
    void doProc();
}

and jartest.TestPlugin is an implementation of this interface where doProc just prints out some statements.

Now, I package the jartest.TestPlugin class into a jar called test.jar and place it under C:\plugins and run this code. The first iteration runs smoothly and the class loads without issues.

When the program is executing the sleep statement, I replace C:\plugins\test.jar with a new jar containing an updated version of the same class and wait for the next iteration of while. Now here's what I don't understand. Sometimes the updated class gets reloaded without issues i.e. the next iteration runs fine. But sometimes, I see an exception thrown:

java.lang.NullPointerException
at java.io.FilterInputStream.close(FilterInputStream.java:155)
at sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream.close(JarURLConnection.java:90)
at sun.misc.Resource.getBytes(Resource.java:137)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:256)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at test.misc.TestJarLoading.main(TestJarLoading.java:22)

I have searched on the net and scratched my head but can't really arrive at any conclusion as to why this exception is thrown and that too - only sometimes, not always.

I need your experience and expertise to understand this. What's wrong with this code? Please help!!

Let me know if you need any more info. Thanks for looking!


Solution

  • This behaviour is related to a bug in the jvm
    2 workarounds are documented here