java.lang.Uns พอใจLinkError no *****. dll ใน java.library.path


92

ฉันจะโหลดไฟล์ dll แบบกำหนดเองในเว็บแอปพลิเคชันของฉันได้อย่างไร? ฉันได้ลองสิ่งต่อไปนี้:

  • คัดลอก dll ที่จำเป็นทั้งหมดในsystem32โฟลเดอร์และพยายามโหลดหนึ่งในนั้นในตัวServletสร้างSystem.loadLibrary
  • คัดลอก dll ที่จำเป็นลงในtomcat_home/shared/libและtomcat_home/common/lib

dll ทั้งหมดนี้อยู่ในWEB-INF/libเว็บแอปพลิเคชัน

คำตอบ:


156

เพื่อSystem.loadLibrary()ให้ทำงานได้ไลบรารี (บน Windows, DLL) ต้องอยู่ในไดเร็กทอรีที่ใดที่หนึ่งบนคุณPATH หรือบนเส้นทางที่แสดงรายการในjava.library.pathคุณสมบัติระบบ (คุณจึงสามารถเปิดใช้งาน Java ได้เช่นjava -Djava.library.path=/path/to/dir)

นอกจากนี้สำหรับloadLibrary()คุณระบุชื่อฐานของไลบรารีโดยไม่ต้อง.dllต่อท้าย ดังนั้น/path/to/something.dllคุณจะใช้System.loadLibrary("something")ไฟล์.

คุณต้องดูที่แน่นอนUnsatisfiedLinkErrorที่คุณจะได้รับ หากมีข้อความว่า:

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

จากนั้นไม่พบไลบรารีfoo (foo.dll) ในPATHหรือjava.library.path. หากมีข้อความว่า:

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

จากนั้นมีบางอย่างผิดปกติกับไลบรารีในแง่ที่ว่า Java ไม่สามารถแมปฟังก์ชัน Java ดั้งเดิมในแอปพลิเคชันของคุณกับคู่หูดั้งเดิมที่แท้จริงได้

ในการเริ่มต้นฉันจะบันทึกการSystem.loadLibrary()โทรของคุณเพื่อดูว่าดำเนินการอย่างถูกต้องหรือไม่ หากมีข้อยกเว้นหรือไม่อยู่ในเส้นทางรหัสที่ดำเนินการจริงคุณจะได้รับคำUnsatisfiedLinkErrorอธิบายประเภทหลังข้างต้นเสมอ

ในฐานะที่เป็น sidenote คนส่วนใหญ่ใส่การloadLibrary()เรียกของพวกเขาลงในบล็อก initializer แบบคงที่ในคลาสด้วยเมธอดดั้งเดิมเพื่อให้แน่ใจว่าจะดำเนินการครั้งเดียวเสมอ:

class Foo {

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

    public Foo() {
    }

}

1
โดยใส่ dll ทั้งหมดใน System32 และใช้ System.loadLibrary ("something") ทำงาน ฉันกำลังทำ System.loadLibrary ("something.dll") ก่อนหน้านี้ เหตุใดจึงไม่สามารถโหลด dll ทั้งหมดจาก WEB-INF ได้ ฉันเดาว่ามันจะโหลดขวดทั้งหมดตามค่าเริ่มต้น ฉันจะทำอย่างไรเพื่อโหลด dll เหล่านี้จาก WEB-INF โดยตรงแทน System32 / ระบุใน java.library.path
Ketan Khairnar

2
+1 สำหรับคำว่า "ใช้" bla "แทน" bla.dll "- loadLibrary()มีประโยชน์มากเมื่อคุณไม่รู้ว่าคุณทำผิดอะไร
MarnixKlooster ReinstateMonica

21
ในระบบของฉัน (Linux & java7) ฉันต้องการlibคำนำหน้า ดังนั้นความต้องการSystem.loadLibrary("foo") libfoo.so
kristianlm

2
ขอบคุณ. มันใช้ได้ผลสำหรับฉันเมื่อฉันอัปเดต "PATH" สำหรับ windows เพื่อให้มีโฟลเดอร์ที่มีไฟล์ * .so
user613114

ฉันจะได้สาบานว่า OSX จำเป็นและลินุกซ์blah.jnilib libblah.soสองชั่วโมงต่อมาหลังจากพยายามหลายครั้งฉันก็สรุปได้ว่า OSX ต้องการlibคำนำหน้าเช่นกัน
Jovan Perovic

15

การเปลี่ยนตัวแปร 'java.library.path' ที่รันไทม์นั้นไม่เพียงพอเนื่องจาก JVM อ่านเพียงครั้งเดียว คุณต้องรีเซ็ตมันเช่น:

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

กรุณาใช้ยกเค้าที่: การเปลี่ยนเส้นทาง Java Runtime


10

คำตอบเดิมของ Adam Batkin จะนำคุณไปสู่วิธีแก้ปัญหา แต่ถ้าคุณปรับใช้เว็บแอปอีกครั้ง (โดยไม่ต้องรีสตาร์ทคอนเทนเนอร์เว็บของคุณ) คุณควรพบข้อผิดพลาดต่อไปนี้:

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)

สิ่งนี้เกิดขึ้นเนื่องจาก ClassLoader ที่โหลด DLL ของคุณ แต่เดิมยังคงอ้างอิง DLL นี้ อย่างไรก็ตามขณะนี้เว็บแอปของคุณทำงานด้วย ClassLoader ใหม่และเนื่องจาก JVM เดียวกันกำลังทำงานอยู่และ JVM ไม่อนุญาตให้มีการอ้างอิง 2 รายการไปยัง DLL เดียวกันคุณจึงไม่สามารถโหลดซ้ำได้ ดังนั้นเว็บแอปของคุณจึงไม่สามารถเข้าถึง DLL ที่มีอยู่และไม่สามารถโหลดใหม่ได้ ดังนั้น .... คุณติดอยู่

เอกสาร ClassLoader ของ Tomcat จะอธิบายว่าเหตุใดเว็บแอปที่โหลดซ้ำของคุณจึงทำงานใน ClassLoader ใหม่ที่แยกออกมาและคุณจะแก้ไขข้อ จำกัด นี้ได้อย่างไร (ในระดับที่สูงมาก)

วิธีแก้ปัญหาคือการขยายโซลูชันของ Adam Batkin เล็กน้อย:

   package awesome;

   public class Foo {

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

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

    }

จากนั้นวาง jar ที่มีเพียงคลาสที่คอมไพล์นี้ลงในโฟลเดอร์ TOMCAT_HOME / lib

ตอนนี้ภายใน webapp ของคุณคุณต้องบังคับให้ Tomcat อ้างอิงคลาสนี้ซึ่งสามารถทำได้ง่ายๆดังนี้:

  Class.forName("awesome.Foo");

ตอนนี้ DLL ของคุณควรโหลดใน classloader ทั่วไปและสามารถอ้างอิงได้จากเว็บแอปของคุณแม้ว่าจะถูกปรับใช้ใหม่

เข้าท่า?

สำเนาอ้างอิงการทำงานสามารถพบได้ในรหัส Google, คง dll-bootstrapper


1
คำตอบนี้ยอดเยี่ยมสำหรับแอปพลิเคชันเซิร์ฟเวอร์และควรมีคำถามของตัวเองเนื่องจากคำถามนี้เสีย
JoshDM

8

คุณสามารถใช้ได้ System.load()เพื่อระบุเส้นทางที่แน่นอนซึ่งเป็นสิ่งที่คุณต้องการแทนที่จะเป็นไฟล์ในโฟลเดอร์ไลบรารีมาตรฐานสำหรับระบบปฏิบัติการที่เกี่ยวข้อง

หากคุณต้องการแอปพลิเคชันเนทีฟที่มีอยู่แล้วให้ใช้ System.loadLibrary(String filename)หากคุณต้องการแอพพลิเคชันที่มีอยู่แล้วให้ใช้หากคุณต้องการจัดหาของคุณเองคุณอาจจะดีกว่าด้วย load ()

คุณควรจะสามารถใช้loadLibraryกับjava.library.pathชุดได้อย่างถูกต้อง ดูClassLoader.javaแหล่งที่มาของการใช้งานที่แสดงทั้งสองเส้นทางที่กำลังตรวจสอบ (OpenJDK)


ขอบคุณ. การใช้โหลดนั้นง่ายกว่าและใช้งานง่ายกว่า IMHO ไม่สามารถรับ loadLibrary ให้ทำงานได้ไม่ว่าฉันจะทำอะไร ...
Plankalkül

2
วิธีนี้ใช้ไม่ได้กับไลบรารีที่โหลดไลบรารีดั้งเดิมของตนเอง
Justin Skiles

7

ในกรณีที่ปัญหาคือ System.loadLibrary ไม่พบ DLL ที่เป็นปัญหาความเข้าใจผิดทั่วไปอย่างหนึ่ง (เสริมด้วยข้อความแสดงข้อผิดพลาดของ Java) คือคุณสมบัติของระบบ java.library.path คือคำตอบ หากคุณตั้งค่าคุณสมบัติระบบ java.library.path เป็นไดเร็กทอรีที่ DLL ของคุณอยู่ System.loadLibrary จะพบ DLL ของคุณ อย่างไรก็ตามหาก DLL ของคุณขึ้นอยู่กับ DLL อื่น ๆ ตามที่มักจะเป็นเช่นนั้น java.library.path ก็ไม่สามารถช่วยได้เนื่องจากการโหลด DLL ที่ขึ้นต่อกันนั้นได้รับการจัดการโดยระบบปฏิบัติการทั้งหมดซึ่งไม่รู้อะไรจาก java.library เส้นทาง. ดังนั้นจึงเป็นการดีกว่าเสมอที่จะข้าม java.library.path และเพิ่มไดเร็กทอรี DLL ของคุณไปยัง LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS) หรือ Path (Windows) ก่อนเริ่ม JVM

(หมายเหตุ: ฉันใช้คำว่า "DLL" ในความหมายทั่วไปของ DLL หรือไลบรารีที่ใช้ร่วมกัน)


5

หากคุณต้องการโหลดไฟล์ที่สัมพันธ์กับไดเร็กทอรีบางรายการที่คุณอยู่ (เช่นในไดเร็กทอรีปัจจุบัน) นี่เป็นวิธีง่ายๆ

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

4

สำหรับใครที่กำลังมองหา java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

ฉันกำลังเผชิญกับข้อยกเว้นเดียวกัน ฉันพยายามทุกอย่างและสิ่งสำคัญที่จะทำให้มันใช้งานได้คือ:

  1. เวอร์ชันที่ถูกต้องของ pdf lib.jar (ในกรณีของฉันมันผิดเวอร์ชัน jar ที่เก็บไว้ในรันไทม์ของเซิร์ฟเวอร์)
  2. สร้างโฟลเดอร์และเก็บ jar pdflib ไว้และเพิ่มโฟลเดอร์ในตัวแปร PATH ของคุณ

มันทำงานร่วมกับแมวตัวผู้ 6


3
  1. หากคุณเชื่อว่าคุณได้เพิ่มเส้นทางของ lib ดั้งเดิมให้%PATH%ลองทดสอบด้วย:

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

มันควรจะแสดงให้คุณเห็นจริง ๆ หาก dll ของคุณเปิดอยู่ %PATH%

  1. รีสตาร์ท IDE Idea ซึ่งดูเหมือนจะใช้งานได้สำหรับฉันหลังจากที่ฉันตั้งค่าตัวแปร env โดยเพิ่มลงในไฟล์ %PATH%

2

น่าสงสารฉัน! ใช้เวลาทั้งวันอยู่เบื้องหลังสิ่งนี้เขียนไว้ที่นี่หากมีเนื้อหาลอกเลียนแบบปัญหานี้

ฉันพยายามโหลดตามที่ Adam แนะนำ แต่ก็พบข้อยกเว้นของ AMD64 เทียบกับ IA 32 หากในกรณีใด ๆ หลังจากทำงานตามคำแนะนำแบบของ Adam (ไม่ต้องสงสัยเลยว่าเป็นตัวเลือกที่ดีที่สุด) ให้ลองใช้ jre เวอร์ชัน 64 บิตล่าสุดตรวจสอบให้แน่ใจ JRE และ JDK ของคุณเป็น 64 บิตและคุณได้เพิ่มลงใน classpath ของคุณอย่างถูกต้อง

ตัวอย่างการทำงานของฉันอยู่ที่นี่: ข้อผิดพลาดลิงก์ไม่เป็นที่พอใจ


0

สำหรับ windows ฉันพบว่าเมื่อฉันโหลดไฟล์ (jd2xsx.dll เรียก & ftd2xx.dll) ลงในโฟลเดอร์ windowws / system32 สิ่งนี้สามารถแก้ไขปัญหาได้ จากนั้นฉันมีปัญหากับ fd2xx.dll รุ่นใหม่ของฉันที่เกี่ยวข้องกับพารามิเตอร์ซึ่งเป็นสาเหตุที่ฉันต้องโหลด dll รุ่นเก่ากว่านี้ ฉันจะต้องกำจัดสิ่งนี้ออกในภายหลัง

หมายเหตุ: jd2xsx.dll เรียก ftd2xx.dll ดังนั้นการตั้งค่าเส้นทางสำหรับ jd2xx.dll อาจไม่ทำงาน


0

ฉันใช้ Mac OS X Yosemite และ Netbeans 8.02 ฉันได้รับข้อผิดพลาดเดียวกันและวิธีแก้ปัญหาง่ายๆที่ฉันพบก็เหมือนข้างต้นสิ่งนี้มีประโยชน์เมื่อคุณต้องการรวมไลบรารีเนทีฟไว้ในโครงการ ดังนั้นทำสิ่งต่อไปสำหรับ 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

ฉันหวังว่ามันจะเป็นประโยชน์สำหรับใครบางคน ลิงค์ที่ฉันพบวิธีแก้ปัญหาอยู่ที่นี่: java.library.path - มันคืออะไรและใช้อย่างไร


สวัสดีอเล็กซ์ฉันมีคำถามที่นี่จะมีปัญหาหรือไม่ถ้าเส้นทางห้องสมุดมีระยะห่างบางส่วนในเส้นทางในบรรทัดนี้VM Options: java -Djava.library.path="your_path"
Lokesh Pandey

อืมผมไม่ได้ทำงานกับ Java สำหรับปีที่ผ่านมา แต่เป็นความขัดแย้งที่อาจปรากฏผมขอแนะนำให้หลีกเลี่ยงจากการเพิ่มพื้นที่ว่างในเส้นทางของแฟ้ม
alexventuraio

0

ฉันมีปัญหาเดียวกันและข้อผิดพลาดเกิดจากการเปลี่ยนชื่อ dll อาจเกิดขึ้นได้ว่าชื่อไลบรารีถูกเขียนไว้ใน dll เมื่อฉันใส่ชื่อเดิมกลับฉันสามารถโหลดโดยใช้System.loadLibrary


0

มันง่ายมากเพียงแค่เขียนคุณสมบัติ java -XshowSettings: บนบรรทัดคำสั่งของคุณใน windows แล้ววางไฟล์ทั้งหมดในพา ธ ที่แสดงโดย java.library.path


0

ขั้นแรกคุณจะต้องตรวจสอบให้แน่ใจว่าไดเร็กทอรีไปยังไลบรารีดั้งเดิมของคุณอยู่ในไฟล์java.library.path. ดูวิธีการทำที่นี่ จากนั้นคุณสามารถโทรSystem.loadLibrary(nativeLibraryNameWithoutExtension)- ตรวจสอบว่าไม่ได้รวมนามสกุลไฟล์ไว้ในชื่อไลบรารีของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.