Monday 19 May 2008

Java enum in Java Native Interface (JNI)

A few days ago I started to take interest in JNI (http://en.wikipedia.org/wiki/Java_Native_Interface). JNI is used to call java native methods that are written in other language than Java. Java native methods are usually system dependent and are used to provide functionality that has to be handled by an operating system. Example is creating and handling threads in Java. This functionality is system dependent and has to be handled directly by an operating system (let’s try to open and go through java.lang.Thread class source code in JDK). You can find very useful specification of JNI containing a few examples at http://java.sun.com/j2se/1.5.0/docs/guide/jni/. JNI provides many methods for manipulating Java primitive types, objects, exceptions, non-native methods and so long, but what is missing in the JNI guide is description how to manipulate enums in native code (implemented using e.g. C++) and that’s what I’m going to show you here (using C++).

Let’s assume we have got the following simple enum:

package test;

public enum MyEnum {
VALUE1, VALUE2, VALUE3;
}


Since Java version 1.5 enums are converted to ordinary Java classes so treat them is similar to any other Java object (I recommend you to decompile compiled MyEnum.class file). For the following native method declaration:

package test;

public class SomeClass {
public native String enumTest(MyEnum myEnum);
}


is then, using javah generator (it’s part of JDK, not just JRE), generated following C header:
JNIEXPORT jstring JNICALL Java_test_SomeClass_ enumTest(JNIEnv *, jobject, jobject);
The first parameter is pointer to Java environment, second is Java the object on which is the native method called and the third is the myEnum parameter (notice that the type is object). The way how to handle object in native code is a little bit similar to the Java reflections.

The first thing you have to do is to find the name of the method that returns actual "value" of the enum. You can find in the documentation (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Enum.html) that every enum has (among others) method name() that returns name of the value of the enum as String (the second usable method is ordinal() ). Now I’m going to show you how to call the name() method in native code implemented using C++:
JNIEXPORT jstring JNICALL Java_test_SomeClass_ enumTest(JNIEnv *env, jobject obj, jobject enumObj) {
jclass enumClass = env->FindClass("test/MyEnum");
jmethodID getNameMethod = env->GetMethodID(enumClass, "name", "()Ljava/lang/String;");
jstring value = (jstring)env->CallObjectMethod(enumObj, getNameMethod);
const char* valueNative = env->GetStringUTFChars(value, 0);
if (strcmp(valueNative, "VALUE1") == 0) {
printf("Enum has the VALUE1 value");
}
// ...
return NULL;
}
On the first line the pointer to the class is obtained using FindClass (http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/functions.html#wp16027) method. You should notice that package name is delimited by "/" and not by ".". On the second line the pointer to the correct method - name() is obtained using GetMethodID (http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/functions.html#wp16660) method. You have to provide correct method’s signature (look into the specification how to get correct method’s signature). The third line calls the method against given object. CallObjectMethod (http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/functions.html#wp4256) means that the method itself returns object (String). The fourth line converts returned string value from UTF-8 (which is used as encoding of strings in Java) to C encoding and finally the last line just compares obtained valued against one of the enum’s values.

4 comments:

Volker said...

Hi,

thank's for the great blog - exactly what I needed!

Reards,
Volker Simonis

Steven said...

hi, can you give Enum.valueOf jni method example?
Thanks!

harzemli said...

i want to use ArrayList in java code but i dont know how to use side of jni of code.ihave class that is called ObjectClass and i declared this variable ArrayList pValue.how to use and reach entities of pValue.please help me

Anonymous said...

Enum turns out to be very versatile than the standard fixed input variable. Enum can also be used in reverse lookup and can be more descriptive with custom toString implementation. here is good link of various examples of enum in java