Java Native Interface

The reason I left Java programming was due to the type of work I got in that field. However, I love the language. It provides the programmer with the right set of tools to focus on getting the task done. Memory management, while important, is not the primary focus of each and every programming task.

Don Knuth’s quote about premature optimization being the root of all evil often comes to mind. C is a great language for low level tasks, for embedding code on a really small platform, and for ensuring that you can understand the end binaries format and structure. One nice thing about Java is that it, too has a rigidly defined and easy to understand binary format. So, one approach to problem programming might be to code in Java to solve the problem, then optimize in C.

The language binding between Java and is the Java Native Interface (JNI). JNI makes use of the Java ‘native’ keyword. a Native method is one implemented external to the current Java class.

Here is a trivial example:

public class Sample

{

   private native String reverse(String prompt);   static {

      System.loadLibrary("reverse");

   }

public static void main(String[] args)

      {

         Sample jn=new Sample();

System.out.println("Got "+ jn.reverse("Some Data"));

      }

}

The ‘native’ line defines the method reverse. To resolve this, the jvm will look for a symbol: Java_Sample_reverse. From this we can infer that
parameter polymorphism is either not supported, or the jvm only resorts to name mangling when required to deal ambiguous situations.

the nexts steps are 1) run javac to convert the .java file to a .class file. and 2)run javah on the .class file to generate a .h file for the c library.

The function definition looks like this:

JNIEXPORT jstring JNICALL Java_Sample_reverse
(JNIEnv *, jobject, jstring);

This is within an extern “C” block for C++code, so we don’t get C++ semantics for the function implementation. This means you won’t be able to make a single set of objects to be shared between C+++ and Java, but this would be unwieldy no matter what.

For gcc The JNIEXPORT is resolved away to nothing. This compiler directive is to support Microsoft’s compiler which forces you to declare if a function is going to be visible outside of the scope of the current file, pretty much the opposite of ‘static’. JNICALL also resolves to nothing, but I suspect on a windows platform it would introduce code that specifies the pascal function calling convention for handling parameters. JNIEnv is a pointer to a pointer, and thus must be used like this :
(*env)->doSomething();
C++ Provides operator overloading of the -> so you can refer using this semantic instead:
env->doSomething();

Note that the string gets passed as a jstring. jni.h shows this to be a jobject. jobject is a struct with nothing inside it. Thus, once we go from java to C++, we are not supposed to know what exists on the other side except from reading the specification.

For this example, I have a function that reverses the string passed in. Here it is:

#include 

#include 

#include 

#include /*

 * Class:     MyJni

 * Method:    getLine

 * Signature: (Ljava/lang/String;)Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_Sample_reverse

  (JNIEnv * env, jobject obj, jstring str)

{

   jstring retval;

   char * msg;

   int i;

   int len;

const char * orig = (*env)->GetStringUTFChars(env, str,0);

len = strlen(orig);

msg = (char *)malloc(len+1);

/*disregard the null termination fro now; it will not be copied.*/

   len--;

for (i = 0; i <= len; ++i){

      msg[i] = orig[len-i];

   }

   msg[i]=0;

(*env)->ReleaseStringUTFChars(env, str, orig);

retval = (*env)->NewStringUTF(env, msg);

free(msg);

return retval;

}

Here is the Makefile I used to build it.

CFLAGS+=-I/usr/lib/jvm/java-6-sun-1.6.0.00/include -I/usr/lib/jvm/java-6-sun-1.6.0.00/include/linux
FILES=MyJni.h

all: libreverse.so Sample.class

.SUFFIXES : .c  .class .h .so .java

.class.h :
        javah $*

clean :
        rm -rf *.o *.so $(FILES) *~ *.class Sample.h

.java.class :
        javac $<

libreverse.so : Sample.h reverse.c
        gcc reverse.c -o $*.so -shared $(CFLAGS) -fPIC

test :
        LD_LIBRARY_PATH=. java Sample

$: make && make test
javac Sample.java
javah Sample
gcc reverse.c -o libreverse.so -shared -I/usr/lib/jvm/java-6-sun-1.6.0.00/include -I/usr/lib/jvm/java-6-sun-1.6.0.00/include/linux -fPIC
LD_LIBRARY_PATH=. java Sample
Got ataD emoS

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.