2011-01-14 9 views
16

PARTE 1

Estoy desarrollando una aplicación Java que se debe lanzar como un contenedor. Este programa depende de librerías externas de C++ llamadas por JNI. Para cargarlos, utilizo el método System.load con una ruta absoluta y esto funciona bien.Java - Cargando dlls por una ruta relativa y ocultarlos dentro de un contenedor

Sin embargo, realmente quiero "esconderlos" dentro del JAR, así que he creado un paquete para recogerlos. Esto me obliga a cargar una ruta relativa: la ruta del paquete. Con este enfoque, dejo que el usuario ejecute el JAR en cualquier directorio, sin preocuparse por vincular los archivos DLL o aburrido con un proceso de instalación anterior.

Esto produce la excepción prevista:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library

¿Cómo puedo conseguir este trabajo?

PARTE 2

El enfoque de copiar los archivos DLL en una carpeta (explicado más adelante) sólo funciona cuando lo ejecuto en el entorno Eclipse. Ejecución de un JAR exportados, los binarios DLL están bien creados, pero la carga de la JNI lanza la siguiente excepción:

Exception in thread "main" java.lang.reflect.InvocationTargetException

at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56) 
Caused by: java.lang.UnsatisfiedLinkError: C:\Users\Supertreta\Desktop\nm files\temp\jniBin.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) 

corro este método de carga:

public static void loadBinaries(){ 
     String os = System.getProperty("os.name").toLowerCase(); 

     if(os.indexOf("win") >= 0){ 
      ArrayList<String> bins = new ArrayList<String>(){{ 
       add("/nm/metadata/bin/dependence1.dll"); 
       add("/nm/metadata/bin/dependence2.dll"); 
       add("/nm/metadata/bin/dependence3.dll"); 
       add("/nm/metadata/bin/dependence4.dll"); 
       add("/nm/metadata/bin/jniBin.dll"); 
      }}; 

      File f = null; 
      for(String bin : bins){ 
       InputStream in = FileManager.class.getResourceAsStream(bin); 
       byte[] buffer = new byte[1024]; 
       int read = -1; 
       try { 
        String[] temp = bin.split("/"); 
        f = new File(TEMP_FOLDER, temp[temp.length-1]);  
        FileOutputStream fos = new FileOutputStream(f); 

        while((read = in.read(buffer)) != -1) { 
         fos.write(buffer, 0, read); 
        } 
        fos.close(); 
        in.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 

      System.load(f.getAbsolutePath()); 
     } 
    } 

Creo que esto podría ser un problema privilegios de acceso , pero no sé cómo resolverlo ¿Qué piensas?

+0

¿Por qué quieres para ocultarlos? –

+0

Para proporcionar un recipiente limpio – supertreta

Respuesta

21

No creo que pueda cargar la DLL directamente desde el JAR. Debe tomar el paso intermedio de copiar el archivo DLL del archivo JAR. El siguiente código debe hacerlo:

public static void loadJarDll(String name) throws IOException { 
    InputStream in = MyClass.class.getResourceAsStream(name); 
    byte[] buffer = new byte[1024]; 
    int read = -1; 
    File temp = File.createTempFile(name, ""); 
    FileOutputStream fos = new FileOutputStream(temp); 

    while((read = in.read(buffer)) != -1) { 
     fos.write(buffer, 0, read); 
    } 
    fos.close(); 
    in.close(); 

    System.load(temp.getAbsolutePath()); 
} 
+0

Esto me parece una manera rápida y fácil de resolver este problema. Lo probé pero tengo un problema: el método createTempFile agrega un número al nombre del archivo, es decir, la lib "hello.dll" pasa a ser "hello.dll4975093656535427331", incluso con estos parámetros (nombre, "").Mi dll JNI principal también depende de otros dlls, así que debo estar al tanto de los nombres. ¿Sabes cómo puedo lidiar con esto? – supertreta

+0

Sí, solo modifique la forma en que se inicializa 'File temp':' File temp = new File (new File (System.getProperty ("java.io.tmpdir")), name) '; –

+0

Ahora copio todos los archivos dll pero cuando cargo el JNI obtengo la siguiente excepción: Excepción en el hilo "principal" java.lang.UnsatisfiedLinkError: C: \ Documents and Settings \ Administrator \ Configuración local \ Temp \ hello.dll : Esta aplicación no se pudo iniciar porque la configuración de la aplicación es incorrecta. Reinstalar la aplicación podría resolver el problema. Confirmé que todas las bibliotecas están en la carpeta temporal ... – supertreta

2

Básicamente esto debería funcionar. Como esta es la forma en que JNA lo hace, simplemente descárguelo y estudie el código. Incluso tiene algunos consejos para hacer que esta plataforma independiente ...

EDITAR

JNA trae su código nativo a lo largo de la jarra, descomprime el archivo binario correcto en tiempo de ejecución und lo carga. Este puede ser un buen patrón a seguir (si tu pregunta es correcta).

+3

Disculpe, ¿omitió algo en su respuesta? Con "esto" te refieres a qué? gracias – supertreta

-1

Usted necesita para cebar el cargador de clases con la ubicación de la DLL - pero se puede cargar sin extraerlo de la jarra. Algo simple antes de que se ejecute la llamada de carga es suficiente. En su principal complemento de clase:

static { 
    System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension! 
} 

En particular, he experimentado básicamente el mismo issue with JNA and OSGi's handling of how the DLLs are loaded.

+0

Creo que lo ejecuto correctamente. Y esto funciona bien cuando lo estoy depurando en eclipse. El problema aparece cuando exporto un jar y lo ejecuto. Actualicé la respuesta con mi método de carga. ¡Gracias! – supertreta

+0

@supertreta: apuesto a que no estás empacando la DLL en tu JAR. Intente cambiar el nombre de su JAR a un archivo zip y explorarlo. –

+0

Veo que el jar crea los dlls en la carpeta. Confirmé que los quitaba antes de ejecutarlo. Lo revisé ahora convirtiéndolo en un zip, y están ahí. – supertreta