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:
1 2 3 4 5 6 7 | 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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | package cz.adamh.utils; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Simple library class for working with JNI (Java Native Interface) * * @see http://frommyplayground.com/how-to-load-native-jni-library-from-jar * * @author Adam Heirnich <adam@adamh.cz>, http://www.adamh.cz */ public class NativeUtils { /** * Private constructor - this class will never be instanced */ private NativeUtils() { } /** * Loads library from current JAR archive * * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting. * Method uses String as filename because the pathname is "abstract", not system-dependent. * * @param filename The filename inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext * @throws IOException If temporary file creation or read/write operation fails * @throws IllegalArgumentException If source file (param path) does not exist * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of {@see File#createTempFile(java.lang.String, java.lang.String)}). */ public static void loadLibraryFromJar(String path) throws IOException { if (!path.startsWith("/")) { throw new IllegalArgumentException("The path to be absolute (start with '/')."); } // Obtain filename from path String[] parts = path.split("/"); String filename = (parts.length > 1) ? parts[parts.length - 1] : null; // Split filename to prexif and suffix (extension) String prefix = ""; String suffix = null; if (filename != null) { parts = filename.split("\\.", 2); prefix = parts[0]; suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; // Thanks, davs! :-) } // Check if the filename is okay if (filename == null || prefix.length() < 3) { throw new IllegalArgumentException("The filename has to be at least 3 characters long."); } // Prepare temporary file File temp = File.createTempFile(prefix, suffix); temp.deleteOnExit(); if (!temp.exists()) { throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist."); } // Prepare buffer for data copying byte[] buffer = new byte[1024]; int readBytes; // Open and check input stream InputStream is = NativeUtils.class.getResourceAsStream(path); if (is == null) { throw new FileNotFoundException("File " + path + " was not found inside JAR."); } // Open output stream and copy data between source file in JAR and the temporary file OutputStream os = new FileOutputStream(temp); try { while ((readBytes = is.read(buffer)) != -1) { os.write(buffer, 0, readBytes); } } finally { // If read/write fails, close streams safely before throwing an exception os.close(); is.close(); } // Finally, load the library System.load(temp.getAbsolutePath()); } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 Widnows.

![By Sandbox.png: DaniDF1995 derivative work: Gregor Shapiro (Sandbox.png) [Public domain], via Wikimedia Commons](/wp-content/uploads/sandbox_wiki2.png)
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
This approach works fine for my situation when the shared library has no other library dependencies. However my JNI native library is linked to another shared library and although this approach finds the JNI library it in turn generates an exception. In this case I am wrapping the Java multicast DNS library libjdns_sd.so that in turn depends on libdns_sd.so. I’m not sure what the best approach is to deal with this.
:Exception in thread “main” java.lang.UnsatisfiedLinkError: /tmp/libjdns_sd.so: libdns_sd.so: cannot open shared object file: No such file or directory
Hello, this is interesting. Look at nice explanation at http://kalblogs.blogspot.cz/2009/01/java.html, 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/2979861…
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!