2011-01-14 8 views
16

PARTE 1

Sto sviluppando un'applicazione Java che dovrebbe essere rilasciata come un jar. Questo programma dipende dalle librerie esterne C++ chiamate da JNI. Per caricare loro, io uso il metodo System.load con un percorso assoluto e questo funziona benissimo.Java: caricamento delle DLL da un percorso relativo e nascosti all'interno di un jar

Tuttavia, ci tengo a "nascondere" al loro interno il JAR, così ho creato un pacchetto per raccoglierli. Questo mi obbliga a caricare un percorso relativo - il percorso del pacchetto. Con questo approccio, ho lasciato l'utente esegue il vaso in una qualsiasi directory, senza essere preoccupati per collegare le DLL o annoiato con un processo di installazione precedente.

Questo genera l'eccezione prevista:

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

Come posso ottenere questo lavoro?

PARTE 2

L'approccio di copiare le DLL in una cartella (spiegato di seguito) funziona solo quando l'eseguo in ambiente Eclipse. L'esecuzione di un JAR esportato, i binari DLL sono ben realizzati, ma il caricamento di quello JNI lancia l'eccezione successiva:

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) 

faccio funzionare questo metodo di caricamento:

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

credo che questo potrebbe essere un problema privilegi di accesso , ma non so come risolverlo. Cosa ne pensi?

+0

Perché vuoi nasconderli? –

+0

Per fornire un barattolo pulito – supertreta

risposta

21

Io non credo che si possa caricare la DLL direttamente dal barattolo. Devi fare il passo intermedio di copiare la DLL dal JAR. Il seguente codice dovrebbe farlo:

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

Questo mi sembra un modo facile e veloce per risolvere questo problema. L'ho provato, ma ho un problema: il metodo createTempFile aggiunge un numero al nome del file, cioè la lib "hello.dll" diventa "hello.dll4975093656535427331", anche con questi parametri (nome, "").La mia DLL principale JNI dipende anche da altre DLL, quindi devo essere a conoscenza dei nomi. Sai come posso affrontare questo? – supertreta

+0

Sì, basta modificare il modo in cui 'File temp' è inizializzato:' File temp = new File (nuovo File (System.getProperty ("java.io.tmpdir")), name) '; –

+0

Ora copio tutti i file dll ma quando carico JNI, ottengo la prossima eccezione: Eccezione nel thread "main" java.lang.UnsatisfiedLinkError: C: \ Documents and Settings \ Administrator \ Impostazioni locali \ Temp \ hello.dll : Questa applicazione non è stata avviata poiché la configurazione dell'applicazione non è corretta. Reinstallare l'applicazone potrebbe risolvere questo problema. Ho confermato che tutte le librerie sono nella cartella temporanea ... – supertreta

2

Fondamentalmente questo dovrebbe funzionare. Poiché questo è il modo in cui JNA lo fa, è sufficiente scaricarlo e studiarlo. Ancora di avere qualche suggerimento per rendere questa piattaforma indipendente ...

EDIT

JNA porta il suo codice nativo lungo nel vaso, scompatta il file binario corretto in fase di esecuzione und lo carica. Questo può essere un buon modello da seguire (se ho corretto la tua domanda).

+3

Siamo spiacenti, ti è mancato qualcosa sulla vostra risposta? Con "questo" fai riferimento a cosa? grazie – supertreta

-1

È necessario adescare il programma di caricamento classe con la posizione della DLL - ma possono essere caricati senza estrarre dal barattolo. È sufficiente qualcosa di semplice prima che venga eseguita la chiamata di caricamento. Nella tua classe principale aggiuntivo:

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

In particolare, ho sperimentato fondamentalmente la stessa issue with JNA and OSGi's handling of how the DLLs are loaded.

+0

Penso di averlo eseguito correttamente. E questo funziona bene quando sto eseguendo il debug su Eclipse. Il problema appare quando esporto un barattolo e lo avvio. Ho aggiornato la risposta con il mio metodo di caricamento. Grazie! – supertreta

+0

@supertreta: Scommetto che non stai impacchettando la DLL nel tuo JAR. Prova a rinominare il tuo JAR in un file zip ed esplorarlo. –

+0

Posso vedere il jar che crea le DLL sulla cartella. Ho confermato che li rimuoveva prima di eseguirlo. Ho controllato ora convertendolo in una zip, e loro sono lì. – supertreta