rss

The Java Native Interface (JNI)

Monday, August 31, 2009

The Java Native Interface (JNI) enables the integration of code written in the Java programming language with code written in other languages such as C, C++ and assembly. Therefore, you can natively run heavy tasks in pursuance of a better performance.

Is it platform-independent?

Your application can be platform-independent if you store the compiled JNI libraries (for all possible architectures) in the jar alongside the class files. However, since System.load() can't cope with loading libraries from within a jar, you'll therefore need a custom loader which extracts the library to a temporary file at runtime.

So, what do I need ?

  • Microsoft Windows SDK (recomended if you are using Windows)
  • Microsoft Visual Studio (same here)
  • Java SDK

And, which are the magic steps?

1. Declare your native methods in a normal Java class.
public class JavaJNI {

static {
System.loadLibrary("NativeLib");
}

public static native String write(String s);

public static void main(String[] argv) {
System.out.print(JavaJNI.write("Hello"));
}

}


2. Compile the Java file normally.
javac JavaJNI.java

3. Generate the .h file.
javah -jni JavaJNI

4. Write the native code using the file generated above.

In this case you will find the following method inside:
JNIEXPORT jstring JNICALL 
Java_JavaJNI_write(JNIEnv *, jclass, jstring);
Now you have the green light to implement it.
#include "JavaJNI.h"

JNIEXPORT jstring JNICALL Java_JavaJNI_write(JNIEnv *env, 
jobject thisobject, jstring js){
const char *temp;
jstring result = NULL;
temp = (*env)->GetStringUTFChars(env, js, 0);

/** Your normal C code */
printf("Input: %s", temp);

result = (*env)->NewStringUTF(env, 
(const char*) "\nResult: http://www.think-techie.com");
(*env)->ReleaseStringUTFChars(env, js, temp);
return result;
}

If you are trying to use C++ use the following methods instead:
env->GetStringUTFChars(js, 0);
env->NewStringUTF((const char*) temp);
env->ReleaseStringUTFChars(js, temp);

5. Compile the code

Windows with MS C++ Compiler
cl -c /Ic:\Programs\JavaSDK\include /I"C:\Programs\Microsoft Visual Studio 9.0\VC\include" /Ic:\Programs\JavaSDK\include\win32 NativeLib.c

link -LIBPATH:"C:\Programs\Microsoft SDKs\Windows\v6.0\VC\LIB" -LIBPATH:"C:\Programs\Microsoft SDKs\Windows\v6.0\Lib" NativeLib.obj /dll
Or this one:
cl -I C:\Programs\JavaSDK\include -I C:\Programs\JavaSDK\include\win32 -LD NativeLib.c -FeNativeLib.dll
Solaris with Sun Compiler
cc -G -I JavaSDK/include -I JavaSDK/include/solaris -o libNativeLib.so NativeLib.c
Linux with GCC
gcc -fPIC -I JavaSDK/include -I JavaSDK/include/linux -shared -o libNativeLib.so NativeLib.c
If your prefer to use Ant take a look at cc Task.

6. Run the example
java JavaJNI
Application output:
Input: Hello
Result: http://www.think-techie.com
7. Did you got errors ?

There are some common errors such as:
java.lang.UnsatisfiedLinkError: no NativeLib in 
java.library.path
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.loadLibrary0(Unknown Source)
at java.lang.System.loadLibrary(Unknown Source)
at ...
The manner through which java searches for the needed files are different in each scenario:
  1. Java searches for *.class files through the "-cp"/"-classpath" command line switch. If neither are specified on the command line, CLASSPATH is searched.
  2. Java searches for JNI files through the "-Djava.library.path" command line switch. If "-Djava.library.path" is not specified, LD_LIBRARY_PATH is searched, but the /etc/ld.so.conf mechanism is not used.
  3. The way a JNI glue library searches for the underlying C++ *.so libraries is not controlled by the Java process at all. The normal operating system rules are applied (LD_LIBRARY_PATH, /etc/ld.so.conf, etc.).
Consequently, one way of fixing this is to include the current location of your newly compiled shared library. If you are using linux you can do this:
LD_LIBRARY_PATH=`pwd`
export LD_LIBRARY_PATH
Another way is to define the location when running the program:
java -Djava.library.path=. JavaJNI
8. Did you got more errors ?
java.lang.UnsatisfiedLinkError: <library location>: 
<library location>: only ET_DYN and ET_EXEC can be loaded
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1778)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1674)
at java.lang.Runtime.load0(Runtime.java:770)
at java.lang.System.load(System.java:1005)
...
If you are using g++, try to include the option "-shared" when compiling the cpp file.
g++ -shared NativeLib.cpp -o NativeLib.so -I JavaSDK/include
-I JavaSDK/include/linux
Do not include the "-c" option. It will skip the linking stage.

References

[1] JNI – Java Native Interface. White Magician. wmagician.wordpress.com

1 comments:


Mithun V said...

i have followed the tutorials exactly how it is mentioned but i am getting the core dump , i am using linux