Recipe 26.5 Blending in Native Code (CC)


Recipe 26.5 Blending in Native Code (C/C++)

Problem

You wish to call native C/C++ functions from Java, either for efficiency or to access hardware- or system-specific features.

Solution

Use JNI, the Java Native Interface.

Discussion

Java lets you load native or compiled code into your Java program. Why would you want to do such a thing? One reason might be to access OS-dependent functionality. Another is speed: native code will likely run faster than Java, at least at present. Like everything else in Java, this mechanism is subject to security restrictions; for example, applets are not allowed to access native code.

The native code language bindings are defined for code that has been written in C or C++. If you need to access a language other than C/C++, write a bit of C/C++ and have it pass control to other functions or applications, using any mechanism defined by your operating system.

Due to such system-dependent features as the interpretation of header files and the allocation of the processor's general-purpose registers, your native code may need to be compiled by the same C compiler used to compile the Java runtime for your platform. For example, on Solaris you can use SunPro C or maybe gcc. On Win32 platforms, use Microsoft Visual C++ Version 4.x or higher (32 bit). For Linux and Mac OS X, you should be able to use the provided gcc-based compiler. For other platforms, see your Java vendor's documentation.

Also note that the details in this section are for the Java Native Interface (JNI) of Java 1.1 and later, which differs in some details from 1.0 and from Microsoft's native interface.

The steps to call native code are summarized in the following sidebar and detailed below.

Ian's Basic Steps: Java Calling Native Code

To call native code from Java:

  1. Write Java code that calls a native method.

  2. Compile this Java code.

  3. Create an .h file using javah.

  4. Write a C function that does the work.

  5. Compile the C code into a loadable object.

  6. Try it!


The first step is to write Java code that calls a native method. To do this, use the keyword native to indicate that the method is native, and provide a static code block that loads your native method using System.loadLibrary( ) . (The dynamically loadable module is created in Step 5.) Static blocks are executed when the class containing them is loaded; loading the native code here ensures it is in memory when needed!

Object variables that your native code may modify should carry the volatile modifier. The file HelloWorld.java , shown in Example 26-10, is a good starting point.

Example 26-10. HelloWorld.java
/**  * A trivial class to show Java Native Interface 1.1 usage from Java.  */ public class HelloWorld {     int myNumber = 42;        // used to show argument passing     // declare native class     public native void displayHelloWorld( );     // Application main, call its display method     public static void main(String[] args) {         HelloWorld hw = new HelloWorld( );         hw.displayHelloWorld( );        // call the native function         System.out.println("Back in Java, \"myNumber\" now " + hw.myNumber);     }     // Static code blocks are executed once, when class file is loaded     static {         System.loadLibrary("hello");     } }

The second step is simple; just use javac HelloWorld.java as you normally would. You probably won't get any compilation errors on a simple program like this; if you do, correct them and try the compilation again.

Next, you need to create an .h file. Use javah to produce this file:

javah HelloWorld           // produces HelloWorld.h

The .h file produced is a "glue" file, not really meant for human consumption and particularly not for editing. But by inspecting the resulting .h file, you'll see that the C method's name is composed of the name Java, the package name (if any), the class name, and the method name:

JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env,      jobject this);

Then create a C function that does the work. You must use the same function signature as is used in the .h file.

This function can do whatever it wishes. Note that it is passed two arguments: a JVM environment variable and a handle for the this object. Table 26-2 shows the correspondence between Java types and the C types (JNI types) used in the C code.

Table 26-2. Java and JNI types

Java type

JNI

Java array type

JNI

byte

jbyte

byte[]

jbyteArray

short

jshort

short[]

jshortArray

int

jint

int[]

jintArray

long

jlong

long[]

jlongArray

float

jfloat

float[]

jfloatArray

double

jdouble

double[]

jdoubleArray

char

jchar

char[]

jcharArray

boolean

jboolean

boolean[]

jbooleanArray

void

jvoid

  
Object

jobject

Object[]

jobjectArray

Class

jclass

  
String

jstring

  
array

jarray

  
Throwable

jthrowable

  


Example 26-11 is a complete C native implementation. Passed an object of type HelloWorld, it increments the integer myNumber contained in the object.

Example 26-11. HelloWorld.c
#include <jni.h> #include "HelloWorld.h" #include <stdio.h> /*  * This is the 1.1 implementation of displayHelloWorld.  */ JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject this) {     jfieldID fldid;     jint n, nn;     (void)printf("Hello from a Native Method\n");     if (this == NULL) {         fprintf(stderr, "Input pointer is null!\n");         return;     }     if ((fldid = (*env)->GetFieldID(env,          (*env)->GetObjectClass(env, this), "myNumber", "I")) == NULL) {         fprintf(stderr, "GetFieldID failed");         return;     }     n = (*env)->GetIntField(env, this, fldid);    /* retrieve myNumber */     printf("\"myNumber\" value is %d\n", n);     (*env)->SetIntField(env, this, fldid, ++n);    /* increment it! */     nn = (*env)->GetIntField(env, this, fldid);     printf("\"myNumber\" value now %d\n", nn);         /* make sure */     return; }

Finally, you compile the C code into a loadable object. Naturally, the details depend on platform, compiler, etc. For example, on Windows:

> set JAVA_HOME=C:\java              # or wherever > set INCLUDE=%JAVA_HOME%\include;%JAVA_HOME%\include\Win32;%INCLUDE% > set LIB=%JAVA_HOME%\lib;%LIB% > cl HelloWorld.c -Fehello.dll -MD -LD

And on Unix:

$ export JAVAHOME=/local/java   # or wherever $ cc -I$JAVAHOME/include -I$JAVAHOME/include/solaris \ -G HelloWorld.c -o libhello.so

Example 26-12 is a makefile for Unix.

Example 26-12. Unix makefile
# Makefile for the Java Native Methods examples for # Learning Tree International Course 471/478. # Has been tested on Solaris both with "gcc" and with SunSoft "cc". # On other platforms it will certainly need some tweaking; please # let me know how much! :-) # Configuration Section CSRCS        = HelloWorld.c JAVAHOME    = /local/jdk1.1.2 INCLUDES    = -I$(JAVAHOME)/include -I$(JAVAHOME)/include/solaris  all:        testhello testjavafromc # This part of the Makefile is for C called from Java, in HelloWorld testhello:        hello.all         @echo         @echo "Here we test the Java code \"HelloWorld\" that calls C code."         @echo         LD_LIBRARY_PATH='pwd':. java HelloWorld hello.all:        HelloWorld.class libhello.so HelloWorld.class: HelloWorld.java         javac HelloWorld.java HelloWorld.h:    HelloWorld.class         javah -jni HelloWorld HelloWorld.o::    HelloWorld.h libhello.so:    $(CSRCS) HelloWorld.h     $(CC) $(INCLUDES) -G $(CSRCS) -o libhello.so # This part of the Makefile is for Java called from C, in javafromc testjavafromc:    javafromc.all hello.all     @echo     @echo "Now we test HelloWorld using javafromc instead of java"     @echo      ./javafromc HelloWorld     @echo     @echo "That was, in case you didn't notice, C->Java->C. And,"     @echo "incidentally, a replacement for JDK program \"java\" itself!"     @echo javafromc.all:    javafromc javafromc:    javafromc.o     $(CC) -L$(LIBDIR) javafromc.o -ljava -o $@ javafromc.o:    javafromc.c     $(CC) -c $(INCLUDES) javafromc.c clean:     rm -f core *.class *.o *.so HelloWorld.h clobber: clean     rm -f javafromc

And you're done! Just run the Java interpreter on the class file containing the main program. Assuming that you've set whatever system-dependent settings are necessary (possibly including both CLASSPATH and LD_LIBRARY_PATH or its equivalent), the program should run as follows:

C> java HelloWorld Hello from a Native Method        // from C "myNumber" value is 42            // from C "myNumber" value now 43        // from C Value of myNumber now 43        // from Java

Congratulations! You've called a native method. However, you've given up portability; the Java class file now requires you to build a loadable object for each operating system and hardware platform. Multiply {Windows NT, 2000, XP, and 2003, Mac OS X, Sun Solaris, HP/UX, Linux, OpenBSD, NetBSD, FreeBSD} times {Intel, Intel-64, AMD64, SPARC, PowerPC, HP-PA} and you begin to see the portability issues. Also note that native code can be used in server code and desktop applications but is normally not permitted in web browsers.

Beware that problems with your native code can and will crash the runtime process right out from underneath the Java Virtual Machine. The JVM can do nothing to protect itself from poorly written C/C++ code. Memory must be managed by the programmer; there is no automatic garbage collection of memory obtained by the system runtime allocator. You're dealing directly with the operating system and sometimes even the hardware, so, "Be careful. Be very careful."

See Also

If you need more information on Java Native Methods, you might be interested in the comprehensive treatment found in Essential JNI: Java Native Interface by Rob Gordon (Prentice Hall).



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net