Thursday, November 26, 2009

How to pack native libraries (JNI) into a jar.

Usually the native libraries are passed to the VM with the property java.library.path. This is not pratical when we want to provide a single jar, and we cannot pass an URL to System#load(String).
Therefore we adopt the strategy to copy the native library contained in the jar (or simply classpath) in the system temporary folder and than we
pass this filename to System#load(String).

As for the standard JDK method, this one should be invoked at classloading:

static {
  try {
   loadLibrary("my.package", "HelloWorld");
  } catch (IOException e) {
   // handle this exception
  }
 }

This is our implementation:
/**
 * Load native libraries packed in a jar.
 *
 * @param pkg
 *            The package where the shared library (for example .so or .ddl)
 *            is.
 * @param libname
 *            The system indipendent name (for example HelloWorld for
 *            libHelloWorld.so under linux or HelloWorld.dll under windows).
 * @throws IOException
 */
static public void loadLibrary(String pkg, String libname) throws IOException {
 String syslibname = System.mapLibraryName(libname);
  String tmpdir = System.getProperty("java.io.tmpdir");
 File file = new File(tmpdir + File.separator + syslibname);
 if (!file.exists()) {
  String resourcename = "/" + pkg.replace('.', '/') + "/" + syslibname;
  writeToFile(GtkVersion.class.getResourceAsStream(resourcename), file);
 }
  System.load(file.getAbsolutePath());
}
 static private void writeToFile(InputStream stream, File file) throws IOException {
 OutputStream os = null;
 try {
  os = new FileOutputStream(file);
  byte buffer[] = new byte[4096];
  int len = 0;
  while ((len = stream.read(buffer)) > 0) {
   os.write(buffer, 0, len);
  }
 } finally {
  if (os != null) {
   os.close();
  }
  stream.close();
 }
}

No comments: