Skip to main content

IBM Systems  >  System z  >  Software  >  

Using JNI with Java 2 Technology Edition for OS/390, SDK 1.3

 

This page contains information on using the Java Native Interface (JNI) with the IBM Developer Kit for OS/390, Java 2 Technology Edition.

The information here relates specifically to IBM's Java products for OS/390. For general information on using JNI with Java 2 see Sun's web page Java Native Interface.

Related information:

Migrating from JDK 1.1

A JNI application built for JDK 1.1 can be both binary and source compatible with SDK 1.3 if certain conditions are met.  But the most general case is that a rebuild of the application is required.  In addition some migration paths require changes to the build scripts and an optional change to the source code.  The degree of change required depends on several factors:
  • Does the application use Java long, double or float?
  • Does the application exploit the macros and functions provided in float_md.h and longlong_md.h?
  • Is the application built for binary floating point (BFP) or floating point emulation?
  • Does the application use the JNI invocation API?
  • Does the application use the JNI helper functions (JNI_a2e_vsprintf and the string conversion functions)?

For full information see Migrating JNI applications to Java 2 Technology Edition for OS/390, SDK 1.3.

Building and running a JNI application

Building a JNI application with Java 2 Technology Edition for OS/390, SDK 1.3,  is not difficult but does require you take heed of a few details of the SDK and of OS/390 itself.

Compiling

Your C or C++ source code must #include the JNI header file jni.h.

The path to this file is <hlq>/J1.3/include where <hlq> is the directory in which SDK 1.3 is installed.  Specify this path in the compile command.   You also need some options that are peculiar to OS/390:

-W "c,langlvl(extended)" - enables support for C/C++ type long long (omitting this will cause a compiler syntax error)

-W "c,float(ieee)" - enables support for IEEE754-compliant types double and float (omitting this will not cause an error but will make the results of operations on Java doubles and floats meaningless)

-W c,dll -  enables the program to refer to symbols exported by a DLL

Example

c89 -c -o myprog.o -W c,dll -W "c,langlvl(extended)" -W "c,float(ieee)" -I/usr/lpp/java/IBM/J1.3/include myprog.c

If you are building a DLL (a library of native methods, for example) add the option -W c,exportall to the compile command, or code #pragma export statements in your source code for the symbols that the DLL needs to export.

If you are migrating a JDK 1.1 application and your code currently #includes the JDK header files float_md.h and longlong_md.h, in SDK 1.3 you do not need to include these files.  The JNI native type helper functions and macros they contain in JDK 1.1 are built in to SDK 1.3.  The header files are still supplied but they are empty, provided solely to maintain source compatibility.

Linking

If your application uses the JNI Invocation API or the JNI helper functions (JNI_a2e_vsprintf and the string conversion functions) you must link your application with the libjvm.x side deck shipped with SDK 1.3.  The path to this file is <hlq>/J1.3/bin/classic.

Example

c89 -o myprog myprog.o /usr/lpp/java/IBM/J1.3/bin/classic/libjvm.x

To link with the debug version of the side deck (corresponding to the java_g command) use libjvm_g.x.

If you are building a DLL the command is a little different:

Example

c89 -o libmyprog.so -W l,dll myprog.o /usr/lpp/java/IBM/J1.3/bin/classic/libjvm.x

Executing

The DLL directories shipped with SDK 1.3 must be in the LIBPATH of the application.  The addition you need to LIBPATH is <hlq>/J1.3/bin:<hlq>/J1.3/bin/classic.  Set the class path to point to the classes used by the application.

Example

export LIBPATH=/usr/lpp/java/IBM/J1.3/bin:
/usr/lpp/java/IBM/J1.3/bin/classic:$LIBPATH
export CLASSPATH=.
myprog

With JDK 1.1 and the JNI Invocation API you needed to add the JDK system classes (classes.zip) to the class path.  With SDK 1.3 you only need to do the equivalent if you are starting the Java virtual machine with 1.1 style initialisation arguments.  In this case you must add <hlq>/J1.3/lib/rt.jar to the class path.  If you are starting with 1.2 style initialisation arguments you do NOT need to do this.  For more information on 1.1 versus 1.2 style initialisation arguments see Migrating from prior releases to SDK 1.3.

Full code examples

The directory J1.3/demo/jni in your SDK 1.3 installation contains several complete C and C++ examples with makefiles and scripts for building and  running the examples.

Using the Java types long, double and float

JNI applications will usually need to deal with the Java types long, double and float, for one or more of the following reasons:

  • to receive such variables as native method parameters
  • to inspect or modify such instance or class variables directly
  • to return such a variable as the native method return value
  • to pass such variables as parameter data when invoking other Java methods from a native method

JNI Native types

For each of the Java types the JNI provides native types that you can use in your JNI application.  These types are shown in the following table.

JNI native types
Java type JNI native type Size in bytes
long jlong 8
double jdouble 8
float jfloat 4

Note that there is no need to use these types if your application does not manipulate the Java types long, double and float.

The JNI native types should be used when defining a native method implementation so that the compiler will match the native method signature with that in the header file generated by using the javah utility.

Helper functions and macros

The JNI native types jlong, jdouble and jfloat are incompatible with the respective C/C++ types long, double and float.  If you attempt to cast one to the other you will get either a compile error or unexpected behaviour at runtime.  You should not attempt to convert one to the other as the formats of the JNI native types are private to the SDK and might change in future releases.

The JNI header file jni.h includes another header file that is shipped with SDK 1.3 named jni_md.h. This defines helper functions and macros you can use to work with the native types.

The functions and macros in jni_md.h relating to jlong typically include ll either at the start or the end of the name.  Those relating to jdouble or jfloat typically include dbl, double, flt or float in the name.  There are also some jlong, jdouble and jfloat constants defined which may be useful.

Examples:

Macro ll_add() adds two jlongs and returns a jlong.

Macro int2ll() converts an int to a jlong.

Function ll2str() converts a jlong to an ASCII string representation of the 64-bit value.

Function flt2dbl() converts a jfloat and returns a jdouble.

Macro dbl2nat() converts a jdouble to an ESA/390 native double.

Macro dbl_sqrt() takes the square root of a jdouble and returns a jdouble.

Functions dbl2str() and flt2str() convert their arguments into an ASCII string representation.

Compared with the standard Unix math library (libm.a) set of functions, only those required by Java are available through jni_md.h. Other standard math library functions may be used by #including the system header file math.h and taking care with the arguments you use with them and in converting the results, if necessary, for use by Java. Also, you will have to specify -lm on the link command to link using the standard math function library.

IEEE754 native support in OS/390

OS/390 V2R6 introduced support for the IEEE754-compliant types double and float implemented in hardware on G5 and later systems.  SDK 1.3, which runs on OS/390 V2R8 and above, fully exploits this support.  The reason is simple:  the performance of floating point operations, such as + - / *, is greatly improved.

The IEEE754 double and float types can be still used where there is no hardware support (for example pre-G5 hardware) but the floating point operations are then emulated in the Language Environment (LE) layer of the operating system and yield poorer performance.

Note that, in contrast to JDK 1.1, SDK 1.3 always exploits hardware support of IEEE754 double and float.  There are not two sets of libraries, one for hardware exploitation and one for floating point emulation, as there were for JDK 1.1.6 and JDK 1.1.8.

Example

The directory J1.3/demo/jni/JNINativeTypes in your SDK 1.3 installation contains examples of using JNI native types with the helper functions and macros

Using the ESA/390 native types double and float

Prior to OS/390 V2R6, the native floating point support on ESA/390 systems did not represent double and float values in the manner described in IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std 754-1985). The OS/390 C and C++ compilers support the float, double, and long double types implemented as 4-byte, 8-byte, and 16-byte storage elements and the binary layout of mantissa and exponent parts differs from IEEE754.

Your JNI application is not restricted from using these types in any way you wish, including use of math functions, provided that you do not need to pass values across the JNI without first converting to the corresponding Java types.

ESA/390 native double and float will give better performance than using the JNI native types described above because they are implemented in microcode rather than application level code.

Using va_list in JNI function calls

The JNI provides a range of functions, for example CallBooleanMethodV(), to which you can pass the necessary arguments using a single va_list argument. Historically, there have been two different header files used to handle va_list arguments - stdarg.h and varargs.h. You should take care to use the stdarg.h header file in your JNI application.

Furthermore, under OS/390 Unix System Services there are two different implementations of va_list and the associated macros va_start(),va_arg(),and va_end() in stdarg.h.  It is important that your native methods are compiled without specifying -D_VARARG_EXT_, otherwise unexpected results will occur.

Codepage considerations

The Java virtual machine handles strings in both UNICODE or UTF-8 format. A JNI application will usually need to deal with these types, for one or more of the following reasons:

  • to receive such variables as native method parameters
  • to inspect or modify such instance or class variables directly
  • to return such a variable as the native method return value
  • to pass such variables as parameter data when invoking other Java methods from a native method

The JNI Specification details the types required by each JNI function.

There are two approaches that may be taken for coding the JNI application:

Approach 1

In this approach the conversion boundary is between the JNI application and the C runtime Library:

  • Compile literal strings into ASCII ISO8859-1.
  • Only convert strings to EBCDIC when passing them to any code that requires EBCDIC, for example the C runtime library.  C library functions __atoe() and iconv() may be used.

Approach 2

In this approach the conversion boundary is between the JNI application and the Java virtual machine:

The difference in these approaches is shown in the following table:

Conversion boundaries
Stage Approach 1 Approach 2
Java virtual machine UNICODE/UTF-8 UNICODE/UTF-8
JNI application ASCII EBCDIC
C runtime library EBCDIC EBCDIC

JNI string conversion helper functions

A number of JNI APIs have been provided to help with conversion of Java strings.

jint GetStringPlatform(JNIEnv* env,
                       jstring instr,
                       char* outstr,
                       jint outlen,
                       const char* encoding)

Convert a Java string instr to a null-terminated C string and write the converted string to the buffer outstr. outlen is the size of the outstr buffer. encoding is the Java encoding name (in EBCDIC) to be used for the conversion. If a null pointer is passed for encoding, the default file encoding is used (from Java's file.encoding property). A quick conversion is used if it is available and, if not, a Java-based converter is used.

jint GetStringPlatformLength(JNIEnv* env,
jstring instr, jint* outlen,
const char* encoding)

Return the length in bytes of the output buffer that would be needed to hold the converted Java string instr, including a null terminating byte. The length is returned as the new value of *outlen.

jint NewStringPlatform(JNIEnv* env,
const char* instr, jstring* outstr,
const char* encoding)

Convert a null-terminated C string instr to a Java string object which is returned as the new value of *outstr. encoding is the Java encoding name (in EBCDIC) to be used for the conversion. If a null pointer is passed for encoding, the default file encoding is used (from Java's file.encoding property).

To take advantage of these functions include the header file jni_convert.h and link your application with the libjvm.x side deck. See Building and running a JNI application.

Note for JDK 1.1 users:  This support is no longer available by linking with a side deck named libJNIConvert.x or an object file named jni_convert.o.  The string conversion functions are now built into the Java virtual machine.

Example

The directories J1.3/demo/jni/InvocationAPI and J1.3/demo/jni/NativeMethods in your SDK 1.3 installation contain examples of using the string conversion functions

JNI_a2e_vsprintf helper function

The JNI Invocation API provides a hook for a function that will receive all messages from the Java virtual machine. You supply a function and give its name in the vfprintf field of the JavaVMInitArgs structure. The difficulty for the JNI programmer is that, on OS/390, the format string and any character arguments are passed to the function in ASCII. It is not a trivial task to convert the values to EBCDIC before output to a Runtime Library function such as fprintf.

To make this job easier a JNI helper function, JNI_a2e_vsprintf, is provided with Java 2 Technology Edition for OS/390. JNI_a2e_vsprintf accepts the format string and arguments passed to the vfprintf hook and formats the message into a buffer in EBCDIC. Apart from providing ASCII-EBCDIC conversion, JNI_a2e_vsprintf otherwise behaves in exactly the same way as the Runtime Library function vsprintf.

jint JNI_a2e_vsprintf(char *target,
const char *format, va_list args)

Take a format string and variable list of arguments as passed to the vfprintf hook and return a formatted string encoded in EBCDIC. The return buffer, target, must be large enough to hold the formatted output. Since, in general, the size of the formatted output is not known ahead of time, it is recommended that a buffer of 6144 bytes be allocated, this being the longest string that will be returned by the function. The return value is the number of characters placed in the target buffer or -1 if the call was unsuccessful.

To take advantage of this function include the header file jni_convert.h and link your application with the libjvm.x side deck. See Building and running a JNI application.

Example

The directory J1.3/demo/jni/vfprintfHook in your SDK 1.3 installation contains an example of setting the vfprintf hook and using the JNI_a2e_vsprintf function