Wie bündelt man eine native Bibliothek und eine JNI-Bibliothek in einem JAR?

Lesezeit: 8 Minuten

Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
Alex B

Die betreffende Bibliothek ist Tokioter Kabinett.

Ich möchte die native Bibliothek, die JNI-Bibliothek und alle Java-API-Klassen in einer JAR-Datei haben, um Umverteilungsprobleme zu vermeiden.

Es scheint zu geben ein Versuch dazu bei GitHubaber

  1. Es enthält nicht die eigentliche native Bibliothek, sondern nur die JNI-Bibliothek.
  2. Es scheint spezifisch zu sein Leiningen‘s natives Abhängigkeits-Plugin (es funktioniert nicht als Redistributable).

Die Frage ist, kann ich alles in einem JAR bündeln und weiterverteilen? Wenn ja, wie?

PS: Ja, mir ist klar, dass dies Auswirkungen auf die Portabilität haben kann.

Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
kwo

Es ist möglich, eine einzelne JAR-Datei mit allen Abhängigkeiten einschließlich der nativen JNI-Bibliotheken für eine oder mehrere Plattformen zu erstellen. Der grundlegende Mechanismus besteht darin, System.load(File) zum Laden der Bibliothek anstelle des typischen System.loadLibrary(String) zu verwenden, das die Systemeigenschaft java.library.path durchsucht. Diese Methode macht die Installation viel einfacher, da der Benutzer die JNI-Bibliothek nicht auf seinem System installieren muss, jedoch auf Kosten dessen, dass möglicherweise nicht alle Plattformen unterstützt werden, da die spezifische Bibliothek für eine Plattform möglicherweise nicht in der einzelnen JAR-Datei enthalten ist .

Der Ablauf ist wie folgt:

  • Fügen Sie die nativen JNI-Bibliotheken in die JAR-Datei an einem plattformspezifischen Speicherort ein, beispielsweise unter NATIVE/${os.arch}/${os.name}/libname.lib
  • Code in einem statischen Initialisierer der Hauptklasse zu erstellen
    • berechnet die aktuelle os.arch und os.name
    • Suchen Sie die Bibliothek in der JAR-Datei am vordefinierten Speicherort mit Class.getResource(String)
    • Wenn es existiert, extrahieren Sie es in eine temporäre Datei und laden Sie es mit System.load(File).

Ich habe dazu Funktionen für jzmq hinzugefügt, die Java-Bindungen von ZeroMQ (schamloses Plug-in). Der Code kann gefunden werden Hier. Der jzmq-Code verwendet eine Hybridlösung, sodass der Code, wenn eine eingebettete Bibliothek nicht geladen werden kann, auf die Suche nach der JNI-Bibliothek entlang java.library.path zurückgreift.

  • Ich liebe es! Es erspart viel Mühe bei der Integration, und Sie können jederzeit mit System.loadLibrary() zum “alten” Weg zurückkehren, falls es fehlschlägt. Ich denke, ich werde damit anfangen. Danke! 🙂

    – Matthias

    8. Juni 13 um 19:55 Uhr


  • Was mache ich, wenn meine Bibliotheks-DLL eine Abhängigkeit zu einer anderen DLL hat? Ich bekomme immer eine UnsatisfiedLinkError da das Laden der ersteren DLL implizit die letztere laden möchte, sie aber nicht finden kann, da sie im JAR versteckt ist. Auch das vorherige Laden der letzteren DLL hilft nicht.

    – fabelhaft

    30. Juni 13 um 9:29 Uhr

  • Die anderen Angehörigen DLL müssen im Voraus bekannt sein und ihre Standorte sollten im hinzugefügt werden PATH Umgebungsvariable.

    – Eigenfeld

    29. Juni 2020 um 10:16 Uhr

  • Funktioniert super! Wenn es jemandem nicht klar ist, der darauf stößt, ziehen Sie die EmbeddedLibraryTools-Klasse in Ihr Projekt und ändern Sie sie entsprechend.

    – Jon LaMarr

    1. August 2020 um 22:41 Uhr

  • @JonLaMarr Gehen Sie diesen Weg nur, wenn Ihr Code GPL-lizenziert ist, wie es jzmq ist.

    – Kobbeln

    8. September 21 um 14:10 Uhr

1644021427 324 Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
davs

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

ist ein großartiger Artikel, der mein Problem löst.

In meinem Fall habe ich den folgenden Code zum Initialisieren der Bibliothek:

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

  • Verwenden Sie NativeUtils.loadLibraryFromJar(“/natives/”+System.mapLibraryName(“crypt”)); könnte besser sein

    – BlackJoker

    19. November 13 um 7:53 Uhr

  • Hallo, wenn ich versuche, die NativeUtils-Klasse zu verwenden und versuche, die .so-Datei in libs/armeabi/libmyname.so zu laden, erhalte ich die Ausnahme wie java.lang.ExceptionInInitializerError, verursacht durch: java.io.FileNotFoundException: Datei /native/libhellojni.so wurde nicht in JAR gefunden. Bitte lassen Sie mich wissen, warum ich die Ausnahme erhalte. Danke schön

    – Ganesh

    24. März 15 um 6:24 Uhr


Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
Evan

Schauen Sie sich an Ein-JAR. Es wird Ihre Anwendung in einer einzigen JAR-Datei mit einem spezialisierten Klassenladeprogramm verpacken, das unter anderem „Jars in Jars“ behandelt.

Es handhabt native (JNI) Bibliotheken indem Sie sie nach Bedarf in einen temporären Arbeitsordner entpacken.

(Haftungsausschluss: Ich habe One-JAR noch nie verwendet, musste es noch nicht, hatte es nur für einen regnerischen Tag mit einem Lesezeichen versehen.)

  • Ich bin kein Java-Programmierer, keine Ahnung, ob ich verfügen über um die Anwendung selbst zu verpacken? Wie würde dies in Kombination mit einem vorhandenen Classloader funktionieren? Zur Verdeutlichung, ich werde es von Clojure verwenden und möchte in der Lage sein, ein JAR als Bibliothek und nicht als Anwendung zu laden.

    – Alex B

    31. Mai ’10 um 1:44 Uhr


  • Ahhh, es wäre wahrscheinlich nicht geeignet als. Denken Sie, dass Sie Ihre native(n) Bibliothek(en) außerhalb der JAR-Datei verteilen müssen, mit Anweisungen, um sie in den Bibliothekspfad der Anwendung zu stellen.

    – Evan

    2. Juni 10 um 15:26 Uhr

1) Fügen Sie die native Bibliothek als Ressource in Ihr JAR ein. Z.B. mit Maven oder Gradle und dem Standardprojektlayout die native Bibliothek in main/resources Verzeichnis.

2) Irgendwo in den statischen Initialisierern von Java-Klassen, die sich auf diese Bibliothek beziehen, fügen Sie den Code wie folgt ein:

String libName = "myNativeLib.so"; // The name of the file in resources/ dir
URL url = MyClass.class.getResource("https://stackoverflow.com/" + 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());

JarClassLoader ist ein Klassenlader zum Laden von Klassen, nativen Bibliotheken und Ressourcen aus einer einzelnen Monster-JAR und aus JARs innerhalb der Monster-JAR.

1644021427 336 Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
TofuBier

Wahrscheinlich müssen Sie die native Bibliothek für das lokale Dateisystem entzerren. Soweit ich weiß, betrachtet der Code, der das native Laden durchführt, das Dateisystem.

Dieser Code sollte Ihnen beim Einstieg helfen (ich habe ihn mir eine Weile nicht angesehen und er dient einem anderen Zweck, sollte aber den Zweck erfüllen, und ich bin im Moment ziemlich beschäftigt, aber wenn Sie Fragen haben, hinterlassen Sie einfach einen Kommentar und ich werde so schnell wie möglich antworten).

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("\.", "https://stackoverflow.com/") + "https://stackoverflow.com/" + 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("https://stackoverflow.com/", ""), 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();
            }
        }
    }
}

1644021428 174 Wie bundelt man eine native Bibliothek und eine JNI Bibliothek in
Max Farsikow

Lösung für Kotlin:

  • build.gradle.dsl: Kotlin-Laufzeit (kotlin-stdlib-1.4.0.jar) und native Bibliothek (librust_kotlin.dylib) in JAR kopieren

    tasks.withType<Jar> {
        manifest {
            attributes("Main-Class" to "MainKt")
        }
    
        val libs = setOf("kotlin-stdlib-1.4.0.jar")
    
        from(configurations.runtimeClasspath.get()
            .filter { it.name in libs }
            .map { zipTree(it) })
    
        from("librust_kotlin.dylib")
    }
    
  • main Methode: Bibliothek in eine temporäre Datei kopieren, um sie mit absolutem Pfad zu laden

     with(createTempFile()) {
         deleteOnExit()
         val bytes = My::class.java.getResource("librust_kotlin.dylib")
             .readBytes()
    
         outputStream().write(bytes)
         System.load(path)
     }
    

.

776180cookie-checkWie bündelt man eine native Bibliothek und eine JNI-Bibliothek in einem JAR?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy