Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to influence search path of System.loadLibrary() through Java code?

In a Java project, I am using a third-party library that loads some native library via

System.loadLibrary("libName");

I'd like to be able to influence the search path of this method from within my application, so that the user doesn't need to specify a correct java.library.path value on the command line (this value depends on the current OS and architecture). E.g on Windows I want to set it to "lib/native/windows", on Linux 32bit to "lib/native/linux32" etc.

I tried

System.setProperty("java.library.path", ...)

but this is ignored, apparently because the JVM reads this property only once before my code is run.

I also tried to load the native libray before using the Java library that depends on it with

System.load("fullPath/lib")

This call succeeds, but there will still be an UnsatisfiedLinkError when the native library is loaded again with System.loadLibrary().

The only way I found is the following:

  • Add interfaces that abstract the whole API of the external library.
  • Use only these interfaces in the rest of the code.
  • Add classes that implement the interfaces and delegate to the library.
  • Write an own ClassLoader, that
    • overwrites findLibary() so that the native library is found in the correct path
    • overwrites loadClass() and loads all classes of the external library and the wrapper layer by itself instead of trying to delegate to its parent like the default ClassLoader would do
  • Ensure that the interfaces are loaded with the normal ClassLoader and the wrapping classes and the external library are loaded with my own ClassLoader.

This works, but I find it very complicated and it is much effort because I need to add all those interfaces. Is there a simpler way?

like image 219
Philipp Wendler Avatar asked Feb 16 '11 07:02

Philipp Wendler


People also ask

What is Java library path in linux?

The library path environment variable tells Java™ applications that run on AIX® and Linux®, such as the JVM, where to find shared libraries. The location of shared libraries is important when they are located in a different directory from the directory that is specified in the header section of the program.

How does system loadLibrary work?

The System. load() method takes as a parameter a fully qualified path to the native library and loads the specified native library. The System. loadLibrary() takes as parameter a library name, locates a native library that corresponds to that name, and loads the native library.

What is Java library path?

java. library. path is a System property, which is used by Java programming language, mostly JVM, to search native libraries, required by a project. Similar to PATH and Classpath environment variable, java.


4 Answers

I needed to change the dll path for my unit tests. I tried the following hack and it worked:

System.setProperty( "java.library.path", "/path/to/libs" ); 
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );

For explanation, see the original link.

like image 152
Samil Avatar answered Oct 01 '22 08:10

Samil


  1. There is no approved way to change the library path for a running JVM.
  2. You cannot load a native library more than once ... and you cannot unload a native library so that you can load it again: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986

Based on your comments above (particularly, the behavior of the 3rd-party library), I'd say that your best option is to get the library path right when you launch the JVM.

Note that there is a hacky way to change the library path (see https://stackoverflow.com/a/24258955/139985) but it involves nasty reflection, and it reportedly doesn't work for all Java releases. Certainly, it relies on undocumented private implementation details of ClassLoader that could change from one release to the next.

like image 27
Stephen C Avatar answered Oct 01 '22 07:10

Stephen C


Just recently ran into this issue and using OpenJDK where a NullPointerException is thrown (as @0-0 mentioned in his comment to Samil's answer). The following works in OpenJDK and should work with Oracle JDK as well.

(Option 1) Replace the java.library.path

System.setProperty("java.library.path", newPath);
Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
field.set(ClassLoader.getSystemClassLoader(), new String[]{newPath});

(Option 2) Add to existing java.library.path

String libPath = System.getProperty("java.library.path");
String newPath;

if (libPath == null || libPath.isEmpty()) {
    newPath = path;
} else {
    newPath = path + File.pathSeparator + libPath;
}

System.setProperty("java.library.path", newPath);

Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
    
// Create override for sys_paths
ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 
List<String> newSysPaths = new ArrayList<>();
newSysPaths.add(path);  
newSysPaths.addAll(Arrays.asList((String[])field.get(classLoader)));
           
field.set(classLoader, newSysPaths.toArray(new String[newSysPaths.size()]));
like image 26
Donny Avatar answered Oct 01 '22 07:10

Donny


I tried to following to load a native Growl library for a Java application on my Mac where the lib is in the root of the classpath of my application:

System.load(GrowlUtils.class.getResource("/libgrowl.jnilib").getFile().toString());
like image 38
fimez Avatar answered Oct 01 '22 08:10

fimez