Pages

Tuesday, 3 January 2012

Integrate C code within Java code using Java Native Interface in Ubuntu

Java and C are two of the most widely used high level programming languages today. As a result of which there is often a need to incorporate the two in a single application. Like say for example, we have some legacy code written in C or C++ and we want to use certain features of the powerful Java platform within our application without rewriting the legacy code in Java.

This is where JNI or Java Native Interface comes riding in from the darkness on a fine steed and helps save the day.

This post serves to illustrate how to write a simple HelloWorld application using JNI wherein a certain function written in C will be invoked from a Java driver class.

And oh, this is for the Linux user. So if you are on Windows you should be better off Googling the same issue.

Prerequisites:

Needless to say, your system should have Java installed. I do not wish to digress and explain installing Java on Linux, there are a host of tutorials that will help you with the same.

So first up, we write a tiny Java code, and then analyse it's anatomy to see how it's different from the usual Java code.


import java.io.*;


class HelloWorld {
private native void nprint();
public static void main(String[] args) {
new HelloWorld().nprint();
}


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

It's a simple HelloWorld class with a few striking features.

  1. private native void nprint(); This statement is crucial here. What it does is, it declares a native method, nprint(). (hence the n in the beginning) What is a native method? A native method is one that is implemented in some other native language (in this case, it will be, in C)
  2. public static void main(String[] args)  - the usual driver method. It invokes the aforementioned method nprint()
  3. System.loadLibrary("HelloWorld") locates a native library corresponding to the name "HelloWorld" and loads the native library into the application. It shall be clearer as we progress.
Now we compile this class. So head over to your terminal and type

javac HelloWorld

Now we need to create a native header file that we can include in our C program. Doing that is simple. Another line in the terminal:

javah jni HelloWorld

Now if you do an ls -al, you shall see the original HelloWorld.java file that you've written, the HelloWorld.class file that was produced after compilation and HelloWorld.h - the header file that just got created.

If you check the contents of the header file, you'll see this line

JNIEXPORT void JNICALL Java_HelloWorld_nprint
  (JNIEnv *, jobject);

Java_HelloWorld_nprint(JNIEnv *, jobject) : This is the C method that we implement. All the fanciful names in the function name - forget them for the time being, and proceed with the task at hand.

Also, this is important. The header file is system generated. So DO NOT EDIT it. Right, with that caution in mind, we move to the remaining part.

Now we write the native code in C.

#include jni.h
#include stdio.h
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_nprint(JNIEnv *env, jobject obj) {
printf("Hello JNI World!\n");
return;
}

Do not blindly copy this code! Thanks to Blogger's adeptness at parsing everything within angular brackets as html and not displaying the same I have been unable to include the header files in their proper syntax. Simply enclose jni.h and stdio.h within angular brackers and we're done.

Also notice that the function that we've written is the same function whose prototype we saw in the header file. This function isn't difficult to comprehend - save for the high sounding names, but let us leave that there.

Now we need to compile the C code and create a native library . We shall use our favourite compiler and all it takes is a single line in the terminal.

gcc -I/usr/lib/jvm/java-6-openjdk/include  -o libHelloWorld.so -shared HelloWorld.c

This command compiles HelloWorld.c and also creates a native library called libHelloWorld. You will find the newly created library file libHelloWorld.so within your current working folder.

Now we are all set to run our Java program. There is however one thing we need to take care of. We need to define the native library directory path. To do that we run the following two commands.

LD_LIBRARY_PATH=.

(This is because the native shared library is contained within the current folder ( . ) )
and then

export LD_LIBRARY_PATH

That is it. Now we are all set to run our first JNI program.

 java -Djava.library.path=. HelloWorld

If all goes well, you'll see the output.

Hello JNI World!

There you go. Your first working JNI program!

To sum up, here's a screenshot from my terminal, that shows the sequence of shell commands (click for bigger picture).




Post a Comment