java.lang.UnsatisfiedLinkError no *****. dll in java.library.path

92

Wie kann ich eine benutzerdefinierte DLL-Datei in meine Webanwendung laden? Ich habe folgendes versucht:

  • Kopierte alle erforderlichen DLLs in den system32Ordner und versuchte, eine davon in den ServletKonstruktor zu ladenSystem.loadLibrary
  • Erforderliche DLLs in tomcat_home/shared/libund kopierttomcat_home/common/lib

Alle diese DLLs befinden sich in WEB-INF/libder Webanwendung

Ketan Khairnar
quelle

Antworten:

154

Damit dies System.loadLibrary()funktioniert, muss sich die Bibliothek (unter Windows eine DLL) in einem Verzeichnis befinden, das sich irgendwo in Ihrem PATH oder in einem Pfad befindet, der in der java.library.pathSystemeigenschaft aufgeführt ist (damit Sie Java wie starten können java -Djava.library.path=/path/to/dir).

Außerdem loadLibrary()geben Sie für den Basisnamen der Bibliothek ohne den .dllam Ende an. Also, für /path/to/something.dll, würden Sie nur verwenden System.loadLibrary("something").

Sie müssen sich auch genau ansehen UnsatisfiedLinkError, was Sie bekommen. Wenn es so etwas sagt wie:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path

dann kann es die foo- Bibliothek (foo.dll) in Ihrem PATHoder nicht finden java.library.path. Wenn es so etwas sagt wie:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.example.program.ClassName.foo()V

Dann stimmt etwas mit der Bibliothek selbst nicht, da Java eine native Java-Funktion in Ihrer Anwendung nicht ihrem tatsächlichen nativen Gegenstück zuordnen kann.

Zunächst würde ich Ihren System.loadLibrary()Anruf protokollieren, um zu sehen, ob dies ordnungsgemäß ausgeführt wird. Wenn es eine Ausnahme auslöst oder sich nicht in einem Codepfad befindet, der tatsächlich ausgeführt wird, erhalten Sie immer den UnsatisfiedLinkErroroben erläuterten letzteren Typ .

Als Nebenbemerkung stellen die meisten Benutzer ihre loadLibrary()Aufrufe mit den nativen Methoden in einen statischen Initialisierungsblock in der Klasse, um sicherzustellen, dass er immer genau einmal ausgeführt wird:

class Foo {

    static {
        System.loadLibrary('foo');
    }

    public Foo() {
    }

}
Adam Batkin
quelle
1
Durch das Einfügen aller DLLs in System32 und die Verwendung von System.loadLibrary ("etwas") hat funktioniert. Ich habe früher System.loadLibrary ("Something.dll") gemacht. Warum kann es nicht alle DLLs von WEB-INF laden? Ich denke, es lädt standardmäßig alle Gläser. Was kann ich tun, um diese DLLs direkt von WEB-INF anstelle von System32 zu laden / in java.library.path anzugeben
Ketan Khairnar
2
+1 für 'benutze "bla" anstelle von "bla.dll" für loadLibrary()Bemerkung - sehr nützlich, wenn du keine Ahnung hast, was du falsch machst.
MarnixKlooster ReinstateMonica
20
Auf meinem System (Linux & Java7) benötige ich ein libPräfix. Also System.loadLibrary("foo")braucht libfoo.so.
Kristianlm
2
Vielen Dank. Es hat bei mir funktioniert, als ich "PATH" für Windows so aktualisiert habe, dass es einen Ordner mit der Datei * .so enthält.
user613114
15

Das Ändern der Variablen 'java.library.path' zur Laufzeit reicht nicht aus, da sie von JVM nur einmal gelesen wird. Sie müssen es wie folgt zurücksetzen:

System.setProperty("java.library.path", path);
//set sys_paths to null
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);

Bitte machen Sie eine Beute unter: Ändern des Java-Bibliothekspfads zur Laufzeit .

Alexandre
quelle
10

Die ursprüngliche Antwort von Adam Batkin führt Sie zu einer Lösung. Wenn Sie jedoch Ihre Webanwendung erneut bereitstellen (ohne Ihren Webcontainer neu zu starten), sollte der folgende Fehler auftreten:

java.lang.UnsatisfiedLinkError: Native Library "foo" already loaded in another classloader
   at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1715)
   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1646)
   at java.lang.Runtime.load0(Runtime.java:787)
   at java.lang.System.load(System.java:1022)

Dies liegt daran, dass der ClassLoader, der Ihre DLL ursprünglich geladen hat, weiterhin auf diese DLL verweist. Ihre Webanwendung wird jetzt jedoch mit einem neuen ClassLoader ausgeführt. Da dieselbe JVM ausgeführt wird und eine JVM nicht zwei Verweise auf dieselbe DLL zulässt, können Sie sie nicht neu laden . Daher kann Ihre Webanwendung nicht auf die vorhandene DLL zugreifen und keine neue laden. Also ... du steckst fest.

In der ClassLoader-Dokumentation von Tomcat wird erläutert, warum Ihre neu geladene Webanwendung in einem neuen isolierten ClassLoader ausgeführt wird und wie Sie diese Einschränkung umgehen können (auf sehr hohem Niveau).

Die Lösung besteht darin, die Lösung von Adam Batkin ein wenig zu erweitern:

   package awesome;

   public class Foo {

        static {
            System.loadLibrary('foo');
        }

        // required to work with JDK 6 and JDK 7
        public static void main(String[] args) {
        }

    }

Platzieren Sie dann ein Glas mit NUR dieser kompilierten Klasse im Ordner TOMCAT_HOME / lib.

Jetzt müssen Sie in Ihrer Webanwendung nur noch Tomcat zwingen, auf diese Klasse zu verweisen. Dies ist so einfach:

  Class.forName("awesome.Foo");

Jetzt sollte Ihre DLL im allgemeinen Klassenladeprogramm geladen sein und kann auch nach der erneuten Bereitstellung von Ihrer Webanwendung aus referenziert werden.

Sinn ergeben?

Eine funktionierende Referenzkopie finden Sie im Google-Code static-dll-bootstrapper .

Matt M.
quelle
1
Diese Antwort eignet sich hervorragend für Anwendungsserver und sollte eine eigene Frage haben, da sie für diese Frage verschwendet wird.
JoshDM
8

Sie können verwenden System.load() einen absoluten Pfad angeben, der Ihren Wünschen entspricht, und nicht eine Datei im Standardbibliotheksordner für das jeweilige Betriebssystem.

Wenn Sie native Anwendungen möchten, die bereits vorhanden sind, verwenden Sie System.loadLibrary(String filename). Wenn Sie Ihre eigenen bereitstellen möchten, sind Sie wahrscheinlich besser mit load ().

Sie sollten auch in der Lage sein, loadLibrarymit dem java.library.pathSet richtig zu arbeiten. Siehe ClassLoader.javaImplementierungsquelle, in der beide Pfade überprüft werden (OpenJDK)

Philip Whitehouse
quelle
Vielen Dank. Die Verwendung von Load ist meiner Meinung nach viel einfacher und intuitiver. LoadLibrary konnte nicht zum Laufen gebracht werden, egal was ich getan habe ...
Plankalkül
2
Diese Lösung funktioniert nicht für Bibliotheken, die ihre eigenen nativen Bibliotheken laden.
Justin Skiles
7

In dem Fall, in dem das Problem darin besteht, dass System.loadLibrary die betreffende DLL nicht finden kann, besteht ein häufiges Missverständnis (verstärkt durch Javas Fehlermeldung) darin, dass die Systemeigenschaft java.library.path die Antwort ist. Wenn Sie die Systemeigenschaft java.library.path auf das Verzeichnis setzen, in dem sich Ihre DLL befindet, findet System.loadLibrary tatsächlich Ihre DLL. Wenn Ihre DLL wiederum von anderen DLLs abhängt, wie dies häufig der Fall ist, kann java.library.path nicht helfen, da das Laden der abhängigen DLLs vollständig vom Betriebssystem verwaltet wird, das nichts von java.library weiß. Pfad. Daher ist es fast immer besser, java.library.path zu umgehen und das Verzeichnis Ihrer DLL vor dem Starten der JVM einfach zu LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS) oder Path (Windows) hinzuzufügen.

(Hinweis: Ich verwende den Begriff "DLL" im allgemeinen Sinne von DLL oder Shared Library.)

Ian Emmons
quelle
5

Wenn Sie eine Datei laden müssen, die sich auf ein Verzeichnis bezieht, in dem Sie sich bereits befinden (wie im aktuellen Verzeichnis), finden Sie hier eine einfache Lösung:

File f;

if (System.getProperty("sun.arch.data.model").equals("32")) {
    // 32-bit JVM
    f = new File("mylibfile32.so");
} else {
    // 64-bit JVM
    f = new File("mylibfile64.so");
}
System.load(f.getAbsolutePath());
Rick C. Hodgin
quelle
4

Für diejenigen, die suchen java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

Ich stand vor der gleichen Ausnahme; Ich habe alles versucht und wichtige Dinge, damit es funktioniert, sind:

  1. Richtige Version von pdf lib.jar (In meinem Fall war es eine falsche Version, die in der Server-Laufzeit gespeichert wurde)
  2. Erstellen Sie einen Ordner, behalten Sie das pdflib-Glas darin und fügen Sie den Ordner in Ihre PATH-Variable ein

Es hat mit Tomcat 6 funktioniert.

Mangesh M. Kulkarni
quelle
3
  1. Wenn Sie glauben, dass Sie einen Pfad der nativen Bibliothek hinzugefügt haben %PATH%, testen Sie mit:

    System.out.println(System.getProperty("java.library.path"))

Es sollte Ihnen tatsächlich zeigen, ob Ihre DLL aktiviert ist %PATH%

  1. Starten Sie die IDE-Idee neu, die nach dem Einrichten der env-Variablen für mich zu funktionieren schien, indem Sie sie der hinzufügen %PATH%
AlexPes
quelle
2

Ich armer ! verbrachte einen ganzen Tag dahinter. Schreiben Sie es hier auf, wenn jemand dieses Problem wiederholt.

Ich habe versucht zu laden, wie Adam es vorgeschlagen hatte, wurde dann aber mit der Ausnahme AMD64 vs IA 32 erwischt. Wenn Sie auf jeden Fall nach Adams (zweifellos die beste Wahl) Vorgehensweise gearbeitet haben, versuchen Sie, eine 64-Bit-Version des neuesten jre zu haben. Stellen Sie sicher Ihre JRE UND JDK sind 64-Bit und Sie haben sie korrekt zu Ihrem Klassenpfad hinzugefügt.

Mein Arbeitsbeispiel lautet hier: Unbefriedigter Linkfehler

sayannayas
quelle
0

Für Windows stellte ich fest, dass beim Laden der Füllungen (jd2xsx.dll ruft & ftd2xx.dll auf) in den Ordner windowws / system32 die Probleme behoben wurden. Ich hatte dann ein Problem mit meiner neueren fd2xx.dll, die mit Parametern zu tun hatte, weshalb ich die ältere Version dieser DLL laden musste. Ich werde das später herausfinden müssen.

Hinweis: Die Datei jd2xsx.dll ruft die Datei ftd2xx.dll auf, sodass das Festlegen des Pfads für die Datei jd2xx.dll möglicherweise nicht funktioniert.

Dan Carte
quelle
0

Ich verwende Mac OS X Yosemite und Netbeans 8.02. Ich habe den gleichen Fehler erhalten und die einfache Lösung, die ich gefunden habe, ist wie oben. Dies ist nützlich, wenn Sie eine native Bibliothek in das Projekt aufnehmen müssen. Machen Sie das nächste für Netbeans:

1.- Right click on the Project
2.- Properties
3.- Click on RUN
4.- VM Options: java -Djava.library.path="your_path"
5.- for example in my case: java -Djava.library.path=</Users/Lexynux/NetBeansProjects/NAO/libs>
6.- Ok

Ich hoffe, es könnte für jemanden nützlich sein. Der Link, unter dem ich die Lösung gefunden habe, ist hier: java.library.path - Was ist das und wie wird es verwendet?

alexventuraio
quelle
Hallo Alex, ich habe eine Frage hier gibt es ein Problem, wenn der Bibliothekspfad einige Abstände im Pfad in dieser Zeile enthältVM Options: java -Djava.library.path="your_path"
Lokesh Pandey
Mmm, ich arbeite seit einem Jahr nicht mehr mit Java, aber da Konflikte auftreten können, empfehle ich, keine Leerzeichen in den Dateipfad einzufügen .
Alexventuraio
0

Ich hatte das gleiche Problem und der Fehler war auf eine Umbenennung der DLL zurückzuführen. Es kann vorkommen, dass der Bibliotheksname auch irgendwo in der DLL geschrieben ist. Als ich seinen ursprünglichen Namen zurücklegte, konnte ich mit ladenSystem.loadLibrary

Alessandro Marini
quelle
0

Es ist einfach, einfach java -XshowSettings: properties in Ihre Befehlszeile in Windows zu schreiben und dann alle Dateien in den von java.library.path angezeigten Pfad einzufügen.

Ali Sasoli
quelle