Java - DLLs

java

https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
https://newcircle.com/bookshelf/java_fundamentals_tutorial/_java_native_interface_jni
https://en.wikipedia.org/wiki/Java_Native_Interface
https://netbeans.org/kb/docs/cnd/beginning-jni-linux.html
http://homepage.cs.uiowa.edu/~slonnegr/wpj/JNI.pdf
http://www.ibm.com/developerworks/java/tutorials/j-jni/j-jni.html
http://web.mit.edu/javadev/doc/tutorial/native1.1/implementing/index.html
http://thebreakfastpost.com/2012/01/21/wrapping-a-c-library-with-jni-introduction/

How can we load a Windows DLL file?

private final static Log logger = LogFactory.getLog(...);
try {
    System.loadLibrary(...);
    logger.info("DLL is loaded from memory");
} catch (UnsatisfiedLinkError e) {
    // loadFromJar();
}

How can we load a Windows DLL file that is included inside a JAR file?

private static void loadLib(String path, String name) {
    name = name + ".dll";
    try {
        InputStream in = ACWrapper.class.getResourceAsStream(LIB_BIN + name);
        // always write to different location
        File fileOut = new File(System.getProperty("java.io.tmpdir") + "/" + path + LIB_BIN + name);
        logger.info("Writing dll to: " + fileOut.getAbsolutePath());
        OutputStream out = FileUtils.openOutputStream(fileOut);
        IOUtils.copy(in, out);
        in.close();
        out.close();
        System.load(fileOut.toString());
    } catch (Exception e) {
        throw new ACCoreException("Failed to load required DLL", e);
    }
}

The above code extract the content of the DLL from the JAR file, use IOUtils to copy it to a different location on disk, and then use System.load to load the DLL.

What is the difference between the System.loadLibrary method and the System.load method?

The System.loadLibrary method takes the name of the DLL, whereas the System.load method takes the absolute path of the DLL.

What are the advantages and disadvantages of using http://www.jdotsoft.com/JarClassLoader.php?

  1. It can load DLLs and JARs from another JAR with unlimited nesting. For example, DLL could be in JAR which is in another root JAR. All DLLs and JARs are loaded like they are in a classpath or library path.
  2. License is GPLv3, which substantially limits applicability
  3. This class loader unpacks all nested jars into a temporary directory. So it works like the first answer. The native libs are unpacked only if needed.
  4. Commercial license available.
  5. Because it support nested JARs, it may not be very efficient. We need to test it if efficiency is important (most likely not).

What does JNI abbreviate for?

Java Native Interface. It 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.

It enables Java code to call and be called by native code.

JNI enables programmers to write native methods to handle situations when an application cannot be written entirely in the Java programming language, e.g. when the standard Java class library does not support the platform-specific features or program library. It is also used to modify an existing application—written in another programming language—to be accessible to Java applications. Many of the standard library classes depend on JNI to provide functionality to the developer and the user, e.g. file I/O and sound capabilities. Including performance- and platform-sensitive API implementations in the standard library allows all Java applications to access this functionality in a safe and platform-independent manner.

The JNI framework lets a native method use Java objects in the same way that Java code uses these objects. A native method can create Java objects and then inspect and use these objects to perform its tasks. A native method can also inspect and use objects created by Java application code.

What are the pitfalls of using JNI?

How can we call native code from Java?

HelloWorld.java:

class HelloWorld
{
    private native void print();
    public static void main(String[] args)
    {
        new HelloWorld().print();
    }
    static {
        System.loadLibrary("HelloWorld");
    }
}

The above code declare a method named print which is a regular method in a sense that it is accessible to our normal method, but it is defined in a separate file containing native code.

build.bat:

:: Microsoft Visual Studio 2012 Visual C++ compiler
SET VC=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC
:: Microsoft Windows SDK for Windows 7 and .NET Framework 4 
SET MSDK=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A
:: Java 1.7.0 update 21
SET JAVA_HOME=C:\Program Files (x86)\Java\jdk1.7.0_21
:: Add compiler tools folder to the PATH variable.  Do not run this too many times or the PATH will exceed the maximum limit.
call "%VC%\vcvarsall.bat"

javac HelloWorld.java
javah HelloWorld
:: On Windows, the JNI library should not have a "lib" prefix
%VC%\bin\cl "/I%JAVA_HOME%\include" "/I%JAVA_HOME%\include\win32" "/I%VC%\include" "/I%VC%\lib" "/I%MSDK%\Lib" "libHelloWorld.c" "/FeHelloWorld.dll" /LD
java HelloWorld

make.sh:

#!/bin/sh

# Linux 2.6.32-358.el6.x86_64
# gcc 4.4.7
# openjdk 1.7.0

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
javac HelloWorld.java
javah HelloWorld
gcc -shared -fPIC libHelloWorld.c -o libHelloWorld.so
java HelloWorld

Note that in this example, we are using HelloWorld, and we will have to update the the above make.sh and build.bat to replace everywhere that HelloWorld is used.

The above code use javah to generate the HelloWorld.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

libHelloWorld.c

#include <stdio.h>
#include "HelloWorld.h"

JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
     printf("Hello World!\n");
     return;
}

How can we call native code from Java - part 2?

public class Hello {
    public native void sayHi(String who, int times);
    static {
        System.loadLibrary("HelloImpl"); 
    }

    public static void main (String[] args) {
        Hello hello = new Hello();
        hello.sayHi(args[0], Integer.parseInt(args[1]));
    }
}

The method sayHi will be implemented in C/C++ in separate file(s), which will be compiled into a library. The library filename will be called libHelloImpl.so (on Unix), HelloImpl.dll (on Windows) and libHelloImpl.jnilib (Mac OSX), but when loaded in Java, the library has to be loaded as HelloImpl.

Hello.h:

...
#include <jni.h>
...
JNIEXPORT void JNICALL Java_com_marakana_jniexamples_Hello_sayHi
  (JNIEnv *, jobject, jstring, jint);
...

Hello.c:

#include <stdio.h>
#include "com_marakana_jniexamples_Hello.h"

JNIEXPORT void JNICALL Java_com_marakana_jniexamples_Hello_sayHi(JNIEnv *env, jobject obj, jstring who, jint times) {
  jint i;
  jboolean iscopy;
  const char *name;
  name = (*env)->GetStringUTFChars(env, who, &iscopy);
  for (i = 0; i < times; i++) {
    printf("Hello %s\n", name);
  }
}
  1. Create a Java class with native method(s): public native void sayHi(String who, int times);
  2. Load the library which implements the method: System.loadLibrary("HelloImpl");
  3. Compile our Java code: javac -d ./classes/ ./src/com/marakana/jniexamples/Hello.java
  4. Use the javah utility to generate the header file containing a function prototype for the sayHi method. In the classes directory run: javah -jni com.marakana.jniexamples.Hello to generate the header file com_marakana_jniexamples_Hello.h
  5. Create com_marakana_jniexamples_Hello.c to implement the Java_com_marakana_jniexamples_Hello_sayHi function.
  6. Compile our native code. The compilation is system-dependent.

To compile com_marakana_jniexamples_Hello.c in the "classes" directory (if your .h file and .c file are there) on Linux do:

gcc -o libHelloImpl.so -lc -shared \
    -I/usr/local/jdk1.6.0_03/include \
    -I/usr/local/jdk1.6.0_03/include/linux com_marakana_jniexamples_Hello.c

On Mac OSX:

gcc -o libHelloImpl.jnilib -lc -shared \
    -I/System/Library/Frameworks/JavaVM.framework/Headers com_marakana_jniexamples_Hello.c
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License