2010-05-30 10 views
84

La libreria in questione è Tokyo Cabinet.Come raggruppare una libreria nativa e una libreria JNI all'interno di un JAR?

Voglio che sia la libreria nativa, la libreria JNI e tutte le classi dell'API Java in un file JAR per evitare mal di redistribuzione.

Sembra che ci sia an attempt at this at GitHub, ma

  1. non include la libreria nativa reale, unica biblioteca JNI.
  2. Sembra essere specifico del plug-in di origini native di Leiningen (non funzionerà come ridistribuibile).

La domanda è: posso raggruppare tutto in un JAR e ridistribuirlo? Se sì, come?

P.S .: Sì, mi rendo conto che potrebbe avere implicazioni sulla portabilità.

risposta

1

Probabilmente sarà necessario rimuovere la libreria nativa nel file system locale. Per quanto ne so il bit di codice che fa il caricamento nativo guarda al file system.

Questo codice dovrebbe aiutarti a iniziare (non l'ho guardato da un po ', ed è per uno scopo diverso ma dovrebbe fare il trucco, e al momento sono abbastanza impegnato, ma se hai domande basta lasciare un commento e ti risponderò al più presto possibile).

import java.io.Closeable; 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.UnsupportedEncodingException; 
import java.net.URI; 
import java.net.URISyntaxException; 
import java.net.URL; 
import java.net.URLDecoder; 
import java.security.CodeSource; 
import java.security.ProtectionDomain; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipException; 
import java.util.zip.ZipFile; 


public class FileUtils 
{ 
    public static String getFileName(final Class<?> owner, 
            final String name) 
     throws URISyntaxException, 
       ZipException, 
       IOException 
    { 
     String fileName; 
     final URI uri; 

     try 
     { 
      final String external; 
      final String decoded; 
      final int pos; 

      uri  = getResourceAsURI(owner.getPackage().getName().replaceAll("\\.", "/") + "/" + name, owner); 
      external = uri.toURL().toExternalForm(); 
      decoded = external; // URLDecoder.decode(external, "UTF-8"); 
      pos  = decoded.indexOf(":/"); 
      fileName = decoded.substring(pos + 1); 
     } 
     catch(final FileNotFoundException ex) 
     { 
      fileName = null; 
     } 

     if(fileName == null || !(new File(fileName).exists())) 
     { 
      fileName = getFileNameX(owner, name); 
     } 

     return (fileName); 
    } 

    private static String getFileNameX(final Class<?> clazz, final String name) 
     throws UnsupportedEncodingException 
    { 
     final URL url; 
     final String fileName; 

     url = clazz.getResource(name); 

     if(url == null) 
     { 
      fileName = name; 
     } 
     else 
     { 
      final String decoded; 
      final int pos; 

      decoded = URLDecoder.decode(url.toExternalForm(), "UTF-8"); 
      pos  = decoded.indexOf(":/"); 
      fileName = decoded.substring(pos + 1); 
     } 

     return (fileName); 
    } 

    private static URI getResourceAsURI(final String resourceName, 
             final Class<?> clazz) 
     throws URISyntaxException, 
       ZipException, 
       IOException 
    { 
     final URI uri; 
     final URI resourceURI; 

     uri   = getJarURI(clazz); 
     resourceURI = getFile(uri, resourceName); 

     return (resourceURI); 
    } 

    private static URI getJarURI(final Class<?> clazz) 
     throws URISyntaxException 
    { 
     final ProtectionDomain domain; 
     final CodeSource  source; 
     final URL    url; 
     final URI    uri; 

     domain = clazz.getProtectionDomain(); 
     source = domain.getCodeSource(); 
     url = source.getLocation(); 
     uri = url.toURI(); 

     return (uri); 
    } 

    private static URI getFile(final URI where, 
           final String fileName) 
     throws ZipException, 
       IOException 
    { 
     final File location; 
     final URI fileURI; 

     location = new File(where); 

     // not in a JAR, just return the path on disk 
     if(location.isDirectory()) 
     { 
      fileURI = URI.create(where.toString() + fileName); 
     } 
     else 
     { 
      final ZipFile zipFile; 

      zipFile = new ZipFile(location); 

      try 
      { 
       fileURI = extract(zipFile, fileName); 
      } 
      finally 
      { 
       zipFile.close(); 
      } 
     } 

     return (fileURI); 
    } 

    private static URI extract(final ZipFile zipFile, 
           final String fileName) 
     throws IOException 
    { 
     final File   tempFile; 
     final ZipEntry  entry; 
     final InputStream zipStream; 
     OutputStream  fileStream; 

     tempFile = File.createTempFile(fileName.replace("/", ""), Long.toString(System.currentTimeMillis())); 
     tempFile.deleteOnExit(); 
     entry = zipFile.getEntry(fileName); 

     if(entry == null) 
     { 
      throw new FileNotFoundException("cannot find file: " + fileName + " in archive: " + zipFile.getName()); 
     } 

     zipStream = zipFile.getInputStream(entry); 
     fileStream = null; 

     try 
     { 
      final byte[] buf; 
      int   i; 

      fileStream = new FileOutputStream(tempFile); 
      buf  = new byte[1024]; 
      i   = 0; 

      while((i = zipStream.read(buf)) != -1) 
      { 
       fileStream.write(buf, 0, i); 
      } 
     } 
     finally 
     { 
      close(zipStream); 
      close(fileStream); 
     } 

     return (tempFile.toURI()); 
    } 

    private static void close(final Closeable stream) 
    { 
     if(stream != null) 
     { 
      try 
      { 
       stream.close(); 
      } 
      catch(final IOException ex) 
      { 
       ex.printStackTrace(); 
      } 
     } 
    } 
} 
+1

Se avete bisogno di un po 'di unjar risorsa specifica, consiglio di dare un'occhiata al https://github.com/zeroturnaround/zt-zip project –

+0

zt-zip sembra un'API decente. – TofuBeer

17

Dai uno sguardo allo One-JAR. Comprenderà l'applicazione in un unico file jar con un caricatore di classe specializzato che gestisce tra l'altro "barattoli all'interno di barattoli".

It handles native (JNI) libraries decomprimendoli in una cartella di lavoro temporanea come richiesto.

(Disclaimer:. Non ho mai usato One-JAR, non hanno bisogno di ancora, appena aveva un segnalibro per un giorno di pioggia)

+0

Non sono un programmatore Java, non so se * devo * racchiudere l'applicazione stessa? Come funzionerebbe in combinazione con un classloader esistente? Per chiarire, lo userò da Clojure e voglio essere in grado di caricare un JAR come libreria, piuttosto che come un'applicazione. –

+0

Ahhh, probabilmente non sarebbe adatto a. Pensa che sarai bloccato a distribuire le tue librerie native al di fuori del file jar, con le istruzioni per inserirle nel percorso della libreria dell'applicazione. – Evan

5

JarClassLoader è un caricatore di classe per caricare le classi, le librerie native e risorse da un singolo mostro JAR e da JARs all'interno del mostro JAR.

29

https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/

è grande articolo, che risolve il mio problema ..

Nel mio caso ho il seguente codice per inizializzare la libreria:

static { 
    try { 
     System.loadLibrary("crypt"); // used for tests. This library in classpath only 
    } catch (UnsatisfiedLinkError e) { 
     try { 
      NativeUtils.loadLibraryFromJar("/natives/crypt.dll"); // during runtime. .DLL within .JAR 
     } catch (IOException e1) { 
      throw new RuntimeException(e1); 
     } 
    } 
} 
+17

usa NativeUtils.loadLibraryFromJar ("/ natives /" + System.mapLibraryName ("crypt")); potrebbe essere meglio – BlackJoker

+0

Buon punto!grazie – davs

+1

Salve quando sto cercando di usare la classe NativeUtils e sto provando ad inserire il file .so all'interno di libs/armeabi/libmyname.so, sto ricevendo l'eccezione come java.lang.ExceptionInInitializerError, causata da: java.io .FileNotFoundException: File /native/libhellojni.so non è stato trovato all'interno di JAR. Per favore fatemi sapere perché sto ottenendo l'eccezione. Grazie – Ganesh

Problemi correlati