Dynamic java.library.path HowTo




written by Marco Ferretti on August 09, 2011, at 09:00 AM

en it

Sometimes you need to use native libraries in order to accomplish some tasks ( e.g. : statistics ) that, if written in Java, take more time than they should. The "classic" way of loading a native library is to use one of the following Java call

 Runtime.getRuntime().loadLibrary(String s);
 Runtime.getRuntime().load(String s);

 that are exactly equivalent to 

 System.loadLibrary(String s);
 System.load(String s);

The main difference between the two calls is that load(String s) needs an absolute path, while loadLibrary the "core name" of the library that is then resolved by the underlying operating system into a library file. For example, if we wanted to use the library "TestJni" the call

 Runtime.getRuntime().load("TestJni");

would be resolved into looking for the library file

 Windows:
 TestJni.dll
 Unix:
 libTestJni.so

and that file would be looked up in the directories specified in the system variable java.library.path

I prefer loading a library calling the core name because doing so I can create and compile the same native library for as many O.S. as I need to run my software on and load those libraries always in the same way regardless of the platform the software is running onto. The caveats of this method, though, are that of actually finding and loading the library.

There are, in the Java language, three strategies that can be used to achieve this task :

  1. copy the native library into the default O.S. system paths
  2. setting the java.library.path variable at JVM startup
  3. copy the native library in a predefined path within the software and use the System.load call

The main problem with solution 1 is that you can't always suppose that the person installing your software will have the privileges to write in the system library directories; solution 2 consists of firing the Java Vitual Machine with the parameter

 -Djava.library.path=path_to_the_directory_where_your_library_is_sitting

doable, but ugly ( IMHO ) especially if you think that modern S.O. can run a jar file ( if it contains a main(String[] args) method by double clicking it.

Solution number 3 implies that java.library.path is modified before you try to load the native library; apart form that, it permits to install/copy all the necessary libraries and jars your application depends on in, my personal flavor, ./lib without having to care where that directory will actually be copied by the installer .

In order to modify java.library.path you need to resolve the absolute path of the "./lib" directory :

 public String getRunningPath() {
     Logger.getLogger(getClass()).debug("determining running path");
     URL url = Utils.class.getProtectionDomain().getCodeSource().getLocation();
     File file;
     try {
         file = new File(url.toURI().getPath());
     } catch (URISyntaxException e) {
         file = new File(url.getPath());
     }
     Logger.getLogger(Utils.class).debug("resolved running path : " + file.getAbsolutePath());
     if (file.isDirectory()) {
         return file.getAbsolutePath();
     } else {
         return getPath(file);
     }
 }

 public String getPath(File file) {
     Logger.getLogger(Utils.class).debug("resolving path for " + file.getName());
     if (file.isDirectory()) {
         Logger.getLogger(Utils.class).debug(file.getName() + " is a directory so the running path is "+ file.getAbsolutePath() + File.separator);
         return file.getAbsolutePath() + File.separator;
     }
     String path = file.getAbsolutePath();
     path = path.substring(0, path.lastIndexOf(file.getName()));
     Logger.getLogger(Utils.class).debug(
     file.getName() + " is a file so the running path is " + path);
     return path;
 }
 

The method getRunningPath returns the path where the Java Virtual Machine was fired from. If we kept the example nomenclature, out library directory will be

 String libraryPath = getRunningPath() + File.separator + "lib";

The method that changes the system variable java.library.path is

        public void addDir(String s) throws IOException {
                try {
                        // This enables the java.library.path to be modified at runtime
                        // From a Sun engineer at http://forums.sun.com/thread.jspa?threadID=707176
                        //
                        Field field = ClassLoader.class.getDeclaredField("usr_paths");
                        field.setAccessible(true);
                        String[] paths = (String[])field.get(null);
                        for (int i = 0; i < paths.length; i++) {
                                if (s.equals(paths[i])) {
                                        return;
                                }
                        }
                        String[] tmp = new String[paths.length+1];
                        System.arraycopy(paths,0,tmp,0,paths.length);
                        tmp[paths.length] = s;
                        field.set(null,tmp);
                        System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
                } catch (IllegalAccessException e) {
                        throw new IOException("Failed to get permissions to set library path");
                } catch (NoSuchFieldException e) {
                        throw new IOException("Failed to get field handle to set library path");
                }
        }
 

Thus the main call :

 addDir(getRunningPath() + File.separator + "lib") ;

this way the call

 System.loadLibrary(LIBRARY_CORE_NAME);

will look up your library in ./lib too

Leave a comment

Name (required)
E-mail (required, will not be published)
Website
Comment

Enter value: Captcha