tags:

views:

60

answers:

3

I have a JNI library that interacts with a set of third party libraries, and there may be more than one version of the third party library on the system. For each version of the third party library, I have to re-compile the JNI code for comparability reasons. Right now I deal with this by loading a DLL with a specific name, and if the version changes I change the names of the JNI interface DLLs so that the correct one for the version has the right name to get loaded.

I'd like to be able to dynamically load the dll though, based on which version the user wants to use. What happens if I call System.loadLibrary twice on DLLs with different names but the same method signatures?

System.loadLibrary("JNIv1");
// Same code compiled against a different third party version
System.loadLibrary("JNIv2");

I only need to use one of the versions at a time, so it's fine if the old version is no longer accessable.

Is it possible to load two different versions of a DLL with the same method signatures without re-starting the program?

A: 

I don't know the details of how DLL loading in windows works, but my gut feeling tells me it definitely would not be safe to have overlapping libraries loaded at the same time. But maybe if you first unloaded the first one, it might be.

As far as I see, there's no way to explicitly unload a library. If you look at java.lang.Classloader.NativeLibrary you'll see that a library is unloaded when the classloader that caused it to be loaded gets garbage collected (in its finalize() method). So if you load it in a separate classloader, and then wait for it to be garbage collected (with some evil calls to System.gc() to make that happen faster) before loading the new one, that might help.

Wouter Coekaerts
A: 

This is not safe and is quite messy to implement. It is possible to have different classloaders for different versions of the dll. You will have to ensure the classloader is garbage collected to be sure that the dll has been unloaded. (See Java JNI - DLL Unloading)

In the past, we have solved this problem by writing a lightweight java mediator process which handles a user's request and creates a new child java process with the version of the dll requested. When a user requests a different version, the mediator shuts down the existing child and spawns a new one with a different library path. This approach works well. The only downside is the time taken to startup new JVMs, but this will only be noticeable if you have many requests for version switches coming in very frequently.

dogbane
A: 

It is possible and in fact it is totally supported and works brilliantly.

I have had to do this in a production environment and on a sun JVM it is rock solid.

Basically, if you load the library from a different classloader, then it will load a different copy of the library. Its as simple as that.

I wouldn't recommend doing this unless you really have to... but it does work.

As an alternative, depending on your specific requirements, you could just make it out of process, and have a simple protocol (using say jetty/xstream/httpclient, or netty) between the client and the different servers, each of which has a different dll version loaded.

Essentially this involves you writing a classloader

public class MyClassLoader extends URLClassLoader {

   protected String findLibrary(String libName) {
         if ( libName.equals("mylib.dll")) {
              return "full/path/to/library";
         }
         else {
              super.findLibrary(libName);
         }
   }
}

Then you arrange to load the implementation of your class using the relevant classloader...

public interface Implementation {

}

public class ImplementationLookerUpper {

    Classloader v1 = new MyClassloader(version1);
    Classloader v2 = new MyClassloader(version2);


    public Implementation implementationUsingVersion(Version someversion) {

             Classloader classloader = pickCorrectClassLoaderForVersion(someVersion);

            return (Implementation) classloader.loadClass(RealImplementation.class.getName()).newInstance();

    }
}

That kind of thing.....

time4tea