Java Native Interface (JNI)



General Information

Java Native Interface

javah
PlatformTool Documentationlocal copy
Solaris, Linux javah javah
Windows javah javah

JNI
  1. Create Java source code with native methods
          native return type method (arguments);
          
  2. Compile Java source code and obtain the class files
  3. Generate C/C++ headers for the native methods; javah gets the info it needs from the class files
  4. Write the C/C++ source code for the native method using the function prototype from the generated include file and the typedefs from include/jni.h
  5. Compile the C/C++ with the right header files
  6. Use the linker to create a dynamic library file
  7. Execute a Java program that loads the dynamic library
          static {
             System.loadLibrary("dynamic libary");
          }
          
actionGNUWindows
compilegcccl
linklnlink
actionSolarisWindows
compile-G/LD
link-G/dll
library filelibname.soname.dll
Mac OS X: library file: libname.jnilib loadLibrary: name

Examples

HelloWorld

  1. Create Java source program, say, HelloWorld.java with native methods. Require dynamic library to be loaded.
  2. Compile Java source code and obtain the class file.
          javac HelloWorld.java
          
  3. Generate C/C++ headers for the native methods
          javah -jni HelloWorld
          
    HelloWorld.h include include/jni.h in the Java system install directory. jni.h contains C/C++ function prototypes and includes system dependent file jni_md.h.
  4. Write the C/C++ source code, say, HelloWorld.c for the native method using the function prototype from the generated include file and the typedefs from jni.h
  5. Compile the C/C++ with the right header files generating *.o file.
          gcc -I/software/java/jdk1.2.2/include -I/software/java/jdk1.2.2/include/solaris -c HelloWorld.c -o HelloWorld.o
          
  6. Use the linker to create a dynamic library file
          ld -G HelloWorld.o -o libHelloWorld.so
          
  7. Execute a Java program that loads the dynamic library
          java -Djava.library.path=. HelloWorld
          Hello World!
          
    (Could do setenv LD_LIBRARY_PATH . on Unix instead.) The current directory "." appears to be on the default path used by Java to load libraries.

C/C++

C

JNIEXPORT void JNICALL
Java_ ... (JNIENV *env, ...) {
   (*env)->GetFieldID (env, ...);
}

C++

JNIEXPORT void JNICALL
Java_ ... (JNIENV *env, ...) {
   env->GetFieldID (...);
}

Setting Fields

> java -Djava.library.path=. Access
A private variable = 0
A private variable = 97
A static private variable = 0
A static private variable = 101

Getting Fields

jclass c = (*env)->GetObjectClass(env, this);
jfieldID fid = (*env)->GetFieldID(env, c, "private_variable", "I");
(*env)->SetIntField (env, this, fid, val);
jclass c = (*env)->GetObjectClass(env, this);
jfieldID fid = (*env)->GetFieldID(env, c, "private_variable", "I");
jint val = (*env)->GetIntField(env, this, fid);

Accessing Other Classes

jclass c = (*env)->GetObjectClass(env, this);
JNIEXPORT void JNICALL
Java_AccessOther_setPrivate (JNIENV *env, jobject this, jobject other, jint val) {
   jclass c = (*env)->GetObjectClass(env, other);
}

Types

Java Type JNI Type machine dependent
C/C++ typedef
SignatureCall...Method
Get...Field
booleanjbooleanunsigned char ZBoolean
byte jbyte signed char BByte
char jchar unsigned shortCChar
short jshort short SShort
int jint int IInt
long jlong long JLong
float jfloat float FFloat
double jdouble double DDouble
void void VVoid
nonprimitive jobject *...L...;Object
Examples
method definitionsignature
int m1 ()()I
double m2 (long l, char c)(JC)D
void m3 (String s, int[] a)(Ljava/lang/String;[I)V
String m4 (boolean b)(Z)Ljava/lang/String;
Object m4 (BigDecimal b)(Ljava/math/BigDecimal;)Ljava/lang/Object;

Invoking Java Methods from within JNI

Forming the method descriptor
private String getLine (String)            (Ljava/lang/String;)Ljava/lang/String;
public static void main (String [] args)   ([Ljava/lang/String;)V
getMethodID (JNIEnv, jclass, char* name, char* descriptor)

There are three forms of calling a Java method:

  1. Instance methods
  2. Class methods
  3. Instance methods defined in a superclass that have been overridden
JNI type CalltypeMethod (jobject, jmethodID, arguments);
JNI type CallStatictypeMethod (jobject, jmethodID, arguments);
JNI type CallNonvirtualtypeMethod (jobject, jmethodID, arguments);

Constructors are named <inti>.

There are three forms for passing the unknown number of arguments:

  1. JNI subprocedures with different number of arguments
  2. an argument of type jvalue* (an array)
  3. an argument of type va_list
JNItypeCalltypeMethod  (jobject obj, jmethodID mid,  ...);
JNItypeCalltypeMethodA (jobject obj, jmethodID mid, jvalue* args);
JNItypeCalltypeMethodV (jobject obj, jmethodID mid, va_list args);

Arrays

NewObjectArray
GetArrayLength
SetObjectElement

Strings

A Java string is stored internally using UTF-8. This cannot be used directly in C/C++.
JNIEXPORT jstring JNICALL
Java_Strings_printString (JNIEnv *env, jclass this, jstring str) {
    const char* utf_string;
    jboolean    is_copy;
    utf_string = env->GetStringUTFChars (str, &isCopy);
    printf ("%s\n", utf_string);
    if (isCopy == JNI_TRUE) {
       env->ReleaseStringUTFChars (str, utf_string);
    }
}
How to create a Java string in native code:
JNIEXPORT jstring JNICALL
Java_NewString_newStringUTF (JNIEnv *env, jclass this) {
    const char* msg = "String constructed using NewStringUTF";
    return env->NewStringUTF(msg);
}

Exceptions

jclass cls = (*env)->FindClass (env, "java/lang/IllegalArgumentException");
if (cls==NULL) return;  // Give up
(*env)->ThrowNew (env, cls, "message");
return;
(*env)->CalltypeMethod (env, obj, method_id)
jthrowable exc = (*env)->ExceptionOccurred(env)
if (exc) {
   (*env)->ExceptionClear(env)
}

Call JVM

int main (int argc, char *argv[]) {

   JNIEnv *env;
   JavaVM *jvm;
   int i;
   JDK1_1InitArgs vm_args;	// env variables to control invocation

   jint ret;
   jmethodID mid;

   vm_args.version = 0x00010001;
   JNI_GetDefaultJavaVMInitArgs(&vm_args);
  
   int len = strlen(MY_CLASSPATH) + strlen(vm_args.classpath) + 1;
   char * newcp = (char*) malloc(len * sizeof(char));
   strcpy(newcp, MY_CLASSPATH);
   strcat(newcp, vm_args.classpath);

   vm_args.classpath = newcp;
   vm_args.properties = properties;

   vm_args.vfprintf = y_fprintf;
   vm_args.exit = y_exit;
   vm_args.abort = y_abort;

   ret = JNI_CreateJavaVM(&jvm,&env,&vm_args);

   if (ret < 0) {
       fprintf(stderr, "Can't create Java VM.  Error: %ld\n", ret);
       return(1);
   }

   if (argc < 2) {
       printf("No class specified.  Exiting...\n\n");
       return(-1);
   }

   jstring jstr = env->NewStringUTF("");

   jobjectArray str_array = 
	env->NewObjectArray(argc-2, env->FindClass("java/lang/String"), jstr);


   // Pass main arguments to Java
   for( i = 2; i < argc; i++ ) {
   	jstr = env->NewStringUTF (argv[i]);
     	if (jstr == 0) {
     		fprintf(stderr, "Out of memory\n");
     		return(1);
	}
    	env->SetObjectArrayElement( str_array, i - 2, jstr);
   }

   jclass clazz = env->FindClass(dotsToSlashes(argv[1]));

   if (clazz == 0) {
      fprintf(stderr, "Can't locate the %s class. Exiting...\n", argv[1]);
      return(1);
   }

   mid = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
   if (mid == 0) {
       fprintf(stderr, "Can't locate the main method. Exiting...\n");
       return(1);
   }

   env->CallStaticVoidMethod (clazz, mid, str_array);

   jvm->DestroyJavaVM();
   return(0);
}

Ryan Stansifer <ryan@cs.fit.edu>
Last modified: Thu Mar 10 14:53:29 EST 2005