Examples: Use the Java Native Interface for native methods

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.

Note: The library where the NATHELLO service program is located must be in the library list for this example to run.

Example 1: NativeHello class

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

Note: Read the Code example disclaimer for important legal information.
/* 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.

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.

Example 3: NativeHello.c native method implementation of the NativeHello Java class

Note: Read the Code example disclaimer for important legal 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)

See Use the Java Native Interface for native methods for background information.

Related concepts
Example: IBM i5/OS PASE native method for Java
Related tasks
Example: Run the Java Performance Data Converter
Related reference
Example: Internationalization of dates using the java.util.DateFormat class
Example: Internationalization of numeric display using the java.util.NumberFormat class
Example: Internationalization of locale-specific data using the java.util.ResourceBundle class
Example: Access property
Example: BLOB
Example: CallableStatement interface for IBM Developer Kit for Java
Example: Remove values from a table through another statement's cursor
Example: CLOB
Example: Create a UDBDataSource and bind it with JNDI
Example: Create a UDBDataSource, and obtain a user ID and password
Example: Create a UDBDataSourceBind and set DataSource properties
Example: DatabaseMetaData interface for IBM Developer Kit for Java - Return a list of tables
Example: Datalink
Example: Distinct types
Example: Embed SQL Statements in your Java application
Example: End a transaction
Example: Invalid user ID and password
Example: JDBC
Example: Multiple connections that work on a transaction
Example: Obtain an initial context before binding UDBDataSource
Example: ParameterMetaData
Example: Change values with a statement through another statement's cursor
Example: ResultSet interface for IBM Developer Kit for Java
Example: ResultSet sensitivity
Example: Sensitive and insensitive ResultSets
Example: Set up connection pooling with UDBDataSource and UDBConnectionPoolDataSource
Example: SQLException
Example: Suspend and resume a transaction
Example: Suspended ResultSets
Example: Test the performance of connection pooling
Example: Test the performance of two DataSources
Example: Update BLOBs
Example: Update CLOBs
Example: Use a connection with multiple transactions
Example: Use BLOBs
Example: Use CLOBs
Example: Use JTA to handle a transaction
Example: Use metadata ResultSets that have more than one column
Example: Use native JDBC and IBM Toolbox for Java JDBC concurrently
Example: Use PreparedStatement to obtain a ResultSet
Create and populate a DB2CachedRowSet
Example: Use the Statement object's executeUpdate method
Examples: JAAS HelloWorld
Example: JAAS SampleThreadSubjectLogin
Sample: IBM JGSS non-JAAS client program
Sample: IBM JGSS non-JAAS server program
Sample: IBM JGSS JAAS-enabled client program
Sample: IBM JGSS JAAS-enabled server program
Examples: IBM Java Secure Sockets Extension
Example: Call a CL program with java.lang.Runtime.exec()
Example: Call a CL command with java.lang.Runtime.exec()
Example: Call another Java program with java.lang.Runtime.exec()
Example: Call Java from C
Example: Call Java from RPG
Example: Use input and output streams for interprocess communication
Example: Java Invocation API
Example: Use sockets for interprocess communication
Examples: Change your Java code to use client socket factories
Examples: Change your Java code to use server socket factories
Examples: Change your Java client to use secure sockets layer
Examples: Change your Java server to use secure sockets layer