Java:加载依赖于其他库的库

发布于 2024-11-04 08:13:16 字数 2746 浏览 0 评论 0 原文

我想在我的 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 语言)来测试动态加载程序是否工作。

#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;
}

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

I want to load my own native libraries in my java application. Those native libraries depend upon third-party libraries (which may or may not be present when my application is installed on the client computer).

Inside my java application, I ask the user to specify the location of dependent libs. Once I have this information, I am using it to update the "LD_LIBRARY_PATH" environment variable using JNI code. The following is the code snippet that I am using to change the "LD_LIBRARY_PATH" environment variable.

Java code


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 code (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;
}

I also have corresponding native methods to fetch the environment variable.

I can successfully update the LD_LIBRARY_PATH (this assertion is based on the output of C routine getenv().

I am still not able to load my native library. The dependent third-party libraries are still not detected.

Any help/pointers are appreciated. I am using Linux 64 bit.

Edit:

I wrote a SSCE (in C) to test if dynamic loader is working. Here is the 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;
}

The C code also does not work. Apparently, the dynamic loader is not rereading the LD_LIBRARY_PATH environment variable. I need to figure out how to force the dynamic loader to re-read the LD_LIBRARY_PATH environment variable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酸甜透明夹心 2024-11-11 08:13:21

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

Subversion Edge 是一款 Web 应用程序,可帮助您通过 Web 控制台管理 Subversion 存储库,并使用 SIGAR 到 SIGAR 库,帮助我们直接从操作系统...您需要在运行时更新属性“java.library.path”的值。 (

以下示例是上面 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 等)提供匹配的库版本。

I have successfully implemented something similar for CollabNet Subversion Edge, which depends on the SIGAR libraries across ALL Operating Systems (we support Windows/Linux/Sparc both 32 bits and 64 bits)...

Subversion Edge is a web application that helps one managing Subversion repositories through a web console and uses SIGAR to the SIGAR libraries helps us provide users data values directly from the OS... You need to update the value of the property "java.library.path" at runtime. (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 Note that the URL is a Groovy code, but I have modified it to a Java here)...

The following example is the implementation in URL above... (On Windows, your user will be required to restart the machine if he/she has downloaded the libraries after or downloaded them using your application)... The "java.library.path" will update the user's path "usr_paths" instead of System path "sys_paths" (permissions exception might be raised depending on the OS when using the latter).

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 }

The Bootstrap services (Groovy on Grails application) class of the console runs a service and executes it with the full path to the library directory... UNiX-based servers do not need to restart the server to get the libraries, but Windows boxes do need a server restart after the installation. In your case, you would be calling this as follows:

     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  }

For each OS you ship your application, just make sure to provide a matching version of the libraries for the specific platform (32bit-Linux, 64bit-Windows, etc...).

傲性难收 2024-11-11 08:13:20

请参阅此处接受的答案:

在运行时为 ctypes 更改 LD_LIBRARY_PATH

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

See the accepted answer here:

Changing LD_LIBRARY_PATH at runtime for ctypes

In other words, what you're trying to do isn't possible. You'll need to launch a new process with an updated LD_LIBRARY_PATH (e.g., use ProcessBuilder and update environment() to concatenate the necessary directory)

儭儭莪哋寶赑 2024-11-11 08:13:20

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

This is a hack used to manipulate JVM's library path programmatically. NOTE: it relies on internals of ClassLoader implementation so it might not work on all JVMs/versions.

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

This code uses UNIX-style file path separators ('/') and library path separator (':'). For cross-platform way of doing this use System Properties to get system-specific separators: http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文