How to Load Native JNI Library from JAR
The JNI (Java Native Interface) is a framework that provides a bridge between Java and native applications. Java applications can define native methods which are implemented in dynamic library written in other languages such as C or C++. The dynamic library is able to call both static and dynamic methods. More information about JNI can be found on Wiki or in tutorial Beginning JNI with NetBeans (for Linux).
The problem is that for loading such a dynamic library you have to call method System.load(String filename) which requires an absolute filename. This approach is just fine if you have dynamic library outside the application’s JAR archive, but when bundling dynamic library within the JAR it is necessary to extract the library into filesystem before loading it. And that’s exactly what my code does.
Our simple JNI class could look like this:
public class HelloJNI {
static {
System.load("/path/to/my/library.so");
}
public native void hello();
}
To extract the library before loading it it’s necessary to add some code into the static section. I wrapped it into a static method inside simple class called NativeUtils. I decided to put it into separate class in order to have space for adding more features (like choosing the right dynamic library for host OS and architecture). The class can be found on my Github.
The code is commented and self-explaining, so I don’t have to write too much about it. Just three notes:
- The file path is passed as string, not as instance of File. It is because File transforms the abstract path to system-specific (absolute path decision, directory delimiters) one, which could cause problems. It must be an absolute path (starting with '/') and the filename has to be at least three characters long (due to restrictions of File.createTempFile(String prefix, String suffix).
- The temporary file is stored into temp directory specified by java.io.tmpdir (by default it's the operating system's temporary directory). It should be automatically deleted when the application exits.
- Although the code has some try-finally section (to be sure that streams are closed properly in case an exception is thrown), it does not catch exceptions. The exception has to be handled by the application. I belive this approach is cleaner and has some benefits.
Final usage is pretty simple. :-) Just call method loadLibraryFromJar and handle exception somehow:
import cz.adamh.NativeUtils;
public class HelloJNI {
static {
try {
NativeUtils.loadLibraryFromJar("/resources/libHelloJNI.so");
} catch (IOException e) {
e.printStackTrace(); // This is probably not the best way to handle exception :-)
}
}
public native void hello();
}
Edited 2013-04-02: Lofi came with a workaround to release and delete our DLL from temporary directory on Windows.
Get the whole code from my Github!
Comments
The comments have been archived from Disqus.
The article is great.
One little remark ..
Lines 60-61:
// Prepare temporary file
File temp = File.createTempFile(prefix, suffix);
in your case suffix contain only extension (without '.' before) .. And I was getting the error of FileNotFound in line 91.
Changing Line 61 to
File temp = File.createTempFile(prefix, "." + suffix);
fixed my issue .. maybe not only mine :)
Cheers,
Davs
Thank you, davs. Sorry for my fault, the temporary file was really "libraryso" instead of "library.so". But I didn't have any problems caused by the FileNotFound with my code, maybe it was some OS-specific issue (I run application using this code on Linux).
I modified line 52 from
suffix = (parts.length > 1) ? parts[parts.length - 1] : null;tosuffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null;, because the File.createTempFile(prefix, suffix) method uses ".tmp" as default extension if suffix is null.I also changed split() on line 50, it is now limited to 2 items.
Eveything should be OK now :-)
Hello, this is interesting. Look at nice explanation at http://kalblogs.blogspot.cz..., maybe the
$LD_LIBRARY_PATHvariable could be the way to go.Some people wanted to do the same thing (embed JNI library that depends on some shared libs into JAR), after some searching I found only this crazy solution: http://stackoverflow.com/a/......
Hey I am trying this with a folder tructure
/demo
--/src
----- App.java
----- /com.core
----- NativeUtils.java
--/Libs
----- libs.jar
with the App.java looking like this:
import java.io.IOException;
import com.core.NativeUtils;
public class App {
public static void main(String[] args) {
try {
NativeUtils.loadLibraryFromJar("/lib/lib_fifi.so");
System.out.println("Library Loaded - Loaded from jar");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
But it does not work any ideas I get: File /lib/lib_fifi.so was not found inside JAR
Hello, it looks like you didn't include the file (I don't see any /lib/lib_fifi.so in your folder structure).
Thanks, useful trick
A great solution, very simple, much simpler than all the competing solutions posted on the net.
There is one thing in this article that I do not understand: the issue of "absolute path (starting with ‘/’)".
This refers to the
path
parameter in the code, which indicates the native library as a resource inside the jar.
This far, I have worked only with audio files as resources, and their paths should not start with a slash / .
In fact, I have tried to specify audio files by a path starting with a slash, as /com/...; here com was the name of the top-level directory in the jar, and the rest of the path pointed to the file-resource. This did not work out (java could not find the resource), neither on Windows XP nor on Linux Ubuntu 12.04, both running a most recent java from Oracle.
Removing the initial slash resolves the problem: java finds the audio file.
I am going to try both ways with the native library; but what is the rule here?
I have found the answer:
loadLibraryFromJar calls java.lang.Class.getResourceAsStream(String) method while for audio files I use a different method, javal.lang.ClassLoader.getResource(String).
These two methods have different rules for path name resolution, and for this reason in getResource(..) I must use the path without the leading / while in getResourceAsStream(..) I must use the leading /.
Thanks for the nice code, it works!
Thank you very much for sharing this! However, there's a slight hickup in the solution: The temporary library won't get deleted on windows because the JVM still has a lock on it when it shuts down and invokes deleteOnExit(). The library should be "unloaded". Unfortunately that's currently not possible in Java.
In case anyone else runs into this issue of having their temporary folder flooded with DLLs, I came up with this workaround:
* in additon to the temporary DLL, also create a .lock file on which you also issue a deleteOnExit(). This file will get deleted when the JVM shuts down
* during startup search for all .dll files and delete those which don't have an accompanying ".lock" file
Not quite the proper solution, but unless this is fixed internally in Java it may be some workaround :-)
Here's some code in case someone wants to use it, just put it at the bottom of the loadLibraryFromJar method:
Lofi, thank you for this workaround, it will be useful!
This workaround is very clean, and worked for me right out of the box. Thank you sir.
Thanks so much, this saved the day!
I'm trying use this class in a Maven project.
-src/main/java
----app.java
..src/main/resources
----libraries
--------win32-x86-64
------------jni.dll
I'm calling the "loadLibraryFromJar()" method and I try this:
NativeUtils.loadLibraryFromJar("/resources/libraries/win32-x86-64/jni.dll");
NativeUtils.loadLibraryFromJar("/libraries/win32-x86-64/jni.dll");
The exception is as follows:
java.io.FileNotFoundException: File /resources/libraries/win32-x86-64/jni.dll was not found inside JAR.
I am generating a jar with the libraries inside the maven jar-project so that I only have one jar application. the libraries go inside the resources directory of the maven structure.
Any idea what is happening?
In my project there are the dll inside the jar and in my eclipse project.