Java Native Interface

Overview

A Java source file is compiled to a Java class file. This is a set of instructions for the class which use instructions from the Java Virtual Machine. The class file is interpreted by the Java runtime engine (java or jre). The interpreter is written in C, although other languages could have been used.

Not everything can be written in Java. Certain operations in classes are not portable across all operating systems. Examples are

These methods are native methods, meaning that the implementation is done in C, not in Java.

The object code for native methods are stored in dynamic link libraries (.dll's in Windows, .so's in Unix). You need to be able to match up the object code to the correct method. Diagramatically it looks like

Hello World

Step 1: Java source

public class HelloWorld {
    public native void sayHello();
}// HelloWorld
Step 2: Compile

javac HelloWorld.java
      
Step 3: Generate header

      javah -jni HelloWorld
      
The result is

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_sayHello(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
The important part is the function prototype for Java_HelloWorld_sayHello
Step 4: implement C code for Java_HelloWorld_sayHello

#include <stdio.h>
#include "HelloWorld.h"

JNIEXPORT void JNICALL Java_HelloWorld_sayHello(JNIEnv *env, jobject obj) {
    printf("Hello world\n");
}
      
Step 5: Compile C code to object module

gcc -c -I/usr/local/src/jdk1.3/include \
       -I/usr/local/src/jdk1.3/include/linux \
       -fPIC hello.c
      
Step 6: Build shared library

gcc -shared -W1 -o libhello.so hello.o
      
Step 7: Java test harness

public class TestHello {

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

    public static void main(String[] argv){
	new HelloWorld().sayHello();
    }
}// TestHello
Step 8: Set up shared library environment

      export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
      
Step 9: Run test program

      java TestHello
      
A Makefile suitable for this is

# Set to where your JDK home is
JHOME = /usr/local/src/jdk1.3

INCLUDE = -I $(JHOME)/include -I $(JHOME)/include/linux

# Set this to something appropriate for building shared libraries
CFLAGS = $(INCLUDE) -fPIC -g

OBJS = hello.o

CLASSES = \
	HelloWorld.class \
	TestHello.class

# GNU Make rule to build Java class files
%.class: %.java
	javac $<

# Override default rule for building .o files
%.o: %.c %.class
	javah -jni $*
	gcc -c $(CFLAGS) $<

all: $(CLASSES) libhello.so

libhello.so : $(OBJS)
	gcc -shared -W1 -o libhello.1.0 $(OBJS)
	ln -f libhello.1.0 libhello.so

clean:
	rm -f *~ core *.o lib* *.class

Other requirements of JNI

C code written using JNI must also

From this, Java methods such as


class ClassName {
  native String methodName(int n);
}
will generate a C prototype of

JNIEXPORT jstring JNICALL Java_ClassName_methodName(JNIEnv *env,
                       jobject this, jint n);

Read example

A very simplified form of the FileInputStream.read() method is


JNIEXPORT jint JNICALL Java_FileInputStream.read(JNIEnv *env,
                       jobject this) {
    char ret;
    int fd, nread;
    // get the file descriptor from a private field of the
    // object
   fd = ...;
   nread = read(fd, &ret, 1);
   if (nread == 0) { /* EOF */
       return -1;
   } else if (nread == -1) { /* error */
       if (errno == EINTR) {
           JNUThrowByName(env, "java/io/InterruptedIOException");
       } else {
           JNU_ThrowIOException(env, "Read error");
       }
   } else {
       return ret;
   }
}

What else JNI can do

The JNI has mechanisms to


Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: 6 Sept 2007
Copyright ©Jan Newmarch