2011-04-29 47 views
3

我想在我的Java應用程序中加載自己的本地庫。這些本地庫依賴於第三方庫(當我的應用程序安裝在客戶端計算機上時,可能會或可能不存在)。Java:加載依賴於其他庫的庫

在我的java應用程序中,我要求用戶指定依賴庫的位置。獲得這些信息後,我使用它來使用JNI代碼更新「LD_LIBRARY_PATH」環境變量。以下是我用來更改「LD_LIBRARY_PATH」環境變量的代碼片段。

Java代碼

 

public static final int setEnv(String key, String value) { 
     if (key == null) { 
      throw new NullPointerException("key cannot be null"); 
     } 
     if (value == null) { 
      throw new NullPointerException("value cannot be null"); 
     } 
     return nativeSetEnv(key, value); 
    } 

public static final native int nativeSetEnv(String key, String value); 

JNI代碼(C)

 
    JNIEXPORT jint JNICALL Java_Test_nativeSetEnv(JNIEnv *env, jclass cls, jstring key, jstring value) { 
    const char *nativeKey = NULL; 
    const char *nativeValue = NULL; 
    nativeKey = (*env)->GetStringUTFChars(env, key, NULL); 
    nativeValue = (*env)->GetStringUTFChars(env, value, NULL); 
    int result = setenv(nativeKey, nativeValue, 1); 
    return (jint) result; 
} 

我也有相應的天然方法來獲取環境變量。

我可以成功地更新LD_LIBRARY_PATH(這種說法是基於C例程getenv()

我仍然無法加載我的本地庫的輸出。仍不能檢測的依賴第三方庫。

任何幫助/指針讚賞我使用Linux 64位

編輯:。

我寫了一個SSCE(C語言)來測試,如果動態加載程序是在這裏工作是SSCE

 
#include 
#include 
#include 
#include 

int main(int argc, const char* const argv[]) { 

    const char* const dependentLibPath = "...:"; 
    const char* const sharedLibrary = "..."; 
    char *newLibPath = NULL; 
    char *originalLibPath = NULL; 
    int l1, l2, result; 
    void* handle = NULL; 

    originalLibPath = getenv("LD_LIBRARY_PATH"); 
    fprintf(stdout,"\nOriginal library path =%s\n",originalLibPath); 
    l1 = strlen(originalLibPath); 
    l2 = strlen(dependentLibPath); 
    newLibPath = (char *)malloc((l1+l2)*sizeof(char)); 
    strcpy(newLibPath,dependentLibPath); 
    strcat(newLibPath,originalLibPath); 
    fprintf(stdout,"\nNew library path =%s\n",newLibPath); 

    result = setenv("LD_LIBRARY_PATH", newLibPath, 1); 
    if(result!=0) { 
     fprintf(stderr,"\nEnvironment could not be updated\n"); 
     exit(1); 
    }  
    newLibPath = getenv("LD_LIBRARY_PATH"); 
    fprintf(stdout,"\nNew library path from the env =%s\n",newLibPath); 

    handle = dlopen(sharedLibrary, RTLD_NOW); 
    if(handle==NULL) { 
     fprintf(stderr,"\nCould not load the shared library: %s\n",dlerror()); 
     exit(1);   
    } 
    fprintf(stdout,"\n The shared library was successfully loaded.\n"); 

    result = dlclose(handle); 
    if(result!=0) { 
     fprintf(stderr,"\nCould not unload the shared library: %s\n",dlerror()); 
     exit(1); 
    } 

    return 0; 
} 

C代碼也不起作用。顯然,動態加載器不會重讀LD_LIBRARY_PATH環境變量。我需要弄清楚如何強制動態加載器重新讀取LD_LIBRARY_PATH環境變量。

+0

我真的不明白爲什麼它不起作用,因爲我已經做了一些非常相似的事情(在Windows下),它的功能就像一個魅力。順便說一下,你是否嘗試過(僅用於調試目的),將System.load(...)加載到一個「默認」lib目錄中,以查看庫是否損壞(而不是nativeSetEnv代碼) 。然而,很好的問題(+1) – gd1 2011-04-29 22:42:23

+1

..這是主題,但我認爲你應該釋放由GetStringUTFChars分配的內存 – gd1 2011-04-29 22:42:56

+0

@Giacomo:它起作用,如果我啓動我的應用程序時設置LD_LIBRARY_PATH。我將釋放分配的內存。感謝您指出。 :) – 2011-05-02 19:04:56

回答

2

看到這裏接受的答案:

Changing LD_LIBRARY_PATH at runtime for ctypes

換句話說,你想做什麼是不可能的。您需要啓動一個更新後的LD_LIBRARY_PATH的新進程(例如,使用ProcessBuilder和更新environment()來連接必需的目錄)

+0

這是我們正在談論的Java,而不是Python。 – 2011-04-29 23:51:03

+1

當然,但問題依然存在:作爲本機java可執行文件一部分運行的動態加載程序代碼已經讀取了LD_LIBRARY_PATH環境變量。 – 2011-04-30 04:47:25

+0

我測試了你說的話,而你最可能是對的。現在,我該如何強制動態加載器重新讀取LD_LIBRARY_PATH環境變量?謝謝。 – 2011-05-02 19:48:20

0

這是一個用於以編程方式操作JVM庫路徑的hack。注意:它依賴於ClassLoader實現的內部,所以它可能不適用於所有的JVM /版本。

String currentPath = System.getProperty("java.library.path"); 
System.setProperty("java.library.path", currentPath + ":/path/to/my/libs"); 

// this forces JVM to reload "java.library.path" property 
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); 
fieldSysPath.setAccessible(true); 
fieldSysPath.set(null, null); 

此代碼使用UNIX風格的文件路徑分隔符('/')和庫路徑分隔符(':')。對於這樣的使用系統屬性來獲得系統特定的分隔符的跨平臺方式:http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html

+0

如果我沒有弄錯,JVM使用「java.library.path」來搜索共享庫。使用Linux上的LD_LIBRARY_PATH和Windows上的PATH搜索依賴庫。在我的具體情況下,依賴共享庫的加載失敗。 – 2011-05-02 19:07:38

+0

是的你是對的。只是查看它:http://kalblogs.blogspot.com/2009/01/java.html – 2011-05-02 20:56:53

0

我已經成功地實施了CollabNet的Subversion的邊緣,這依賴於SIGAR libraries所有操作系統類似的事情(我們支持Windows/Linux的/ Sparc都是32位和64位)...

Subversion Edge是一個Web應用程序,可幫助您通過Web控制檯管理Subversion存儲庫,並將SIGAR用於SIGAR庫,幫助我們直接從操作系統向用戶提供數據值。 。你需要在運行時更新屬性「java.library.path」的值。(https://ctf.open.collab.net/integration/viewvc/viewvc.cgi/trunk/console/grails-app/services/com/collabnet/svnedge/console/OperatingSystemService.groovy?revision=1890&root=svnedge&system=exsy1005&view=markup請注意,該URL是一個Groovy代碼,但我已將其修改爲Java)...

以下示例是上述URL中的實現...(在Windows上,您的用戶將被要求如果他/她已下載庫或使用應用程序下載庫,請重新啓動機器)...「java.library.path」將更新用戶路徑「usr_paths」而不是系統路徑「sys_paths」(權限異常可能是在使用後者時取決於操作系統)。

133/** 
134 * Updates the java.library.path at run-time. 
135 * @param libraryDirPath 
136 */ 
137 public void addDirToJavaLibraryPathAtRuntime(String libraryDirPath) 
138 throws Exception { 
139 try { 
140   Field field = ClassLoader.class.getDeclaredField("usr_paths"); 
141   field.setAccessible(true); 
142   String[] paths = (String[])field.get(null); 
143   for (int i = 0; i < paths.length; i++) { 
144    if (libraryDirPath.equals(paths[i])) { 
145     return; 
146    } 
147   } 
148   String[] tmp = new String[paths.length+1]; 
149   System.arraycopy(paths,0,tmp,0,paths.length); 
150   tmp[paths.length] = libraryDirPath; 
151   field.set(null,tmp); 
152   String javaLib = "java.library.path"; 
153   System.setProperty(javaLib, System.getProperty(javaLib) + 
154    File.pathSeparator + libraryDirPath); 
155 
156  } catch (IllegalAccessException e) { 
157   throw new IOException("Failed to get permissions to set " + 
158    "library path to " + libraryDirPath); 
159  } catch (NoSuchFieldException e) { 
160   throw new IOException("Failed to get field handle to set " + 
161   "library path to " + libraryDirPath); 
162  } 
163 } 

的引導服務(Groovy中的Grails應用程序)類控制檯運行的服務,並與完整路徑庫目錄執行它...基於UNIX的服務器不需要重新啓動服務器以獲取這些庫,但Windows盒安裝後需要重新啓動服務器。在你的情況,你會調用這個如下:

 String appHomePath = "/YOUR/PATH/HERE/TO/YOUR/LIBRARY/DIRECTORY"; 
    String yourLib = new File(appHomePath, "SUBDIRECTORY/").getCanonicalPath(); 
124 try { 
125  addDirToJavaLibraryPathAtRuntime(yourLib); 
126 } catch (Exception e) { 
127  log.error("Error adding the MY Libraries at " + yourLib + " " + 
128   "java.library.path: " + e.message); 
129 } 

對於每次發貨您的應用程序的操作系統,只要確保特定平臺(32位Linux的,64位窗口提供的庫匹配版本等等......)。

+0

您發佈了與我相同的解決方案。我們儘量避免這樣做。 – 2011-04-30 08:38:16