380 likes | 636 Views
Native Methods. W.H.Carlisle CSE525. Native Methods: procedures implemented in a language other than Java. Reasons To use To interface with special capabilities of a computer not supported by a Java class interface to a new device utilize a library routine of some native os
E N D
Native Methods W.H.Carlisle CSE525
Native Methods: procedures implemented in a language other than Java • Reasons To use • To interface with special capabilities of a computer not supported by a Java class • interface to a new device • utilize a library routine of some native os • Speed (be careful here) • better is JIT Compiler or java2c translator
Native Methods: procedures implemented in a language other than Java • Reasons not to use • Lose portabality • class can never be used in applet code. • no applet allowed to load a class that has a native method.
BUT - in order to have a lecture we assume some method HAS to be native
Different Virtual Machines • Java VMs may differ. • The 1.0 native method framework was specific to Sun’s VM • Java 1.1 introduced JNI (Java Native Interface) that is an API for a “VM Neutral” native method interface.
Step 1: the Java side • Write the Java code • the native methods in the class is declared “native” • the native methods in the class have no body, just an ending semicolon • a static class initializer is added to the class to load the native code library we will build next.
public class HelloWorld { public native void displayHelloWorld(); static { System.loadLibrary(“hello”); } }
public class Cls { private int a; native double f(int i, String s ); public native void close(); static { try {System.loadLibrary(“Cls”);} catch(Throable t) {System.err.println(“Error Loading Library”);} } // calling the native method public static void main(String[] args) { double d = (new Cls()).f(3,”hello”);} }
Programmer … 1. Declares the method as native 2. Insure that the native library is loaded by the JVM System.load System.loadLibrary 3. Then the call to the native method dispatches to the dynamic link library.
Type encoding To invoke native code or for native methods to make JNI API calls, Java types are sometimes encoded Java primitive types are encoded with a single character: Z boolean F float B byte D double C character V void S short I int J long [x array-of-x Lname; object
VM encoding • If the name of the java method is testjni.Cls.f() • the VM uses Java_testjni_Cls_f as the name of the function. • If the java method is overloaded the name is appended with two underscores and the argument types.
VM encoding • If the java method is overloaded the name is appended with two underscores and the argument types. • Suppose the java overloaded method is testjni.Cls.f(int,String) • The name of the function will be Java_testjni_Cls_f__ILjava_lang_String_2
Name Munging • Name Munging is required to generate a unique legal identifier in the native language. • ‘.’ or ‘/’ in names become _ • unicode becomes ‘_0xxxx’ • a ‘_’ becomes ‘_1’ • ‘;’ becomes ‘_2’ • ‘[‘becomes ‘_3’
Arguments • Native method arguments have • A JNI environment pointer • a jobject or jclass • Other arguments correspond to the real arguments boolean jboolean byte jbyte char jchar void void etc…. • and we begin to suspect that “native” means C
Arguments • if java prototype in class Cls is native double f(int, String); • the native prototype is idouble Java_testjni_Cls_f__ILjava_lang_String_2(JNIEnv*, jboject, jint, jstring); One does not have to learn how to mungle!
Step 2: compile the Java code • this produces the file HelloWorld.class which will be fed to step 3
Now we confirm that for all practical purposes native method means C code • (to be fair, I have seen discussion of native methods in Ada but have not followed up on this)
Step 3 : use javah to create a header file javah -jni HelloWorld • run on the HelloWorld.class this places a HelloWorld.h file in the directory…but a -d option can place it elsewhere. • natïve methods are mungled and incorporated into the header file.
Look at, but do not edit the .h file /* 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: displayHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Cls */ #ifndef _Included_Cls #define _Included_Cls #ifdef __cplusplus extern "C" { #endif /* * Class: Cls * Method: f * Signature: (ILjava/lang/String;)D */ JNIEXPORT jdouble JNICALL Java_Cls_f (JNIEnv *, jobject, jint, jstring); #ifdef __cplusplus } #endif #endif
The C function(s) are what you look at since this is the prototype for the function you will write JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); JNIEXPORT jdouble JNICALL Java_Cls_f(JNIEnv *, jobject, jint, jstring);
The interface pointer • This is a pointer to all the library methods available to the native method. • The table of functions is loaded by the VM for a thread • Thus between disjoint threads the pointer may not be valid.M
Using the pointer • To access a java field from within a native method use: env-> GetXXXField(obj,fld) • or env -> SetXXXField(obj,fld,value) e.g. env-> SetIntField(obj,fld,3) • fld is a jfieldID and is a handle to the field used by C functions
getting the jfieldID • To get this ID (an offset of a field in a class) use jfieldID GetFieldID(jclass c, //Class const char* name, //name const char* type); // typecode jfieldID fld = env->GetFieldID( Cls, “a”, “I”) ; for an int a; field in a java class named Cls
Javap • The command javap -s -p MyClass gives the field and method signatures.
Step 5: Write the C function(s) • include the correct header file #include “Cls.h” • include any other C stuff (e.g. <stdio.h> • Implement the functions using the signatures defined in the .h file
#include “Cls.h” JNIEXPORT jdouble JNICALL Java_Cls_f (JNIEnv *env, jobject obj, jint i, jstring str);{ //read a field out of the calling object jclass cls = env-> GetObjectClass(obj); jfieldID a = env->GetFieldID(cls,”a”,I); jint i = evn->GetIntField(obj,a); //change its value env->SetIntField(obj,a,++i); //convert paramater from javastring char* cStr = env-> GetStringUTFChars(str,NULL); //with a java string to C string function return 2.2; }
Method Invocation • Access to methods of an object calling a native method are similar to field access. • Invoke non-static methods with CallXXXMethod() • Invoke static methods with CallStaticXXXMethod() where XXX is the returnType. • A GetMethodID() or GetStaticMethodID give the required jmethodID used as parameters in the Call.
Example public class Cls { public native void f2( Cback cb); } public class Cback { public static void s1(String s) {…} public Date m1( int m, int d, int y) {…} }
Method call #include “Cls.h” JNIEXPORT void JNICALL Java_CLs_f2 (JNIEnv* env, jobject obj, jobjet cback) { jclass cls = env-> GetObjectClass(cback); jmethodID m = env-> GetMethodID( cls,”m1”,”(III)Ljava/util/Date;”); //now java’s cback.m1(2,1,61) jobject date = env->CallObjetMethod (cback, m, 2, 1, 61); jmethodID s = env->GetStaticMethodID (cls,”s1”,”(Ljava/lang/String;)V”); jstring str = env-> NetStringUTF(“Boo”); nv->CallStaticVoidMethod(cls,s,str); }
Exceptions • Exceptions can occur from a native method call to a java method. • Native methods can • throw an exception: ThrowNew • check a pending exception:ExceptionOccurred • handle and clear exceptions:ExceptionClear • Query the stack trace: ExceptionDescribe • throw example jclass exceptionClass = env->FindClass(“java/lang/Exception); env->ThrowNew(exceptionClass, “genException()”);
Step6: Create the dynamically loadable library • These are C compiler/OS issues. • I believe the gcc/unix compiler command to be gcc -G X.c -o libhello.so • An IBM C++ compiler for Win95 uses icc /Ge- /Gm+ /Mt /B”/Def:javai.lib” xyz.cpp • Key words to look for are • relocatable • dynamically linkable (DLL) library
Step 7: Run the program • In UNIX, if you get java.lang.NullPointerException at java.lang.Runtime.loadLibrary... then the os loader needs to have a library path set in your shell environment to the directory in which the library lives.
If you get java.lang.UnsatisfiedLinkError no hello in LD_LIBRARY_PATH at java.lang.Throwable…. then the loader has a library path set in your environment, but it does not include the directory in which the file lives.
Other interface issues • Threads and native methods • native methods must be thread safe • C programs that want a Java virtual machine.
Starting a Java VM • The JDK1.1 Invocation API permits startup of a JVM from C code. JavaVM = *jvm; JNIEnv *env; JavaVMInitArgs args; JNI_GetDefaultJavaVMInitArgs(&args); JNI_CreatJavaVM( &jvm, &env, &args); // now call a static method of MyClass jclass cls = env->FindClss(“MyClass”); jmethodID m = env->getStaticMethodID( cls,”s2”,”(I)V”); env->CallStaticVoidMethod(cls,m,15); jvm->DestroyJavaVM();