3

我想在我的 java 应用程序中加载我自己的本机库。这些本机库依赖于第三方库(当我的应用程序安装在客户端计算机上时,这些库可能存在也可能不存在)。

在我的 java 应用程序中,我要求用户指定依赖库的位置。获得此信息后,我将使用它来使用 JNI 代码更新“LD_LIBRARY_PATH”环境变量。以下是我用来更改“LD_LIBRARY_PATH”环境变量的代码片段。

Java 代码

public static final int setEnv(String key, String value) {
        如果(键==空){
            throw new NullPointerException("key 不能为 null");
        }
        如果(值 == 空){
            throw new NullPointerException("值不能为空");
        }
        返回nativeSetEnv(键,值);
    }

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;
    常量字符 *nativeValue = NULL;
    nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
    nativeValue = (*env)->GetStringUTFChars(env, value, NULL);
    int result = setenv(nativeKey, nativeValue, 1);
    返回(jint)结果;
}

我也有相应的本地方法来获取环境变量。

我可以成功更新 LD_LIBRARY_PATH(此断言基于 C 例程的输出getenv()

我仍然无法加载我的本机库。仍然没有检测到依赖的第三方库。

任何帮助/指针表示赞赏。我正在使用 Linux 64 位。

编辑:

我写了一个 SSCE(用 C 语言)来测试动态加载器是否工作。这里是 SSCE

#包括
#包括
#包括
#包括

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

    const char* constdependentLibPath = "...:";
    const char* const sharedLibrary = "...";
    字符 *newLibPath = NULL;
    字符 *originalLibPath = NULL;
    int l1,l2,结果;
    无效*句柄 = NULL;

    originalLibPath = getenv("LD_LIBRARY_PATH");
    fprintf(stdout,"\n原始库路径 =%s\n",originalLibPath);
    l1 = strlen(originalLibPath);
    l2 = strlen(dependentLibPath);
    newLibPath = (char *)malloc((l1+l2)*sizeof(char));
    strcpy(newLibPath,dependentLibPath);
    strcat(newLibPath,originalLibPath);
    fprintf(stdout,"\n新库路径 =%s\n",newLibPath);

    结果 = setenv("LD_LIBRARY_PATH", newLibPath, 1);
    如果(结果!= 0){
        fprintf(stderr,"\n环境无法更新\n");
        退出(1);
    }    
    newLibPath = getenv("LD_LIBRARY_PATH");
    fprintf(stdout,"\n来自 env 的新库路径 =%s\n",newLibPath);

    句柄 = dlopen(sharedLibrary, RTLD_NOW);
    如果(句柄==NULL){
        fprintf(stderr,"\n无法加载共享库:%s\n",dlerror());
        退出(1);        
    }
    fprintf(stdout,"\n 共享库加载成功。\n");

    结果 = dlclose(句柄);
    如果(结果!= 0){
        fprintf(stderr,"\n无法卸载共享库:%s\n",dlerror());
        退出(1);
    }

    返回0;
}

C 代码也不起作用。显然,动态加载程序没有重新读取 LD_LIBRARY_PATH 环境变量。我需要弄清楚如何强制动态加载器重新读取 LD_LIBRARY_PATH 环境变量。

4

3 回答 3

2

这是一种用于以编程方式操作 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

于 2011-04-30T00:02:21.637 回答
2

在此处查看接受的答案:

在运行时为 ctypes 更改 LD_LIBRARY_PATH

换句话说,您尝试做的事情是不可能的。您需要使用更新的 LD_LIBRARY_PATH 启动一个新进程(例如,使用ProcessBuilder和更新environment()来连接必要的目录)

于 2011-04-29T22:52:06.407 回答
0

我已经成功地为 CollabNet Subversion Edge 实现了类似的东西,它依赖于所有操作系统的SIGAR 库(我们支持 32 位和 64 位的 Windows/Linux/Sparc)......

Subversion Edge 是一个 Web 应用程序,可帮助您通过 Web 控制台管理 Subversion 存储库,并使用 SIGAR 到 SIGAR 库帮助我们直接从操作系统提供用户数据值......您需要更新属性“java.library”的值。路径”在运行时。( 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 }

控制台的 Bootstrap 服务(Groovy on 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 位 Windows 等)提供匹配的库版本。

于 2011-04-30T01:54:23.107 回答