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

时间:2011-04-29 22:38:02

标签: java loadlibrary setenv

我想在我的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环境变量。

3 个答案:

答案 0 :(得分:2)

请参阅此处接受的答案:

Changing LD_LIBRARY_PATH at runtime for ctypes

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

我已经成功实现了类似于CollabNet Subversion Edge的东西,这取决于所有操作系统中的SIGAR libraries(我们支持32位和64位的Windows / Linux / Sparc)......

Subversion Edge是一个Web应用程序,可帮助通过Web控制台管理Subversion存储库并使用SIGAR到SIGAR库帮助我们直接从OS提供用户数据...您需要更新属性“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 }

控制台的Bootstrap服务(Grails应用程序上的Groovy)类运行服务并使用库目录的完整路径执行它...基于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等)提供匹配的库版本。