Example: NativeHello.c

The C source code is used in the integrated language environment (ILE) native method for Java™ example.

The NativeHello.c source code shows the implementation of the native method in C. When the Java native method setTheString(), defined in the Java code, is called by the Java code the C function Java_NativeHello_setTheString() gets control and uses Java Native Interface (JNI) to call back into the Java code to set the value of the Java string variable theString. Control is then returned to the Java code, and the string variable is written to stdout out by the Java code.

This example shows how to link Java to native methods. However, it points out complications that arise from the fact that the IBM i 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 IBM i 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 the codepage conversion pragma statement. However, because you may write information directly to stdout or stderr from your C code, you might allow some literals to remain in EBCDIC.

Note: The #pragma convert(0) statements convert character data to EBCDIC. The #pragma convert(819) statements convert character data to American Standard Code for Information Interchange (ASCII). These statements convert character data in the C program at compile time.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
#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)