2010-09-09 16 views
6

Tengo una biblioteca JNI que interactúa con un conjunto de bibliotecas de terceros, y puede haber más de una versión de la biblioteca de terceros en el sistema. Para cada versión de la biblioteca de terceros, tengo que volver a compilar el código JNI por razones de comparabilidad. En este momento me ocupo de esto al cargar un archivo DLL con un nombre específico, y si la versión cambia, cambio los nombres de las DLL de la interfaz JNI para que el correcto para la versión tenga el nombre correcto para cargarlo.¿Es posible cargar diferentes versiones de la misma DLL en Java?

Me gustaría poder cargar dinámicamente el archivo dll, según la versión que el usuario quiera usar. ¿Qué sucede si llamo a System.loadLibrary dos veces en DLL con diferentes nombres pero con las mismas firmas de método?

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

que sólo tendrá que utilizar una de las versiones a la vez, por lo que está bien si la versión antigua ya no es accesible.

¿Es posible cargar dos versiones diferentes de una DLL con las mismas firmas de método sin reiniciar el programa?

Respuesta

3

Es posible y, de hecho, es totalmente compatible y funciona de manera brillante.

He tenido que hacer esto en un entorno de producción y en una JVM solar es sólida como una roca.

Básicamente, si carga la biblioteca de un cargador de clases diferente, cargará una copia diferente de la biblioteca. Es tan simple como eso.

No recomendaría hacer esto a menos que realmente tenga que hacerlo ... pero funciona.

Como alternativa, dependiendo de sus requisitos específicos, podría simplemente dejarlo fuera de proceso, y tener un protocolo simple (usando say embarcadero/xstream/httpclient, o netty) entre el cliente y los diferentes servidores, cada uno de que tiene una versión dll diferente cargada.

Esencialmente, esto implica que escribir un cargador de clases

public class MyClassLoader extends URLClassLoader { 

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

Entonces usted arregla para cargar la implementación de su clase utilizando el cargador de clases relevante ...

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(); 

    } 
} 

Ese tipo de cosas ....

0

No sé los detalles de cómo funciona la carga DLL en Windows, pero mi instinto me dice que definitivamente no sería seguro tener bibliotecas superpuestas cargadas al mismo tiempo. Pero tal vez si primero descargó el primero, podría ser.

Por lo que veo, no hay manera de explotar explícitamente descargar una biblioteca. Si miras java.lang.Classloader.NativeLibrary verás que una biblioteca se descarga cuando el cargador de clases que causó su carga obtiene basura recolectada (en su método finalize()). Por lo tanto, si lo carga en un cargador de clases separado y luego espera que sea basura (con algunas llamadas maliciosas a System.gc() para que esto suceda más rápido) antes de cargar el nuevo, eso podría ayudar.

1

Esto no es seguro y es bastante complicado de implementar. Es posible tener diferentes clasificadores para diferentes versiones del dll. Tendrás que asegurarte de que el cargador de clases sea basura recolectada para asegurarte de que el dll ha sido descargado. (Ver Java JNI - DLL Unloading)

En el pasado, hemos resuelto este problema escribiendo un proceso de mediador java liviano que maneja la solicitud de un usuario y crea un nuevo proceso java hijo con la versión de la DLL solicitada. Cuando un usuario solicita una versión diferente, el mediador cierra el elemento secundario existente y genera uno nuevo con una ruta de biblioteca diferente. Este enfoque funciona bien. El único inconveniente es el tiempo necesario para iniciar nuevas JVM, pero esto solo se notará si tiene muchas solicitudes de cambios de versión con mucha frecuencia.

Cuestiones relacionadas