2011-03-24 64 views
5

我正在用Java使用JNA封裝共享庫(用C編寫)。共享庫是在內部編寫的,但該庫使用來自另一個外部庫的函數,這又取決於另一個外部庫。因此,情況是這樣的:Java:利用依賴項加載共享庫管理器

EXT1 < - EXT2 < - 內部

即內部使用外部庫ext2,它再次使用外部庫ext1。我試過的是:

System.loadLibrary("ext1"); 
System.loadLibrary("ext2"); 
NativeLIbrary.loadLibrary("internal",xxx.class); 

這種方法在加載庫「ext2」時失敗,出現「UnresolvedException」;鏈接器抱怨庫中確實存在的符號「ext1」。因此,System.loadLibrary()函數不會使「ext1」的符號全局可用?當使用STDLIB函數dlopen()的爲:

handle = dlopen(lib_name , RTLD_GLOBAL); 

所有符號@lib_name發現將可用於在隨後的載荷符號分辨率;我想我想要的是類似於java的各種System.loadLibrary()?

問候 - Joakim Hove

+0

的路徑是在Java屬性'java.library.path'定義的庫? – 2011-03-24 20:48:22

+0

那麼定位庫實際上是另一個抱怨,我已經使用System.load(full_path_to_shared_library) - 但我認爲這個問題主要是netbeans問題。加載庫「ext」時的異常處於符號解析階段,即庫已正確定位等。我認爲/恐懼這與在運行時深層調用低層dlopen()中的RTLD_GLOBAL標誌(或缺少它)有關。 – user422005 2011-03-24 21:03:52

回答

2

OK;

我已經找到了一個可接受的解決方案,但不是沒有大量的箍。我所做的是

  1. 使用常規的JNA機制從動態鏈接庫(libdl.so)映射dlopen()函數。
  2. 使用與JNA映射的dlopen()函數通過選項RTLD_GLOBAL集加載外部庫「ext1」和「ext2」。

它實際上似乎工作:-)

0

試試這個,將此函數添加到您的代碼中。在加載你的dll之前調用它。對於參數,請使用您的dll的位置。

 

    public boolean addDllLocationToPath(String dllLocation) 
    { 
     try 
     { 
      System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation); 
      Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); 
      fieldSysPath.setAccessible(true); 
      fieldSysPath.set(null, null); 
     } 
     catch (Exception e) 
     { 
      System.err.println("Could not modify path"); 
      return false; 
     } 
     return true; 
    } 
} 
 
+1

感謝您的努力 - 您的建議在定位庫時非常有效。但不幸的是,這不是我的問題,問題是庫「ext2」中的符號不​​能用於「ext1」中的函數 - 直接使用dlopen()庫調用時(也許這是我必須做的)此行爲由整型標誌控制,我正在尋找的行爲是通過RTLD_GLOBAL標誌實現的。這似乎比我想象的更困難?! – user422005 2011-03-24 21:27:36

2

這是一個老問題,但我已經找到可接受的解決方案,這也應該是可移植的,我想我應該張貼一個答案。解決方法是使用JNANativeLibrary#getInstance(),因爲在Linux上,這將通過RTLD_GLOBALdlopen()(並且在Windows上,這不是必需的)。

現在,如果你使用這個庫來實現一個Java native方法,你還需要在同一個庫調用System.load()(或Sysem.loadLibrary()),調用NativeLibrary#getInstance()後。

首先,鏈接到JNA錯誤:JNA-61

中有註釋說,基本上是一個應該加載依賴實際的庫使用從內部JNA,而不是標準的Java方法之前。我就複製粘貼我的代碼,這是一個典型場景:

String libPath = 
     "/path/to/my/lib:" + // My library file 
     "/usr/local/lib:" + // Libraries lept and tesseract 
     System.getProperty("java.library.path"); 

System.setProperty("jna.library.path", libPath); 

NativeLibrary.getInstance("lept"); 
NativeLibrary.getInstance("tesseract"); 
OcrTesseractInterf ocrInstance = (OcrTesseractInterf) 
     Native.loadLibrary(OcrTesseractInterf.JNA_LIBRARY_NAME, OcrTesseractInterf.class); 

我寫了一個小型圖書館提供OCR功能使用的Tesseract我的Java應用程序。Tesseract依賴於Leptonica,所以要使用我的庫,我需要首先加載庫lepttesseract。使用標準方式(System.load()和System.loadLibrary())加載庫並不能解決問題,設置屬性jna.library.pathjava.library.path。顯然,JNA喜歡以自己的方式加載庫。

這對我來說很適合在Linux上,我猜如果設置正確的庫路徑,這也應該在其他操作系統中工作。

1

還有另一種解決方案。您可以直接在JNI代碼內部直接打印,如下所示:

void loadLibrary() { 
    if(handle == NULL) { 
    handle = dlopen("libname.so", RTLD_LAZY | RTLD_GLOBAL); 
    if (!handle) { 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 
    } 
} 

... 
... 

loadLibrary(); 

這樣,您將使用RTLD_GLOBAL打開庫。

你可以在這裏找到詳細的描述:http://www.owsiak.org/?p=3640

+0

謝謝,我一直在這個問題上好幾個小時,上述解決方案都沒有工作 – dargaud 2017-05-26 23:07:40

+0

.oOo。謝謝;).oOo。 – mko 2017-05-27 07:11:26