Thursday, September 24, 2009

How to make JNI apps reloadable

JNI has this annoying "feature" , that makes is very difficult to run an application ( or ant task, or whatever) that loads a native library.
When you load a class ( lets call it Foo ) that loads loads a DLL, loadLibrary is called on the ClassLoader that loaded Foo.
If you try to load the Foo class again, and the library is still loaded, then loadLibrary will fail with a java.lang.UnsatisfiedLinkError.

The DLL that Foo loaded will NOT be unloaded when Foo is garbage collected. The DLL will not be unloaded until the ClassLoader, and all the other classes that were loaded with Foo's ClassLoader are garbage collected. If that ClassLoader is the System ClassLoader, it will never be unloaded while the JVM is running.

This makes it an exceptional pain to try and re-deploy an app to an application server.
There is a lot of whining about this, and a lot of bad advice. So here is a WORKING solution.

A) All the classes that load native libraries (JNI clients) should implement interfaces for their use.
B) All the other classes should ONLY reference the interfaces, never the classes themselves.
C) JNI client classes must be loaded by a separate classloader then the other classes.
D) The instances of the JNI client classes must be instantiated by reflection, not by directly calling a class’s constructor. This prevents the class being loaded by a non-JNI ClassLoader

To accomplish this, I created a VolitileFactory class, that subclasses URLClassLoader. It has a makeInstance method, which loads the JNI client classes, and invokes the correct constructor via reflection. The VolitileFactory instance is a field of the Anchor class. Anchor has static methods to obtain implementers of the JNI client’s interfaces. Anchor holds the only reference to the factory. When the app shuts down, that reference is deleted, System.gc() is called, and the libraries unload.

No comments:

Post a Comment