java.lang.UnsatisfiedLinkError no *****.dll en java.library.path

Resuelto Ketan Khairnar asked hace 15 años • 14 respuestas

¿Cómo puedo cargar un archivo dll personalizado en mi aplicación web? Intenté lo siguiente:

  • Copié todos los archivos DLL necesarios en system32la carpeta e intenté cargar uno de ellos en Servletel constructor.System.loadLibrary
  • Copié los archivos DLL necesarios en tomcat_home/shared/libytomcat_home/common/lib

Todos estos archivos DLL están en WEB-INF/libla aplicación web.

Ketan Khairnar avatar Sep 10 '09 14:09 Ketan Khairnar
Aceptado

Para que System.loadLibrary()funcione, la biblioteca (en Windows, una DLL) debe estar en un directorio en algún lugar de su directorio PATH o en una ruta que figura en la java.library.pathpropiedad del sistema (para que pueda iniciar Java como java -Djava.library.path=/path/to/dir).

Además, para loadLibrary(), especifica el nombre base de la biblioteca, sin al .dllfinal. Entonces, para /path/to/something.dll, simplemente usarías System.loadLibrary("something").

También debe observar exactamente lo UnsatisfiedLinkErrorque está obteniendo. Si dice algo como:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path

entonces no puede encontrar la biblioteca fooPATH (foo.dll) en su archivo o java.library.path. Si dice algo como:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.example.program.ClassName.foo()V

entonces algo anda mal con la biblioteca misma en el sentido de que Java no puede asignar una función Java nativa en su aplicación a su contraparte nativa real.

Para empezar, pondría un poco de registro en torno a su System.loadLibrary()llamada para ver si se ejecuta correctamente. Si arroja una excepción o no está en una ruta de código que realmente se ejecuta, siempre obtendrá el último tipo UnsatisfiedLinkErrorexplicado anteriormente.

Como nota al margen, la mayoría de las personas colocan sus loadLibrary()llamadas en un bloque inicializador estático en la clase con los métodos nativos, para garantizar que siempre se ejecute exactamente una vez:

class Foo {

    static {
        System.loadLibrary('foo');
    }

    public Foo() {
    }

}
Adam Batkin avatar Sep 10 '2009 07:09 Adam Batkin

Cambiar la variable 'java.library.path' en tiempo de ejecución no es suficiente porque JVM solo la lee una vez. Tienes que restablecerlo como:

System.setProperty("java.library.path", path);
//set sys_paths to null
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);

Por favor, consulte: Cambio de la ruta de la biblioteca Java en tiempo de ejecución .

Alexandre avatar Feb 12 '2014 14:02 Alexandre

La respuesta original de Adam Batkin lo llevará a una solución, pero si vuelve a implementar su aplicación web (sin reiniciar su contenedor web), debería encontrarse con el siguiente error:

java.lang.UnsatisfiedLinkError: Native Library "foo" already loaded in another classloader
   at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1715)
   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1646)
   at java.lang.Runtime.load0(Runtime.java:787)
   at java.lang.System.load(System.java:1022)

Esto sucede porque el ClassLoader que cargó originalmente su DLL todavía hace referencia a esta DLL. Sin embargo, su aplicación web ahora se ejecuta con un nuevo ClassLoader y, debido a que se está ejecutando la misma JVM y una JVM no permite 2 referencias a la misma DLL, no puede volver a cargarla . Por lo tanto, su aplicación web no puede acceder a la DLL existente y no puede cargar una nueva. Entonces... estás estancado.

La documentación de ClassLoader de Tomcat describe por qué su aplicación web recargada se ejecuta en un nuevo ClassLoader aislado y cómo puede solucionar esta limitación (en un nivel muy alto).

La solución es ampliar un poco la solución de Adam Batkin:

   package awesome;

   public class Foo {

        static {
            System.loadLibrary('foo');
        }

        // required to work with JDK 6 and JDK 7
        public static void main(String[] args) {
        }

    }

Luego, coloque un frasco que contenga SÓLO esta clase compilada en la carpeta TOMCAT_HOME/lib.

Ahora, dentro de su aplicación web, sólo tiene que forzar a Tomcat a hacer referencia a esta clase, lo que se puede hacer de la siguiente manera:

  Class.forName("awesome.Foo");

Ahora su DLL debe cargarse en el cargador de clases común y se puede hacer referencia a ella desde su aplicación web incluso después de volver a implementarla.

¿Tener sentido?

Se puede encontrar una copia de referencia funcional en el código de Google, static-dll-bootstrapper .

Matt M avatar Nov 06 '2012 00:11 Matt M