Come raggruppare una libreria nativa e una libreria JNI all’interno di un JAR?

La biblioteca in questione è Tokyo Cabinet .

Voglio avere 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 un tentativo a questo a GitHub , ma

  1. Non include l’effettiva libreria nativa, solo la libreria JNI.
  2. Sembra essere specifico del plugin di dipendenze native di Leiningen (non funzionerà come ridistribuibile).

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

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

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

    è un 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); } } } 

    Dai uno sguardo a One-JAR . Comprenderà l’applicazione in un unico file jar con un caricatore di class specializzato che gestisce tra l’altro “barattoli all’interno di barattoli”.

    Gestisce le librerie native (JNI) scompattandole in una cartella di lavoro temporanea come richiesto.

    (Disclaimer: non ho mai usato One-JAR, non ho ancora avuto bisogno di farlo, l’ho appena messo tra i segnalibri per un giorno di pioggia.)

    JarClassLoader è un programma di caricamento classi per caricare classi, librerie native e risorse da un singolo JAR di mostri e da JAR all’interno del mostro JAR.

    Probabilmente dovrai aprire la libreria nativa al 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 piuttosto impegnato, ma se hai domande lascia un commento e ti risponderò al più presto ansible).

     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) Include la libreria nativa nel tuo JAR come risorsa. Per esempio. con Maven o Gradle e il layout di progetto standard, inserisci la libreria nativa nella directory main/resources .

    2) Da qualche parte negli inizializzatori statici delle classi Java, relativi a questa libreria, inserire il codice come segue:

     String libName = "myNativeLib.so"; // The name of the file in resources/ dir URL url = MyClass.class.getResource("/" + libName); File tmpDir = Files.createTempDirectory("my-native-lib").toFile(); tmpDir.deleteOnExit(); File nativeLibTmpFile = new File(tmpDir, libName); nativeLibTmpFile.deleteOnExit(); try (InputStream in = url.openStream()) { Files.copy(in, nativeLibTmpFile.toPath()); } System.load(nativeLibTmpFile.getAbsolutePath());