Java Web Start - loading native dependency with another native dependency

I use Java Web Start to run a Java application, which depends on some third-party libraries. Then these native libraries load another native library ( commonLib ) as their dependency using LoadLibrary / dlopen.

If you are not using Web Start, everything works as expected when your own libraries are in the same directory.

However, in Web Start, you need your own libraries to be packaged in a jar file and listed in the jnlp file I made:

  <!-- Windows OS --> <resources os="Windows"> <nativelib href="native/native-windows.jar" /> </resource> <!-- Linux OS --> <resources os="Linux"> <nativelib href="native/native-linux.jar" /> </resources> <!-- Mac OSX --> <resources os="Mac OS X"> <nativelib href="native/native-osx.jar"/> </resources> 

Native libraries load normally, but they cannot load their commonLib dependency - a C ++ call to LoadLibrary / dlopen fails because the file is present in some jar / cache folder not in the current library search path.

On Windows, I was able to solve this problem by preloading commonLib in Java before trying to load the JNI library, for example:

 System.loadLibrary("commonLib"); System.loadLibrary("myNativeLib"); 

However, this approach does not work on OS X - dlopen does not work in native code. dlopen is apparently not smart enough not to try loading the library again if it is already loaded.

Is there a cross-platform way to batch load and load my own libraries that depend on other native libraries in Java Web Start?

+7
java java-web-start jni
source share
1 answer

I was able to find a (ugly) workaround. The trick is to pack the dependent libraries ( commonLib ) into a simple resource container and add it to the jnlp file:

 ... <resources os="Windows"> <jar href="native/deps-windows.jar" /> </resources> <resources os="Linux"> <jar href="native/deps-linux.jar" /> </resources> <resources os="Mac OS X"> <jar href="native/deps-osx.jar" /> </resources> ... 

Step two is to extract these resources using Java into a temporary directory:

 String tmpDir = System.getProperty("java.io.tmpdir"); if (!tmpDir.endsWith("/") && !tmpDir.endsWith("\\")) tmpDir += "/"; String resource = "commonDir.dll"; // Adjust accordingly to current OS, omitted for brevity InputStream is = loader.getResourceAsStream(resource); if (is == null) { // Handle error - resource not found return; } try { FileOutputStream os = new FileOutputStream(tmpDir + resource); byte[] buffer = new byte[1024*1024]; int len = is.read(buffer); while (len != -1) { os.write(buffer, 0, len); len = is.read(buffer); } os.close(); is.close(); System.out.println("Extracted " + resource + " to " + tmpDir); } catch(IOException ex) { // Handle the exception - cannot write to temp directory System.out.println("Could not extract resource " + resource + " to " + tmpDir + ": " + ex.getMessage()); } 

Step 3 is to inform your own JNI library of the full path to the extracted dependency, or temporarily install the current directory in the temp tmpDir , download the JNI library, and install it back. This is a problem in itself - in Java it is difficult to change the current working directory. You can solve this problem by creating another small JNI library that does this with C, though [ 1 ].

+1
source share

All Articles