Search code examples
javaloadlibrarysetenv

Java: load a library that depends on other libs


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.


Solution

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