18

Is it possible to add a new path for native libraries at runtime ?. (Instead of starting Java with the property java.library.path), so a call to System.loadLibrary(nativeLibraryName) will include that path when trying to find nativeLibraryName. Is that possible or these paths are frozen once the JVM has started ?

Sergio
  • 8,532
  • 11
  • 52
  • 94

2 Answers2

30

[This solution don't work with Java 10+]

It seems impossible without little hacking (i.e. accessing private fields of the ClassLoader class)

This blog provide 2 ways of doing it.

For the record, here is the short version.

Option 1: fully replace java.library.path with the new value)

public static void setLibraryPath(String path) throws Exception {
    System.setProperty("java.library.path", path);

    //set sys_paths to null so that java.library.path will be reevalueted next time it is needed
    final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
    sysPathsField.setAccessible(true);
    sysPathsField.set(null, null);
}

Option 2: add a new path to the current java.library.path

/**
* Adds the specified path to the java library path
*
* @param pathToAdd the path to add
* @throws Exception
*/
public static void addLibraryPath(String pathToAdd) throws Exception{
    final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
    usrPathsField.setAccessible(true);

    //get array of paths
    final String[] paths = (String[])usrPathsField.get(null);

    //check if the path to add is already present
    for(String path : paths) {
        if(path.equals(pathToAdd)) {
            return;
        }
    }

    //add the new path
    final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
    newPaths[newPaths.length-1] = pathToAdd;
    usrPathsField.set(null, newPaths);
}
ben75
  • 29,217
  • 10
  • 88
  • 134
  • 1
    indeed quite a hack :) thanks for posting it. It should not be that hacky, probably it is difficult to change that path for security reasons, not sure. I will avoid to do this but it is nice to know it exists. – Sergio Mar 14 '13 at 12:40
  • 1
    The Field class mentioned is: `import java.lang.reflect.Field;` – Troyseph Oct 20 '14 at 12:09
  • 3
    Java9 logs an "illegal reflective access operation". – Stefan Dec 12 '17 at 09:46
  • 1
    Is there a better way to do this in Java-9 or Java-10? This type of reflective access results in a warning or exception in the former and is not permitted in the latter. – drwatsoncode Aug 04 '18 at 04:33
9

I used this in Java 12/13 which should work for any JVM with MethodHandles:

Lookup cl = MethodHandles.privateLookupIn(ClassLoader.class, MethodHandles.lookup());
VarHandle sys_paths = cl.findStaticVarHandle(ClassLoader.class, "sys_paths", String[].class);
sys_paths.set(null);

It has the benefit of being a Java API.

It is replaces the:

    final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
    sysPathsField.setAccessible(true);
    sysPathsField.set(null, null);
  • 4
    This has also stopped working with JDK 16. I am thinking there is no more solution to this - the clean way is to use System.load. – Stefan Reich Oct 08 '21 at 17:50
  • The best way is to stick to older versions of java and do the hack... it is quite clear there are no good things in latest java except more restrictions that choke developers – D. Sikilai Feb 06 '22 at 07:29