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

92

Jak mogę załadować niestandardowy plik dll w mojej aplikacji internetowej? Wypróbowałem następujące:

  • Skopiowałem wszystkie wymagane biblioteki dll w system32folderze i próbowałem załadować jeden z nich w ServletkonstruktorzeSystem.loadLibrary
  • Skopiowano wymagane biblioteki DLL do tomcat_home/shared/libitomcat_home/common/lib

Wszystkie te biblioteki DLL znajdują się w WEB-INF/libaplikacji internetowej

Ketan Khairnar
źródło

Odpowiedzi:

156

Aby System.loadLibrary()biblioteka (w systemie Windows, DLL) działała, musi znajdować się w katalogu gdzieś na twoim komputerze PATH lub na ścieżce wymienionej we java.library.pathwłaściwości systemowej (abyś mógł uruchomić Javę java -Djava.library.path=/path/to/dir).

Dodatkowo w przypadku loadLibrary()określasz nazwę podstawową biblioteki, bez .dllkońcówki. Więc po /path/to/something.dllprostu używałbyś System.loadLibrary("something").

Musisz także dokładnie przyjrzeć się temu UnsatisfiedLinkError, co otrzymujesz. Jeśli mówi coś takiego:

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

wtedy nie może znaleźć biblioteki foo (foo.dll) w twoim PATHlub java.library.path. Jeśli mówi coś takiego:

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

wtedy coś jest nie tak z samą biblioteką w tym sensie, że Java nie jest w stanie odwzorować natywnej funkcji Java w twojej aplikacji na jej rzeczywisty natywny odpowiednik.

Na początek umieściłbym trochę rejestrowania twojego System.loadLibrary()wywołania, aby sprawdzić, czy działa poprawnie. Jeśli zgłosi wyjątek lub nie znajduje się w ścieżce kodu, która jest faktycznie wykonywana, zawsze otrzymasz ten drugi typ UnsatisfiedLinkErrorwyjaśniony powyżej.

Na marginesie, większość ludzi umieszcza swoje loadLibrary()wywołania w statycznym bloku inicjalizatora w klasie z metodami natywnymi, aby upewnić się, że jest on wykonywany zawsze dokładnie raz:

class Foo {

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

    public Foo() {
    }

}
Adam Batkin
źródło
1
Umieszczenie wszystkich bibliotek dll w System32 i użycie System.loadLibrary („coś”) zadziałało. Robiłem wcześniej System.loadLibrary („coś.dll”). Dlaczego nie można załadować wszystkich bibliotek dll z WEB-INF? Domyślam się, że domyślnie ładuje wszystkie słoiki. Co mogę zrobić, aby załadować te biblioteki DLL z WEB-INF bezpośrednio zamiast z System32 / określić je w java.library.path
Ketan Khairnar
2
+1 za „użyj„ bla ”zamiast„ bla.dll ”jako loadLibrary()uwagi - bardzo przydatne, gdy nie masz pojęcia, co robisz źle.
MarnixKlooster ReinstateMonica
21
W moim systemie (Linux i java7) potrzebuję libprefiksu. Więc System.loadLibrary("foo")potrzebuje libfoo.so.
kristianlm
2
Dzięki. U mnie zadziałało, kiedy zaktualizowałem „PATH” dla systemu Windows, tak że zawiera folder zawierający plik * .so.
user613114
Mógłbym przysiąc, że wymagany jest OSX blah.jnilibi Linux libblah.so. Cóż, dwie godziny później, po wielu próbach, doszedłem do wniosku, że OSX również wymaga libprefiksu
Jovan Perovic
15

Zmiana zmiennej „java.library.path” w czasie wykonywania nie wystarczy, ponieważ jest ona odczytywana tylko raz przez maszynę JVM. Musisz to zresetować jak:

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

Zdobądź łup w: Changing Java Library Path at Runtime .

Alexandre
źródło
10

Oryginalna odpowiedź Adama Batkina doprowadzi Cię do rozwiązania, ale jeśli ponownie wdrożysz aplikację internetową (bez ponownego uruchamiania kontenera internetowego), powinieneś napotkać następujący błąd:

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)

Dzieje się tak, ponieważ ClassLoader, który pierwotnie załadował bibliotekę DLL, nadal odwołuje się do tej biblioteki DLL. Jednak Twoja aplikacja internetowa działa teraz z nowym ClassLoaderem, a ponieważ ta sama JVM jest uruchomiona, a JVM nie zezwala na 2 odwołania do tej samej biblioteki DLL, nie możesz jej ponownie załadować . Dlatego Twoja aplikacja internetowa nie może uzyskać dostępu do istniejącej biblioteki DLL i nie może załadować nowej. Więc ... utknęłaś.

Dokumentacja Tomcat ClassLoader przedstawia, dlaczego ponownie załadowana aplikacja internetowa działa w nowej, izolowanej aplikacji ClassLoader i jak można obejść to ograniczenie (na bardzo wysokim poziomie).

Rozwiązaniem jest niewielkie rozszerzenie rozwiązania Adama Batkina:

   package awesome;

   public class Foo {

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

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

    }

Następnie umieść jar zawierający TYLKO tę skompilowaną klasę w folderze TOMCAT_HOME / lib.

Teraz w aplikacji internetowej wystarczy zmusić Tomcat do odwoływania się do tej klasy, co można zrobić tak prosto:

  Class.forName("awesome.Foo");

Teraz Twoja biblioteka DLL powinna zostać załadowana do wspólnego modułu ładującego klasy i można do niej odwoływać się z aplikacji internetowej nawet po ponownym wdrożeniu.

Ma sens?

Roboczą kopię referencyjną można znaleźć w google code, static-dll-bootstrapper .

Matt M.
źródło
1
Ta odpowiedź jest doskonała dla serwerów aplikacji i powinna mieć własne pytanie, ponieważ jest marnowana na to pytanie.
JoshDM
8

Możesz użyć, System.load()aby podać bezwzględną ścieżkę, która jest tym, czego chcesz, zamiast pliku w folderze biblioteki standardowej dla odpowiedniego systemu operacyjnego.

Jeśli potrzebujesz natywnych aplikacji, które już istnieją, użyj System.loadLibrary(String filename). Jeśli chcesz udostępnić własne, prawdopodobnie lepiej będzie z load ().

Powinieneś także umieć poprawnie korzystać loadLibraryz java.library.pathzestawu. Zobacz ClassLoader.javaźródło implementacji pokazujące obie sprawdzane ścieżki (OpenJDK)

Philip Whitehouse
źródło
Dzięki. Korzystanie z obciążenia jest w rzeczywistości znacznie łatwiejsze i bardziej intuicyjne w IMHO. Nie można uruchomić loadLibrary bez względu na to, co zrobiłem ...
Plankalkül
2
To rozwiązanie nie działa w przypadku bibliotek, które ładują własne biblioteki natywne.
Justin Skiles
7

W przypadku, gdy problem polega na tym, że System.loadLibrary nie może znaleźć danej biblioteki DLL, jednym z powszechnych nieporozumień (potwierdzonych komunikatem o błędzie języka Java) jest to, że odpowiedzią jest właściwość systemowa java.library.path. Jeśli ustawisz właściwość systemową java.library.path na katalog, w którym znajduje się biblioteka DLL, wówczas System.loadLibrary rzeczywiście znajdzie tę bibliotekę DLL. Jeśli jednak biblioteka DLL jest z kolei zależna od innych bibliotek DLL, co często się zdarza, java.library.path nie może pomóc, ponieważ ładowaniem zależnych bibliotek DLL zarządza w całości system operacyjny, który nic nie wie o java.library. ścieżka. Dlatego prawie zawsze lepiej jest ominąć java.library.path i po prostu dodać katalog biblioteki DLL do LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS) lub Path (Windows) przed uruchomieniem JVM.

(Uwaga: używam terminu „DLL” w ogólnym znaczeniu biblioteki DLL lub biblioteki współdzielonej).

Ian Emmons
źródło
5

Jeśli chcesz załadować plik, który jest powiązany z katalogiem, w którym już jesteś (np. W bieżącym katalogu), oto proste rozwiązanie:

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
źródło
4

Dla szukających java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

Miałem ten sam wyjątek; Próbowałem wszystkiego, a ważne rzeczy, aby to działało, to:

  1. Poprawna wersja pliku pdf lib.jar (w moim przypadku był to zły jar wersji przechowywany w środowisku wykonawczym serwera)
  2. Utwórz folder i zachowaj w nim jar pdflib i dodaj ten folder do zmiennej PATH

To działało z tomcat 6.

Mangesh M. Kulkarni
źródło
3
  1. Jeśli uważasz, że dodałeś ścieżkę natywnej biblioteki do %PATH%, spróbuj przetestować z:

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

Powinien pokazać ci, czy twoja dll jest włączona %PATH%

  1. Zrestartuj pomysł IDE, który wydawał się działać po skonfigurowaniu zmiennej env, dodając ją do pliku %PATH%
AlexPes
źródło
2

Biedny ja ! spędził za tym cały dzień, zapisując to tutaj, jeśli ktoś powiela ten problem.

Próbowałem załadować, jak zasugerował Adam, ale potem złapałem wyjątek AMD64 vs IA 32.Jeśli w jakimkolwiek przypadku po pracy zgodnie z instrukcją Adama (bez wątpienia najlepszym wyborem), spróbuj mieć 64-bitową wersję najnowszego jre. twoje JRE i JDK są 64-bitowe i poprawnie dodałeś je do swojej ścieżki klas.

Oto mój przykład roboczy: błąd niezadowolonego linku

sayannayas
źródło
0

W przypadku systemu Windows stwierdziłem, że po załadowaniu plików filles (wywołania jd2xsx.dll & ftd2xx.dll) do folderu windowws / system32 rozwiązało to problemy. Wtedy miałem problem z moim nowszym fd2xx.dll związanym z parametrami, dlatego musiałem załadować starszą wersję tej biblioteki dll. Będę musiał to później przerobić.

Uwaga: jd2xsx.dll wywołuje ftd2xx.dll, więc samo ustawienie ścieżki do jd2xx.dll może nie działać.

Dan Carte
źródło
0

Używam Mac OS X Yosemite i Netbeans 8.02, mam ten sam błąd, a proste rozwiązanie, które znalazłem, jest takie jak powyżej, jest to przydatne, gdy potrzebujesz włączyć natywną bibliotekę do projektu. Zrób więc następną dla 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

Mam nadzieję, że komuś się przyda. Link gdzie znalazłem rozwiązanie jest tutaj: java.library.path - co to jest i jak z niego korzystać

alexventuraio
źródło
Cześć Alex, mam pytanie, czy jest jakiś problem, jeśli ścieżka biblioteki zawiera odstępy w ścieżce w tej liniiVM Options: java -Djava.library.path="your_path"
Lokesh Pandey,
Mmm Nie pracuję z Javą przez ostatni rok, ale ponieważ mogą pojawić się konflikty, radzę unikać dodawania pustych spacji w ścieżce pliku .
alexventuraio,
0

Miałem ten sam problem i błąd był spowodowany zmianą nazwy dll. Może się zdarzyć, że nazwa biblioteki jest również zapisana gdzieś w dll. Kiedy przywróciłem jego pierwotną nazwę, mogłem załadować za pomocąSystem.loadLibrary

Alessandro Marini
źródło
0

To proste, wystarczy napisać java -XshowSettings: properties w wierszu poleceń w systemie Windows, a następnie wkleić wszystkie pliki w ścieżce pokazanej przez java.library.path.

Ali Sasoli
źródło
0

Najpierw upewnij się, że katalog Twojej biblioteki natywnej znajduje się w katalogu java.library.path. Zobacz, jak to zrobić tutaj . Następnie możesz zadzwonić System.loadLibrary(nativeLibraryNameWithoutExtension)- uważając, aby nie dodawać rozszerzenia pliku w nazwie biblioteki.

Region39
źródło