IBM Support

Using Java Native Interface (JNI) with COBOL

General Page

In this technote, you'll learn how Java Native Interface (JNI) works and get hands-on with JNI and COBOL interoperability by following the Hello World examples.

Enterprise COBOL

Enterprise COBOL introduced the concept of Object-Oriented (OO) COBOL that simplifies the interactions between Java and COBOL. Using OO COBOL is a convenient way to create OO applications, in particular when crossing the boundary between Java and COBOL is required.

NEW! Enterprise COBOL 6.4 and later compilers support a COBOL/Java interoperability framework that does not depend on OO COBOL.  The goals of the new framework include making existing COBOL programs easily callable from Java with minimal changes, supporting calls to static Java methods from COBOL using simple CALL statements, making COBOL data items easily accessible for read/write access from Java, and providing comprehensive support for automatic conversions between Java and COBOL data formats, which helps to reduce, and often eliminate, the need to make any JNI calls directly from COBOL code.

For more details on the non-OO COBOL/Java interoperability features of Enterprise COBOL 6.4 and later compilers, please refer to the following resources:

Java Native Interface (JNI)
Java Native Interface (JNI) is a native programming interface that is part of the Java Software Development Kit (SDK). This interface defines the infrastructure between Java and other languages, such as C and C++, COBOL, PL/I and assembly. It is available on z/OS® for all Language Environment-compliant languages.

JNI provides the solution for two-way communications.

  • It defines a method for Java to invoke native methods.
  • It also defines a method, via a defined API, for native code to invoke Java code. With the defined API, the native code can manipulate Java objects, call Java methods, and share data with Java.

How JNI works

JNI provides a means for Java code to call native code. This interaction is useful in cases when native capabilities are more efficient than Java or when the specific code is already available in native and you want to use it. JNI provides JNI services (also referred as functions) via the JNI API to act on the Java data and use Java features.

The JNI also provides the native code with means to embed a JVM in the native code and to access Java features without having to link with the JVM.

JNI services and API

JNI provides many services for the native code to access Java features, such as class operations, calling methods, string and array operations, etc. It also provides services via the Invocation API to embed a JVM in the native application.

The JNI APIs are defined under a language-specific file that holds all API functions and the type mapping for the Java JNI types. For COBOL, the APIs are defined in the JNI copybook called JNI.cpy, which is typically located under the /usr/lpp/cobol/include directory. To use the JNI services, the JNI copybook needs to be included.

There are two main JNI structures defined in the JNI APIs that are key to the JNI execution:

1. The “JNI environment” structure– the JNI service API.
Accessing the JNI environment structure is done via two levels of indirection using an interface pointer (in COBOL, it is the special register, JNIEnvPtr). This pointer points to a per-thread JNI environment pointer (JNIEnv, in COBOL it is declared in the JNI copybook) that points to an array of JNI function pointers (a function table named JNINativeInterface) for accessing the callable service functions. The JNI environment pointer is thread-specific and cannot be passed from one thread to another. See the figure below.

JNI environment pointer

Figure 1. JNI environment pointer

2.  The “JavaVM” structure– the JNI Java VM service API that allows to create and destroy the JVM.

The JavaVM structure is accessed via an interface pointer, JavaVMPtr. This pointer points to a pointer that points to an array of JNI function pointers (a function table named JNIInvokeInterface) for accessing callable “invocation interface” functions.

Java for the z/OS platform provides an extended API to deal with strings due to the encoding differences. It defines the following extensions: NewStringPlatform, GetStringPlatformLength, GetStringPlatform.

These EBCDIC services are packaged as a dynamic link library (DLL) file that is part of the IBM® Java SDK. They are called directly and not dereferenced from the JNIEnv pointer (the JNIEnvPtr). This extended API is resolved through the libjvm.x DLL side file, provided with your IBM Java SDK, which needs to be included in the link-edit step.

Hello World: Making a simple connection

When an application starts with native code and requires to call Java features via JNI APIs, it first needs to load a JVM using the JNI API, unless it is using OO COBOL. With OO COBOL invoking Java, COBOL will start the JVM for you.

When an application starts with Java, it calls the native language via native methods.

The native methods called from Java are declared in the Java code and implemented in the native language. The name of the native method follows the JNI naming rules. In the simple case, the method would be in the format of Java_classname_methodname. See the JNI Specification for more detailed information regarding native method names and arguments.

As part of the JNI calling convention, the native method has two hidden arguments that are not seen on the Java declaration. Those arguments are predefined by the JNI:JNIEnv interface pointer and jobject. In COBOL, the JNIEnv interface pointer is the JNIEnvPtr and it is used to dereference the JNI service. The jobject is a pointer to the class in case the class is static, or to the instance of the class if it is not static.

You can use the javah tool to generate .h header files that can be inspected for the signature of the native method generated. These header files are used by C and C++ compilers, not by the COBOL compiler, but you might find them useful for checking the naming of native methods. Although generating the .h files is optional, it has great value in providing the expected signature.

Once the native code is compiled and linked, the native methods are accessed via an executable DLL module that is loaded on the Java side. There are two ways for Java to load the executable DLL module:

  • Using System.loadLibrary(“Name”)

    In this case the DLL name needs to be in the format of libName.so.

  • Using System.load("<absolutePath>/Name")

    In this case there is no restriction on the name of the library.

For proper loading and binding, the executable DLL needs to reside on the z/OS UNIX side, have the right attributes (executable) and follow naming rules.

Example 1: “Hello world” from COBOL

This Java sample code demonstrates how to invoke a native COBOL method that prints “Hello from COBOL”. The example has no parameters passed to nor no return data from the native method.

  1. Declare the native method in the Java code.
  2. Compile the Java code using javac to generate a .class file.
  3. Generate the native code.

    There are two main parts that need special attention: the program-id and the procedure division’s parameters and return value. The program-id should follow the JNI rules and match the native method name as declared on the Java side. In this case, following the Java_classname_methodname convention, the program-id is Java_HelloTest_printHello. The native method declared has no arguments but the COBOL procedure division declares the two ‘hidden’ arguments required for JNI, even though they are not used in the procedure (as we do not facilitate the JNI services). Note that there is no use of JNI services. Therefore the JNI copybook is not included.

  4. Compile the COBOL code with the required options given in the example. These compilation options are described in the “Compiling and linking for COBOL” section.
  5. Link-edit step to generate the executable DLL module (following naming rules to allow proper loading). The required options are described in the “Compiling and linking for COBOL” section.
  6. Run the Java program.

    If the System.loadLibrary(“Name”) method is used, the Java will search a file of the type libName.so in the library path provided via the -Djava.library.path option. If you use the System.load(...) method, the path points to the library file.

The following is a simple COBOL program that prints “Hello from COBOL “.

helloTest.cbl


helloTest.cbl
Process pgmname(longmixed),lib,dll,thread
       IDENTIFICATION DIVISION.
       PROGRAM-ID.    "Java_HelloTest_printHello" is recursive.
       ENVIRONMENT DIVISION.
       DATA DIVISION.
       LINKAGE SECTION.
       01 ENV-PTR   USAGE POINTER.
       01 OBJECT-REF  PIC S9(9) BINARY.
       PROCEDURE DIVISION USING BY VALUE ENV-PTR
                                     OBJECT-REF.
              Display " >> Hello from COBOL ".
              GOBACK.


This COBOL program is compiled and linked as follows, where a typical installation path for COBOL is /usr/lpp/cobol and a typical installation path for Java is /usr/lpp/java. Replace with your own paths for your environment.


/usr/lpp/cobol/bin/cob2 -c -qdll,thread helloTest.cbl
/usr/lpp/cobol/bin/cob2 -bdll -o libHello.so helloTest.o /usr/lpp/java/IBM/J7.1/bin/j9vm/libjvm.x /usr/lpp/cobol/lib/igzcjava.x helloTest.cbl

The following HelloTest.java program is compiled and run using the following USS commands:


/usr/lpp/java/IBM/J7.1/bin/javac HelloTest.java
/usr/lpp/java/IBM/J7.1/bin/java -Djava.library.path=. HelloTest

HelloTest.java

public class HelloTest
{
   public native void printHello();
   public static void main(String argv[]) throws Exception
  {
    System.loadLibrary("Hello");
    HelloTest newHelloTest = new HelloTest();
    newHelloTest.printHello();
      return;
   }
}

Connecting Java and COBOL

Enterprise COBOL provides Java-oriented capabilities in addition to the basic OO capabilities available directly in the COBOL language. For example, it allows creation of Java and COBOL classes and invocation of methods on Java and COBOL objects using the INVOKE statement. For basic Java-object capabilities, using the OO capabilities is sufficient for the Java-COBOL interoperability. For additional Java-object capabilities you need the JNI services.

There are several ways to structure the OO application. You can either have COBOL invoking Java or Java invoking COBOL. When starting with COBOL, use the INVOKE statement to invoke a Java method. It is possible to start with COBOL program from both the z/OS UNIX side and the MVS. See the “Communicating with Java methods” topic in the Enterprise COBOL for z/OS programming guide.
The main method

To start with Java, there needs to be a ‘main’ method written. It can be written in Java or as a compiled COBOL class definition that contains a factory method called ‘main’ with the appropriate arguments.
Typically, when using OO COBOL, the easiest way for Java to invoke COBOL is via an OO COBOL ‘wrapper’ class, which calls the procedural COBOL logic and thus “wraps” it.

Since Java resides on the z/OS UNIX side, if you start with Java, all the application components (classes and executable DLL modules) must reside on the HFS. To launch Java, you can use the shell command prompt (running the java command) or with the BPXBATCH utility from JCL or TSO/E.

Working with ‘wrapper’ OO COBOL classes

A COBOL source file that contains a class definition is often the gateway between Java and COBOL

Once this COBOL source file is compiled, a Java file is generated on the USS side. This Java file contains the native method declaration based on the JNI rules. If, for example, the class had a method foo, then the generated Java code will have a declaration of a native method named Java_classname_foo. The generated Java code (.java file) needs to be compiled on the USS side by using the javac compiler to produce the .class file.

When using OO COBOL with class definition, there are these naming rules:

  • The name of the resulting DLL module needs to match the expected class name. If ClassName is the external class name, then the name of the DLL module must be libClassName.so, as this is the name expected in the generated Java file (as it uses System.loadLibrary method to load the DLL).
  • If the class is part of a package and thus there are periods in the external class name, the periods should be changed to underscores in the DLL module name (based on JNI naming rules).

Accessing JNI services

In COBOL, the JNI API is defined in the JNI copybook called JNI.cpy. The JNI copybook contains the definition of JNINativeInterface, the COBOL group structure that maps the JNI environment structure, which contains an array of function pointers for the JNI callable services. To facilitate access to JNI services, the JNI.cpy file needs be defined in the COBOL program under the LINKAGE SECTION. For example, if the file is named JNI:



Linkage section.
COPY “JNI”


Before you reference the contents of the JNI environment structure, you must code the following statements to establish its addressability:



Procedure division.
Set address of JNIEnv to JNIEnvPtr
Set address of JNINativeInterface to JNIEnv

The code sets the address of JNIEnv, a pointer data item that JNI.cpy provides, and JNIEnvPtr, the COBOL special register that contains the environment pointer. The JNIEnvPtr is implicitly defined as USAGE POINTER and should not be used as a receiving data item. Use this special register JNIEnvPtr to reference the JNI environment pointer to obtain the address for the JNI environment structure.

After the pointers are set, the JNI callable services can be accessed with CALL statements that reference the function pointers. The JNIEnvPtr special register is the first argument to the services that require the environment pointer, as shown in the following example:



01 InputArrayObj usage object reference jlongArray.
01 ArrayLen pic S9(9) comp-5.
. . .
Call GetArrayLength using by value JNIEnvPtr InputArrayObj
                                    returning ArrayLen

Compiling and linking for COBOL

To compile COBOL source code that uses JNI services or contains OO syntax such as INVOKE statements or class definitions (that is, COBOL code that directly communicates with Java):

  • Use the following compiler options: RENT, DLL, THREAD, and DBCS.
    The RENT and DBCS options are IBM-supplied defaults.
  • Set PGMNAME(LONGMIXED) for long name support.
  • Set the RECURSIVE attribute on COBOL classes and methods or on COBOL programs that invoke Java methods.

Compiling the COBOL with DLL affects the overall COBOL program structure. In general, DLL-linkage-built COBOL programs can only call out to other external DLL-linkage-built programs. Similarly, dynamic call built COBOL programs can only call out to other external dynamic call built programs. However, static linking of objects with either two of these external program call mechanisms is allowed. This provides the bridging between the DLL linkage that Java requires and the traditional COBOL dynamic call.

The link step creates an executable DLL module. It is required to link the object file with the following two DLL side files:

  • libjvm.x, which is provided with your IBM Java SDK
  • igzcjava.x, which is provided in the lib/ subdirectory of the COBOL directory in the HFS. The typical complete path is /usr/lpp/cobol/lib/igzcjava.x.
    This DLL side file is also available as the member IGZCJAVA in the SCEELIB PDS (part of Language Environment).

If the application starts with a Java program or the main factory method of a COBOL class, the XPLINK environment is automatically started by the java command that starts the JVM and runs the application.

If an application starts with a COBOL program that invokes methods on COBOL or Java classes, you must specify the XPLINK(ON) runtime option so that the XPLINK environment is initialized. XPLINK(ON) is not recommended as a default setting. Use XPLINK(ON) only for applications that specifically require it.

In older versions of COBOL, using the COPY statement required the compiler option ‘lib’. This option is not required in Enterprise COBOL v5 and up, and it is always in effect.

For more information, see “Compiling, linking, and running OO applications” in the Enterprise COBOL for z/OS Programming Guide.

Java and COBOL under z/OS UNIX

When you compile, link, and run OO applications in a z/OS UNIX environment, application components reside in the HFS. The compilation and linking is done using z/OS UNIX shell commands, and application is launched via a shell command prompt or with the BPXBATCH utility from JCL or TSO/E.

For compilation use: cob2 -c -qdll,thread
For linking use: cob2 -bdll

The -bdll option specifies that the executable module is to be a DLL. In addition:

  • The COBOL compiler uses the compiler options DLL, EXPORTALL, and RENT, which are required for DLLs.
  • The link step produces a DLL definition side file that contains IMPORT control statements for each of the names exported by the DLL.

You need to specify the include subdirectory of the JNI.cpy by using the -I option of the cob2 command or by setting the SYSLIB environment variable. The JNI.cpy resides under the include subdirectory of the COBOL installation directory (typically, /usr/lpp/cobol/include).

Example: Java calling procedural COBOL

This example demonstrates Java calling procedural COBOL with JNI service calls.

The COBOL program is compiled and linked as follows, where a typical installation path for COBOL is /usr/lpp/cobol and a typical installation path for Java is /usr/lpp/java. Replace with your own paths for your environment.


/usr/lpp/cobol/bin/cob2 -c -qdll,thread -I /usr/lpp/cobol/include stringTest.cbl

/usr/lpp/cobol/bin/cob2 -bdll -o libStringTest.so stringTest.o /usr/lpp/java/IBM/J7.1/bin/j9vm/libjvm.x /usr/lpp/cobol/lib/igzcjava.x -I /usr/lpp/cobol/include stringTest.cbl

stringTest.cbl

Process pgmname(longmixed),lib,dll,thread

       IDENTIFICATION DIVISION.
       PROGRAM-ID.    "Java_StringTest_printStrings" is recursive.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.

       INPUT-OUTPUT SECTION.
       DATA DIVISION.
       Local-storage section.
       01 I pic s9(9) binary.
       01 P pointer.
       01 SAelement pic s9(9) binary.
       01 SAelementlen pic s9(9) binary.
       01 Sbuffer pic X(50) .
       01 rc Pic s9(9) Comp-5.


       LINKAGE SECTION.
       01 SA  PIC S9(9) BINARY.
       01 SAlen pic s9(9) binary.
       01 ENV-PTR   USAGE POINTER.
       01 OBJECT-REF  PIC S9(9) BINARY.

       COPY JNI SUPPRESS.

       PROCEDURE DIVISION USING BY VALUE ENV-PTR //two JNI 'hidden' parms
                                     OBJECT-REF
                                     SA
                           RETURNING SAlen.

           Set address of JNIEnv to ENV-PTR
           Set address of JNINativeInterface to JNIEnv


           Call GetArrayLength using by value ENV-PTR SA
              returning SAlen
           Display " >> COBOL method entered array len: " SAlen

           Perform varying I from 0 by 1 until I = SAlen
              Call GetObjectArrayElement
                 using by value ENV-PTR SA I
                 returning SAelement
              Call "GetStringPlatformLength"
                 using by value ENV-PTR
                                SAelement
                                address of SAelementlen
                                0
                 returning rc
              Display "Returned from GetStringPlatformLength"
              If rc Not = zero Then
              Display "Error occurred retrieving len of jstring object"
              Stop run
              Else
              Display "The length of returned string is:"
              SAelementlen
              End-if
              Call "GetStringPlatform"
                 using by value ENV-PTR
                                SAelement
                                address of Sbuffer
                                length of Sbuffer
                                0
              Display Sbuffer(1:SAelementlen)
           End-perform

            .
           GOBACK.

The following Java program StringTest.java is compiled and run with the following commands:

/usr/lpp/java/IBM/J7.1/bin/javac StringTest.java
/usr/lpp/java/IBM/J7.1/bin/java -Djava.library.path=. StringTest

StringTest.java

class StringTest
{
   static {
     System.loadLibrary("StringTest");
   }

static native int printStrings(java.lang.String[] SA);

public static void main(String argv[]) throws Exception
   {
    int i;
    String[] a = new String[3];
    a[0] = "John";
    a[1] = "White";
    a[2] = "1234567890";
    StringTest st = new StringTest();
    i = StringTest.printStrings(a);
    System.out.println("Number of elements printStrings read:" +i);
    return;
   }
} 

Example: OO COBOL invoking Java using INVOKE

This example demonstrates how an object-oriented COBOL application invokes a Java program on UNIX System Services (USS).

For COBOL applications calling Java, you need the LIBPATH to point to the path for the Java libraries.

LIBPATH=/usr/lpp/java/IBM/J7.1/bin/classic:$LIBPATH

Also, use the _CEE_RUNOPTS environment variable to set the XPLINK(ON) option :

_CEE_RUNOPTS="XPLINK(ON)"

Exporting _CEE_RUNOPTS=”XPLINK(ON)” so that it is in effect for the entire z/OS UNIX shell session is not recommended, however. Suppose for example that an OO COBOL application starts with a COBOL program called App1. One way to limit the effect of the XPLINK option to the execution of the App1 application is to set the _CEE_RUNOPTS variable on the command-line invocation of App1Driver as follows:

_CEE_RUNOPTS="XPLINK(ON)" App1

The following example demonstrates how an OO COBOL application invokes a Java program by using INVOKE.

The COBOL program is compiled and linked as follows, where a typical installation path for COBOL is /usr/lpp/cobol and a typical installation path for Java is /usr/lpp/java. Replace with your own paths for your environment.

/usr/lpp/cobol/bin/cob2 -c -qthread,dll helloTest.cbl
/usr/lpp/cobol/bin/cob2 -bdll helloTest.cbl -o hello /usr/lpp/java/IBM/J7.1/bin/j9vm/libjvm.x /usr/lpp/cobol/lib/igzcjava.x -I /usr/lpp/cobol/include

helloTest.cbl

Process thread,pgmname(longmixed)
IDENTIFICATION DIVISION.
PROGRAM-ID. "HELLOWORLD" is recursive.
*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. RM-COBOL.
OBJECT-COMPUTER. RM-COBOL.
       
Repository.
    Class HelloJ is "HelloJ".
DATA DIVISION.
FILE SECTION.
       
PROCEDURE DIVISION.
       
MAIN-LOGIC SECTION.
BEGIN.
    DISPLAY " " .
    INVOKE HelloJ "sayHello".
    DISPLAY "Hello world!".
    STOP RUN.
MAIN-LOGIC-EXIT.
    EXIT.

The following HelloJ.java program is compiled and exported by using the following commands in USS:

/usr/lpp/java/IBM/J7.1/bin/javac HelloJ.java

export _CEE_RUNOPTS="XPLINK(ON)"
export LIBPATH=/usr/lpp/java/IBM/J7.1/bin:/usr/lpp/java/IBM/J7.1/bin/classic:$LIBPATH
export CLASSPATH=:$CLASSPATH

HelloJ.java

class HelloJ {
   public static void sayHello() {
     System.out.println("Hello World, from Java!");
   }
}

[{"Line of Business":{"code":"LOB61","label":"IBM Technology Lifecycle Services"},"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SGMV104","label":"IBM Support for Java (Oracle)"},"ARM Category":[],"Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"All Version(s)"}]

Document Information

Modified date:
22 February 2023

UID

ibm16320801