java jar clojure jni tokyo-cabinet

java - ¿Cómo agrupar una biblioteca nativa y una biblioteca JNI dentro de un JAR?



clojure tokyo-cabinet (5)

1) Incluye la biblioteca nativa en tu JAR como recurso. P.ej. con Maven o Gradle, y el diseño del proyecto estándar, coloque la biblioteca nativa en el directorio main/resources .

2) En algún lugar de los inicializadores estáticos de las clases de Java, relacionadas con esta biblioteca, coloque el código como el siguiente:

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

La biblioteca en cuestión es el Gabinete de Tokio .

Lo que quiero es tener la biblioteca nativa, la biblioteca JNI y todas las clases API de Java en un archivo JAR para evitar dolores de cabeza de redistribución.

Parece que hay un intento de esto en GitHub , pero

  1. No incluye la biblioteca nativa real, solo la biblioteca JNI.
  2. Parece ser específico del complemento de dependencias nativas de Leiningen (no funcionará como redistribuible).

La pregunta es, ¿puedo agrupar todo en un JAR y redistribuirlo? Si es así, ¿cómo?

PD: Sí, me doy cuenta de que puede tener implicaciones de portabilidad.


Eche un vistazo a One-JAR . Envolverá su aplicación en un único archivo jar con un cargador de clases especializado que maneja "jarras dentro de jarras", entre otras cosas.

Maneja bibliotecas nativas (JNI) desempaquetandolas en una carpeta de trabajo temporal según sea necesario.

(Descargo de responsabilidad: nunca he usado One-JAR, aún no lo he necesitado, solo lo he marcado como favorito para un día lluvioso).


Es probable que deba destrabar la biblioteca nativa en el sistema de archivos local. Por lo que sé, el bit de código que hace la carga nativa se ve en el sistema de archivos.

Este código debería ayudarlo a comenzar (no lo he visto desde hace tiempo, y es para un propósito diferente, pero debería funcionar, y estoy bastante ocupado en este momento, pero si tiene alguna pregunta, simplemente deje un comentario y responderé tan pronto como pueda).

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


JarClassLoader es un cargador de clases para cargar clases, bibliotecas nativas y recursos de un solo monstruo JAR y de JAR dentro del monstruo JAR.


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

es un gran artículo, que resuelve mi problema ...

En mi caso, tengo el siguiente código para inicializar la biblioteca:

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