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

92

Bagaimana cara memuat file dll kustom di aplikasi web saya? Saya sudah mencoba yang berikut ini:

  • Menyalin semua dll yang diperlukan dalam system32folder dan mencoba memuat salah satunya di ServletkonstruktorSystem.loadLibrary
  • Dll yang diperlukan disalin ke dalam tomcat_home/shared/libdantomcat_home/common/lib

Semua dll ini ada di WEB-INF/libaplikasi web

Ketan Khairnar
sumber

Jawaban:

156

Agar System.loadLibrary()dapat berfungsi, pustaka (di Windows, DLL) harus berada di direktori di suatu tempat di Anda PATH atau di jalur yang tercantum di java.library.pathproperti sistem (sehingga Anda dapat meluncurkan seperti Java java -Djava.library.path=/path/to/dir).

Selain itu, untuk loadLibrary(), Anda menentukan nama dasar pustaka, tanpa .dllakhiran. Jadi, untuk itu /path/to/something.dll, Anda hanya akan menggunakan System.loadLibrary("something").

Anda juga perlu melihat persis apa UnsatisfiedLinkErroryang Anda peroleh. Jika tertulis sesuatu seperti:

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

maka ia tidak dapat menemukan pustaka foo (foo.dll) di PATHatau java.library.path. Jika tertulis sesuatu seperti:

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

maka ada yang salah dengan pustaka itu sendiri dalam arti bahwa Java tidak dapat memetakan fungsi Java asli dalam aplikasi Anda ke mitra aslinya yang sebenarnya.

Untuk memulainya, saya akan meletakkan beberapa logging di sekitar System.loadLibrary()panggilan Anda untuk melihat apakah itu dijalankan dengan benar. Jika itu melontarkan pengecualian atau tidak dalam jalur kode yang benar-benar dieksekusi, maka Anda akan selalu mendapatkan jenis terakhir yang UnsatisfiedLinkErrordijelaskan di atas.

Sebagai catatan kecil, kebanyakan orang menempatkan loadLibrary()panggilan mereka ke dalam blok penginisialisasi statis di kelas dengan metode asli, untuk memastikan bahwa itu selalu dieksekusi tepat sekali:

class Foo {

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

    public Foo() {
    }

}
Adam Batkin
sumber
1
Dengan meletakkan semua dll di System32 dan menggunakan System.loadLibrary ("sesuatu") bekerja. Saya melakukan System.loadLibrary ("sesuatu.dll") sebelumnya. Mengapa tidak bisa memuat semua dll dari WEB-INF? Saya kira itu memuat semua toples secara default. Apa yang dapat saya lakukan untuk memuat dll ini dari WEB-INF secara langsung daripada System32 / menentukannya di java.library.path
Ketan Khairnar
2
+1 untuk 'gunakan "bla" daripada "bla.dll" untuk loadLibrary()komentar-- sangat berguna bila Anda tidak tahu apa yang Anda lakukan salah.
MarnixKlooster ReinstateMonica
21
Di sistem saya (Linux & java7), saya membutuhkan libawalan. Jadi System.loadLibrary("foo")kebutuhan libfoo.so.
kristianlm
2
Terima kasih. Ini bekerja untuk saya ketika saya memperbarui "PATH" untuk windows sehingga berisi folder yang memiliki file * .so.
pengguna613114
Saya berani bersumpah bahwa OSX dibutuhkan blah.jnilibdan Linux libblah.so. Nah, dua jam kemudian, setelah beberapa kali mencoba, saya sampai pada kesimpulan bahwa OSX juga memerlukan libawalan
Jovan Perovic
15

Mengubah variabel 'java.library.path' saat runtime tidak cukup karena hanya dibaca sekali oleh JVM. Anda harus mengatur ulang seperti:

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

Silakan, ambil jarahan di: Mengubah Jalur Perpustakaan Java pada Waktu Proses .

Alexandre
sumber
10

Jawaban asli oleh Adam Batkin akan mengarahkan Anda ke solusi, tetapi jika Anda menerapkan ulang webapp Anda (tanpa memulai ulang penampung web Anda), Anda akan mengalami kesalahan berikut:

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)

Ini terjadi karena ClassLoader yang awalnya memuat DLL Anda masih mereferensikan DLL ini. Namun, aplikasi web Anda sekarang berjalan dengan ClassLoader baru, dan karena JVM yang sama sedang berjalan dan JVM tidak mengizinkan 2 referensi ke DLL yang sama, Anda tidak dapat memuatnya kembali. Jadi, aplikasi web Anda tidak dapat mengakses DLL yang ada dan tidak dapat memuat yang baru. Jadi .... Anda terjebak.

Dokumentasi ClassLoader Tomcat menguraikan mengapa aplikasi web Anda yang dimuat ulang berjalan di ClassLoader baru yang terisolasi dan bagaimana Anda dapat mengatasi batasan ini (pada tingkat yang sangat tinggi).

Solusinya adalah memperluas solusi Adam Batkin sedikit:

   package awesome;

   public class Foo {

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

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

    }

Kemudian tempatkan jar yang berisi HANYA kelas yang dikompilasi ini ke dalam folder TOMCAT_HOME / lib.

Sekarang, di dalam aplikasi web Anda, Anda hanya perlu memaksa Tomcat untuk mereferensikan kelas ini, yang dapat dilakukan sesederhana ini:

  Class.forName("awesome.Foo");

Sekarang DLL Anda harus dimuat di classloader umum, dan dapat dirujuk dari aplikasi web Anda bahkan setelah diterapkan ulang.

Masuk akal?

Salinan referensi kerja dapat ditemukan di kode google, static-dll-bootstrapper .

Matt M.
sumber
1
Jawaban ini sangat bagus untuk server aplikasi dan harus memiliki pertanyaannya sendiri karena tidak berguna untuk pertanyaan ini.
JoshDM
8

Anda dapat menggunakan System.load()untuk menyediakan jalur absolut yang Anda inginkan, daripada file dalam folder pustaka standar untuk masing-masing OS.

Jika Anda menginginkan aplikasi asli yang sudah ada, gunakan System.loadLibrary(String filename). Jika Anda ingin menyediakan milik Anda sendiri, Anda mungkin lebih baik menggunakan load ().

Anda juga harus dapat menggunakan loadLibrarydengan java.library.pathset dengan benar. Lihat ClassLoader.javasumber implementasi yang menunjukkan kedua jalur yang sedang diperiksa (OpenJDK)

Philip Whitehouse
sumber
Terima kasih. Menggunakan beban IMHO sebenarnya jauh lebih mudah dan lebih intuitif. Tidak bisa mendapatkan beban Perpustakaan bekerja tidak peduli apa yang saya lakukan ...
Plankalkül
2
Solusi ini tidak berfungsi untuk perpustakaan yang memuat perpustakaan asli mereka sendiri.
Justin Skiles
7

Dalam kasus di mana masalahnya adalah bahwa System.loadLibrary tidak dapat menemukan DLL yang dimaksud, satu kesalahpahaman umum (diperkuat oleh pesan kesalahan Java) adalah bahwa properti sistem java.library.path adalah jawabannya. Jika Anda menyetel properti sistem java.library.path ke direktori tempat DLL Anda berada, maka System.loadLibrary memang akan menemukan DLL Anda. Namun, jika DLL Anda bergantung pada DLL lain, seperti yang sering terjadi, java.library.path tidak dapat membantu, karena pemuatan DLL dependen dikelola sepenuhnya oleh sistem operasi, yang tidak mengetahui apa pun tentang java.library. jalan. Dengan demikian, hampir selalu lebih baik untuk melewati java.library.path dan cukup menambahkan direktori DLL Anda ke LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS), atau Path (Windows) sebelum memulai JVM.

(Catatan: Saya menggunakan istilah "DLL" dalam pengertian umum DLL atau pustaka bersama.)

Ian Emmons
sumber
5

Jika Anda perlu memuat file yang terkait dengan beberapa direktori tempat Anda berada (seperti di direktori saat ini), berikut adalah solusi mudah:

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
sumber
4

Bagi yang mencari java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

Saya menghadapi pengecualian yang sama; Saya mencoba segalanya dan hal penting untuk membuatnya berhasil adalah:

  1. Versi yang benar dari pdf lib.jar (Dalam kasus saya itu adalah versi jar yang salah disimpan dalam runtime server)
  2. Buat folder dan simpan toples pdflib di dalamnya dan tambahkan folder tersebut di variabel PATH Anda

Ini bekerja dengan tomcat 6.

Mangesh M. Kulkarni
sumber
3
  1. Jika Anda yakin telah menambahkan jalur lib asli ke %PATH%, coba uji dengan:

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

Ini harus menunjukkan kepada Anda sebenarnya jika dll Anda aktif %PATH%

  1. Mulai ulang Ide IDE, yang tampaknya berfungsi untuk saya setelah saya menyiapkan variabel env dengan menambahkannya ke %PATH%
AlexPes
sumber
2

Kasihan aku! menghabiskan sepanjang hari di belakang ini. Menuliskannya di sini jika ada orang yang mengulangi masalah ini.

Saya mencoba memuat seperti yang disarankan Adam tetapi kemudian tertangkap dengan pengecualian AMD64 vs IA 32. Jika bagaimanapun setelah bekerja sesuai panduan Adam (tidak diragukan lagi pilihan terbaik), cobalah untuk memiliki versi 64 bit jre terbaru. JRE DAN JDK Anda 64 bit dan Anda telah menambahkannya dengan benar ke classpath Anda.

Contoh kerja saya berjalan di sini: kesalahan tautan tidak puas

sayannayas
sumber
0

Untuk windows saya menemukan bahwa ketika saya memuat filles (panggilan jd2xsx.dll & ftd2xx.dll) ke dalam folder windowws / system32 ini menyelesaikan masalah. Saya kemudian memiliki masalah dengan fd2xx.dll saya yang lebih baru berkaitan dengan parameter itulah sebabnya saya harus memuat versi lama dll ini. Aku harus membicarakan ini nanti.

Catatan: jd2xsx.dll memanggil ftd2xx.dll jadi hanya mengatur jalur untuk jd2xx.dll mungkin tidak berfungsi.

Dan Carte
sumber
0

Saya menggunakan Mac OS X Yosemite dan Netbeans 8.02, saya mendapat kesalahan yang sama dan solusi sederhana yang saya temukan adalah seperti di atas, ini berguna ketika Anda perlu menyertakan perpustakaan asli dalam proyek. Jadi lakukan selanjutnya untuk 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

Semoga bisa bermanfaat bagi seseorang. Tautan tempat saya menemukan solusinya ada di sini: java.library.path - Apa itu dan bagaimana cara menggunakannya

alexventuraio
sumber
Hai Alex, saya punya pertanyaan di sini apakah ada masalah jika jalur perpustakaan menyertakan beberapa spasi di jalur di baris iniVM Options: java -Djava.library.path="your_path"
Lokesh Pandey
Mmm Saya tidak bekerja dengan Java selama setahun terakhir tetapi karena konflik mungkin muncul, saya sarankan untuk tidak menambahkan spasi kosong di jalur file .
alexventuraio
0

Saya memiliki masalah yang sama dan kesalahan itu karena mengganti nama dll. Bisa jadi nama perpustakaan juga ditulis di suatu tempat di dalam dll. Ketika saya mengembalikan nama aslinya, saya bisa memuat menggunakanSystem.loadLibrary

Alessandro Marini
sumber
0

Sangat sederhana, cukup tulis java -XshowSettings: properti pada baris perintah Anda di windows dan kemudian tempelkan semua file di jalur yang ditunjukkan oleh java.library.path.

Ali Sasoli
sumber
0

Pertama, Anda ingin memastikan direktori ke perpustakaan asli Anda ada di java.library.path. Lihat bagaimana melakukannya di sini . Kemudian, Anda dapat menelepon System.loadLibrary(nativeLibraryNameWithoutExtension)- pastikan untuk tidak menyertakan ekstensi file dalam nama perpustakaan Anda.

Wilayah39
sumber