2013-07-21 19 views
7

Sto utilizzando Java Web Start per avviare un'applicazione Java che dipende da alcune librerie native di terze parti. Queste librerie native caricano successivamente un'altra libreria nativa (commonLib) come dipendenza usando LoadLibrary/dlopen.Java Web Start - carica dipendenza nativa con un'altra dipendenza nativa

Quando non si utilizza Web Start, tutto funziona come previsto quando le librerie native si trovano nella stessa directory.

Web Start, tuttavia, richiede le librerie native per essere confezionati in un file jar e fa riferimento nel file JNLP, che ho fatto:

<!-- Windows OS --> 
    <resources os="Windows"> 
     <nativelib href="native/native-windows.jar" /> 
    </resource> 

    <!-- Linux OS --> 
    <resources os="Linux"> 
     <nativelib href="native/native-linux.jar" /> 
    </resources> 

    <!-- Mac OSX --> 
    <resources os="Mac OS X"> 
     <nativelib href="native/native-osx.jar"/> 
    </resources> 

Le librerie native caricano bene, ma non riescono a caricare la loro dipendenza commonLib - la chiamata C++ LoadLibrary/dlopen non riesce perché il file è presente in qualche cartella jar/cache non nel percorso di ricerca della libreria corrente.

Su Windows, sono stato in grado di risolvere questo problema pre-caricamento commonLib in Java prima di provare a caricare la libreria JNI, in questo modo:

System.loadLibrary("commonLib"); 
System.loadLibrary("myNativeLib"); 

Tuttavia, questo approccio non funziona su OS X - dlopen nel codice nativo fallisce. dlopen apparentemente non è abbastanza intelligente da non provare a caricare di nuovo la libreria se è già stata caricata.

Esiste un modo multipiattaforma per imballare e caricare librerie native che dipendono da altre librerie native in Java Web Start?

+0

I barattoli contengono tutti i nativi coinvolti? –

+0

Sì, ricontrollato. –

risposta

1

Sono stato in grado di trovare una soluzione (brutta). Il trucco è quello di imballare le librerie dipendenti (commonLib) per un semplice barattolo di risorse e aggiungerlo al file JNLP:

... 
<resources os="Windows"> 
    <jar href="native/deps-windows.jar" /> 
</resources> 
<resources os="Linux"> 
    <jar href="native/deps-linux.jar" /> 
</resources> 
<resources os="Mac OS X"> 
    <jar href="native/deps-osx.jar" /> 
</resources> 
... 

Fase due è quello di estrarre queste risorse utilizzando Java in una directory temporanea:

String tmpDir = System.getProperty("java.io.tmpdir"); 
if (!tmpDir.endsWith("/") && !tmpDir.endsWith("\\")) 
    tmpDir += "/"; 
String resource = "commonDir.dll"; // Adjust accordingly to current OS, omitted for brevity 
InputStream is = loader.getResourceAsStream(resource); 
if (is == null) { 
    // Handle error - resource not found 
    return; 
} 
try { 
    FileOutputStream os = new FileOutputStream(tmpDir + resource); 
    byte[] buffer = new byte[1024*1024]; 
    int len = is.read(buffer); 
    while (len != -1) { 
     os.write(buffer, 0, len); 
     len = is.read(buffer); 
    } 
    os.close(); 
    is.close(); 
    System.out.println("Extracted " + resource + " to " + tmpDir); 
} catch(IOException ex) { 
    // Handle the exception - cannot write to temp directory 
    System.out.println("Could not extract resource " + resource + " to " + tmpDir + ": " + ex.getMessage()); 
} 

Il passaggio 3 consiste nell'informare la libreria JNI nativa sul percorso completo della dipendenza estratta o per impostare temporaneamente la directory corrente nella directory temporanea tmpDir, caricare la libreria JNI e reimpostarla. Questo è un problema da solo - in Java è difficile cambiare la directory di lavoro corrente. È possibile risolverlo creando un'altra libreria JNI di utilità piccola che esegue tale operazione da C sebbene [1].