330 likes | 661 Views
Java Native Interface (JNI). JNI. Linking Java and C code. JNI. Stands for “ Java Native Interface ” Set of tools/code that allows user to call “ native ” methods from Java. Includes “ bindings ” for C/C++. Can be used to call C/C++ from Java (typical), or Java from C (invocation API)
E N D
JNI Linking Java and C code
JNI • Stands for “Java Native Interface” • Set of tools/code that allows user to call “native” methods from Java. • Includes “bindings” for C/C++. • Can be used to call C/C++ from Java (typical), or Java from C (invocation API) • Differs from spawning executable – data is passed to/from C/C++ method • Question: why is this difficult?
Reasons for using JNI • Feature not available in java language (rare). • Code already written in another language, don’t want to rewrite (typical). • Java is slow (how slow?) • Other language has no additional features per se, but has much better syntax for handling certain operations (Fortran for math).
Problems with JNI • Only provides C/C++ bindings. Going to Fortran, COBOL, Ada, etc. requires extra step. • Not portable • Mapping is not trivial • Can be unsafe • Cannot run from applet (security issues)
Machinery for using JNI Steps to follow …
Basic steps to calling native code • Write java class with at least one method declared with native keyword. Provide no implementation • public native void sayHello(); • Example above is most simple, but method may pass any parameters or have any return type. • Add a call to System.loadLibrary(“libname”) in the class that declares the native method: • static{ System.loadLibrary(“hello”);}//static means called only once.
Steps, cont. 3. Compile the class • javac Hello.java 4. Produce the C/C++ header files using the javah utility: • Javah Hello • This produces the header file Hello.h • Write your implementation file by first copying the function signature produced in the include file. Also, #include the header file. #include “Hello.h”
Steps, cont. 6. Write the implementation in C/C++. This will require using JNI methods to access the data or possibly casts to convert to basic C/C++ types 7. Best technique: Break into two steps. Think of your C/C++ function as a wrapper which accesses the Java data and maps it to C data using JNI methods, then shoves the converted data into a prewritten standalone C program.
Steps, cont. 8. Compile your native method(s) as a shared object (or DLL on Windows). • WARNING: Be sure to point your linker to the include files in /jdk1.3/include and jdk1.3/include/linux (for example). • WARNING: Mixing languages is much easier using a straight C wrapper rather than C++. 9. Set the environment variable LD_LIBRARY_PATH to the shared object directory • Run main Java class.
C language bindings What does Java pass to my method?
What does Java pass to my C function? • JNIEnv* : A pointer to the the JNI environment. This pointer is a handle to the current thread in the JVM, and contains mapping functions and other housekeeping information. • jobject : A reference to the object that called the native code. (like “this” pointer). • Any arguments specified by the method.
Simple examples online • HelloWorld Example: No data passed • Hello.java • Hello.cc • Max example : Only native dataypes • Utils.java • utils.cc • Advanced Max example: Arrays • Utils.java • utils.cc • Max Java-C-Fortran: max.f
Java object Mappings • Object passed by reference • All objects have type jobject as:
Object mappings, cont. • For example, if a method getLine exists in a class call Prompt, then: private native String getLine(String Prompt); is mapped into JNIExport jstring JNICALL Java_Prompt_getLine(JNIEnv *, jobject, jstring); • But how to access data/methods from object that is passed in?
JNI Advice • Can seem like a bewildering number of functions. • Do not try to learn it all. • Keep interfaces very simple. • Preferably, only native datatypes, Strings, and arrays. • Be careful about • Copies vs. rerences • Freeing memory • Best not to allocate memory from with native code.
Accessing java strings • Do NOT do the following: JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt){ printf(“%s”, prompt); } • Why is this bad?
Right way to access Strings • Must use special methods in env structure char *str = (*env)->GetStringUTFChars(env,prompt,0); /* this maps into regular C char* */ printf(“%s”, str); /* now it is ok to print */ (*env)->ReleaseStringUTFChars(env, prompt, str); /* must release String to avoid memory leaks */
Returning Strings • Previous technique allows us to use a String passed in from Java. • What if we want to return a String? • Can use NewStringUTF as: char buf[128]; /* allocate memory for local char* in C */ scanf(“%s”, buf); /* read into char* from stdio */ return( (*env)->NewStringUTF(env, buf)); /* construct and return the Java String */
Other JNI String methods • GetStringChars • Takes the Java String and returns a pointer to an array of Unicode characters that comprise it. • ReleaseStringChars • Releases the pointer to the array of Unicode characters • NewString • Constructs a new String object from an array of Unicode Characters • GetStringLength • Returns the length of a string of Unicode characters
Java arrays • Note that you can NOT do the following: JNIExport jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr){ int i, sum = 0; for (i = 0; i<10; i++){ sum += arr[i]; /* NO! – why not? } ... • Must use java methods to access array data in C
Array methods • The previous example should be written as: jsize len = (*env)->GetArrayLength(env,arr); jint *body = (*env)->GetIntArrayElements(env,arr,0); for (i=0;i<len;++i){ sum += body[i]; } (*env)->ReleastIntArrayElements(env,arr,body,0); /* very important – copies back to java array if copy had to be made */
Array methods, cont. • Note that there are analagous functions for float, byte, double, etc: • Get<type>ArrayElements • Release<type>ArrayElements • Important: These Get functions may copy the entire array. If this is undesirable, use Get/Set<type>ArrayRegion functions
Calling java methods • What if you pass a java object to a C routine and wish to “call back” a method on the Java object. • Good to avoid this when you can but sometimes it is very important. • Need to use the jobject reference that is passed in by java.
Steps to follow • Native method calls JNI function GetObjectClass • returns the jclass object that is type of that obj • Native method calls JNI function GetMethodID • returns jmethodID of method in class (0 for no such method) • Finally, native method calls JNI function CallVoidMethod. • invokes an instance of method with void return type. You pass object, methodID, and actual arguments.
A simple alternative – spawning a system executable • Advantages • Infinitely simpler • Portable • Can use any native language • Disadvantages • Can only pass data to and from vi stdout • Must reload executable for each invocation
Spawning Executable -- technique • Process p = Runtime.exec(“some_exec”); • Use p to manage process: • p.getInputStream(); • p.getOutputStream(); • p.kill(); • p.halt();
Legacy Collections • java.util.Vector • Still useable, but typically ArrayList is preffered. • Only major difference is if you are using muliple threads • java.util.HashTable • Still useable, but typically HashMap is preferred. • Again, different if using multiple threads.