This example program is a simple Java™ Native Interface (JNI) example in which a C native method is used to display "Hello, World." Use the javah tool with the NativeHello class file to generate the NativeHello.h file. This example assumes that the NativeHello C implementation is part of a service program that is called NATHELLO.
Example 1: NativeHello class
public class NativeHello { // Declare a field of type 'String' in the NativeHello object. // This is an 'instance' field, so every NativeHello object // contains one. public String theString; // instance variable // Declare the native method itself. This native method // creates a new string object, and places a reference to it // into 'theString' public native void setTheString(); // native method to set string // This 'static initializer' code is called before the class is // first used. static { // Attempt to load the native method library. If you do not // find it, write a message to 'out', and try a hardcoded path. // If that fails, then exit. try { // System.loadLibrary uses the iSeries library list in JDK 1.1, // and uses the java.library.path property or the LIBPATH environment // variable in JDK1.2 System.loadLibrary("NATHELLO"); } catch (UnsatisfiedLinkError e1) { // Did not find the service program. System.out.println ("I did not find NATHELLO *SRVPGM."); System.out.println ("(I will try a hardcoded path)"); try { // System.load takes the full integrated file system form path. System.load ("/qsys.lib/jniexample.lib/nathello.srvpgm"); } catch (UnsatisfiedLinkError e2) { // If you get to this point, then you are done! Write the message // and exit. System.out.println ("<sigh> I did not find NATHELLO *SRVPGM anywhere. Goodbye"); System.exit(1); } } } // Here is the 'main' code of this class. This is what runs when you // enter 'java NativeHello' on the command line. public static void main(String argv[]){ // Allocate a new NativeHello object now. NativeHello nh = new NativeHello(); // Echo location. System.out.println("(Java) Instantiated NativeHello object"); System.out.println("(Java) string field is '" + nh.theString + "'"); System.out.println("(Java) Calling native method to set the string"); // Here is the call to the native method. nh.setTheString(); // Now, print the value after the call to double check. System.out.println("(Java) Returned from the native method"); System.out.println("(Java) string field is '" + nh.theString + "'"); System.out.println("(Java) All done..."); } }
Example 2: Generated NativeHello.h header file
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class NativeHello */ #ifndef _Included_NativeHello #define _Included_NativeHello #ifdef __cplusplus extern "C" { #endif /* * Class: NativeHello * Method: setTheString * Signature: ()V */ JNIEXPORT void JNICALL Java_NativeHello_setTheString (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
This NativeHello.c example shows the implementation of the native method in C. This example shows how to link Java to native methods. However, it points out complications that arise from the fact that the iSeries™ server is internally an extended binary-coded decimal interchange code (EBCDIC) machine. It also shows complications from the current lack of true internationalization elements in the JNI.
These reasons, although they are not new with the JNI, cause some unique iSeries server-specific differences in the C code that you write. You must remember that if you are writing to stdout or stderr or reading from stdin, your data is probably encoded in EBCDIC form.
In C code, you can easily convert most literal strings, those that contain 7-bit characters only, into the UTF-8 form that is required by the JNI. To do this, bracket the literal strings with code-page conversion pragmas. However, because you may write information directly to stdout or stderr from your C code, you might allow some literals to remain in EBCDIC.
Example 3: NativeHello.c native method implementation of the NativeHello Java class
#include <stdlib.h> /* malloc, free, and so forth */ #include <stdio.h> /* fprintf(), and so forth */ #include <qtqiconv.H> /* iconv() interface */ #include <string.h> /* memset(), and so forth */ #include "NativeHello.h" /* generated by 'javah-jni' */ /* All literal strings are ISO-8859-1 Latin 1 code page (and with 7-bit characters, they are also automatically UTF-8). */ #pragma convert(819) /* handle all literal strings as ASCII */ /* Report and clear a JNI exception. */ static void HandleError(JNIEnv*); /* Print an UTF-8 string to stderr in the coded character */ set identifier (CCSID) of the current job. */ static void JobPrint(JNIEnv*, char*); /* Constants describing which direction to covert: */ #define CONV_UTF2JOB 1 #define CONV_JOB2UTF 2 /* Convert a string from the CCSID of the job to UTF-8, or vice-versa. */ int StringConvert(int direction, char *sourceStr, char *targetStr); /* Native method implementation of 'setTheString()'. */ JNIEXPORT void JNICALL Java_NativeHello_setTheString (JNIEnv *env, jobject javaThis) { jclass thisClass; /* class for 'this' object */ jstring stringObject; /* new string, to be put in field in 'this' */ jfieldID fid; /* field ID required to update field in 'this' */ jthrowable exception; /* exception, retrieved using ExceptionOccurred */ /* Write status to console. */ JobPrint(env, "( C ) In the native method\n"); /* Build the new string object. */ if (! (stringObject = (*env)->NewStringUTF(env, "Hello, native world!"))) { /* For nearly every function in the JNI, a null return value indicates that there was an error, and that an exception had been placed where it could be retrieved by 'ExceptionOccurred()'. In this case, the error would typically be fatal, but for purposes of this example, go ahead and catch the error, and continue. */ HandleError(env); return; } /* get the class of the 'this' object, required to get the fieldID */ if (! (thisClass = (*env)->GetObjectClass(env,javaThis))) { /* A null class returned from GetObjectClass indicates that there was a problem. Instead of handling this problem, simply return and know that the return to Java automatically 'throws' the stored Java exception. */ return; } /* Get the fieldID to update. */ if (! (fid = (*env)->GetFieldID(env, thisClass, "theString", "Ljava/lang/String;"))) { /* A null fieldID returned from GetFieldID indicates that there was a problem. Report the problem from here and clear it. Leave the string unchanged. */ HandleError(env); return; } JobPrint(env, "( C ) Setting the field\n"); /* Make the actual update. Note: SetObjectField is an example of an interface that does not return a return value that can be tested. In this case, it is necessary to call ExceptionOccurred() to see if there was a problem with storing the value */ (*env)->SetObjectField(env, javaThis, fid, stringObject); /* Check to see if the update was successful. If not, report the error. */ if ((*env)->ExceptionOccurred(env)) { /* A non-null exception object came back from ExceptionOccurred, so there is a problem and you must report the error. */ HandleError(env); } JobPrint(env, "( C ) Returning from the native method\n"); return; } static void HandleError(JNIEnv *env) { /* A simple routine to report and handle an exception. */ JobPrint(env, "( C ) Error occurred on JNI call: "); (*env)->ExceptionDescribe(env); /* write exception data to the console */ (*env)->ExceptionClear(env); /* clear the exception that was pending */ } static void JobPrint(JNIEnv *env, char *str) { char *jobStr; char buf[512]; size_t len; len = strlen(str); /* Only print non-empty string. */ if (len) { jobStr = (len >= 512) ? malloc(len+1) : &buf; if (! StringConvert(CONV_UTF2JOB, str, jobStr)) (*env)->FatalError (env,"ERROR in JobPrint: Unable to convert UTF2JOB"); fprintf(stderr, jobStr); if (len >= 512) free(jobStr); } } int StringConvert(int direction, char *sourceStr, char *targetStr) { QtqCode_T source, target; /* parameters to instantiate iconv */ size_t sStrLen, tStrLen; /* local copies of string lengths */ iconv_t ourConverter; /* the actual conversion descriptor */ int iconvRC; /* return code from the conversion */ size_t originalLen; /* original length of the sourceStr */ /* Make local copies of the input and output sizes that are initialized to the size of the input string. The iconv() requires the length parameters to be passed by address (that is as int*). */ originalLen = sStrLen = tStrLen = strlen(sourceStr); /* Initialize the parameters to the QtqIconvOpen() to zero. */ memset(&source,0x00,sizeof(source)); memset(&target,0x00,sizeof(target)); /* Depending on direction parameter, set either SOURCE or TARGET CCSID to ISO 8859-1 Latin. */ if (CONV_UTF2JOB == direction ) { source.CCSID = 819; } else { target.CCSID = 819; } /* Create the iconv_t converter object. */ ourConverter = QtqIconvOpen(&target,&source); /* Make sure that you have a valid converter, otherwise return 0. */ if (-1 == ourConverter.return_value) return 0; /* Perform the conversion. */ iconvRC = iconv(ourConverter, (char**) &sourceStr, &sStrLen, &targetStr, &tStrLen); /* If the conversion failed, return a zero. */ if (0 != iconvRC ) return 0; /* Close the conversion descriptor. */ iconv_close(ourConverter); /* The targetStr returns pointing to the character just past the last converted character, so set the null there now. */ *targetStr = '\0'; /* Return the number of characters that were processed. */ return originalLen-tStrLen; } #pragma convert(0)
See Use the Java Native Interface for native methods for background information.