Page 1 of 1

Setting up JNI for calling Java from COBOL on z/OS

Posted: Wed Nov 15, 2023 9:57 pm
by danik1956
I have been looking at the following IBM sample program:

https://www.ibm.com/docs/en/cobol-zos/6 ... ol-program

To compile the sample program there is a required COPY named JNI.
JNI.cpy is in the z/OSĀ® UNIX file system in the include subdirectory of the COBOL install directory (typically /usr/lpp/cobol/include).

What USS file system should be mounted to have access to this JNI source file ?

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 16, 2023 5:28 am
by zum13
Hello.

I believe the IBM suggested default is "IGYvrm.HFS", but that may not necessarily be where it is on your local installation. You'll need your friendly local sysprogs to mount it if it's not already there, plus I'd suggest that it also needs to be added to the "BPXPRMxx" member in the parmlib so that it's automatically mounted after an IPL.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 16, 2023 10:52 pm
by DB2 Guy
To have access to the JNI source file located in the z/OS UNIX System Services (USS) file system at /usr/lpp/cobol/include, you need to mount the USS file system that corresponds to the path /usr/lpp/cobol. In z/OS, USS file systems are typically mounted under the /u directory.

Therefore, you should mount the USS file system for /usr/lpp/cobol to the appropriate directory under /u. The specific mount point depends on your system configuration, but it could be something like /u/lpp/cobol or a similar path. Once the file system is mounted, you should be able to access the JNI source file at the specified path.

Here's a general example of how you might mount the file system:

Code: Select all

mount -V cifs -o username=<your_username>,password=<your_password> //your_zos_system/usr/lpp/cobol /u/lpp/cobol
Replace <your_username> and <your_password> with your actual credentials, and adjust the mount point (/u/lpp/cobol) based on your system's configuration.

After the mount is successful, you should be able to navigate to /u/lpp/cobol/include and access the JNI source file (JNI.cpy)

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 16, 2023 10:55 pm
by danik1956
Thanks. I found the relevant ZFS file at "IGY630.ZFS" and mounted it as /usr/lpp/cobol (read-only)

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Sat Nov 18, 2023 12:33 am
by Anuj Dhawan
Thanks for sharing what worked for you, appreciate it.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Mon Nov 20, 2023 9:00 pm
by danik1956
One more question related to using JNI with native COBOL on z/OS.
The IBM COBOL version 6.3 doc states that a 64-bit COBOL program can only call Java using JNI and not via INVOKE.
Can someone point me at some example for using JNI to call Java from a COBOL batch program compiled with LP(64) option ?

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Tue Nov 21, 2023 8:58 am
by zum13
I'll admit that the last time I dabbled with COBOL and Java together, the COBOL compiler hadn't been updated to support 64 bits! It appears that the COBOL to JNI interface has yet to receive an update.

I've not seen any examples of how to call Java methods directly using the JNI services from COBOL, but there shouldn't be a problem finding in C/C++ examples which show how it's done (they don't have to be mainframe-specific). For instance, this page has an example in section 4.2 which shows the technique for making a method call:

https://www.baeldung.com/jni

There's also the JNI specification which lists the API functions including a number concerned with the calling of Java methods all of which appear to be listed in the JNI.cpy file. It should just be a matter of adjusting the calling syntax for COBOL.

https://docs.oracle.com/en/java/javase/ ... ce-methods

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 23, 2023 3:58 pm
by DB2 Guy
Calling Java from COBOL on z/OS using JNI involves several steps. Below is a high-level outline of the process, and I'll also provide a simple example:
  1. Write the Java Class:
    Create a simple Java class that you want to call from COBOL. Save it as a .java file. For example:

    Code: Select all

    // HelloWorld.java
    public class HelloWorld {
        public static void sayHello() {
            System.out.println("Hello from Java!");
        }
    }
    
  2. Compile the Java Class:
    Compile the Java class using the javac command:

    Code: Select all

    javac HelloWorld.java
    This will generate a HelloWorld.class file.
  3. Generate Header File:
    Use the javah command to generate the header file for the Java class:

    Code: Select all

    javah HelloWorld
    This will generate a HelloWorld.h file.
  4. Write COBOL Program:

    Write a COBOL program that includes the generated header file and calls the Java method using JNI. Here is a simple example:

    Code: Select all

    IDENTIFICATION DIVISION.
    PROGRAM-ID. HelloWorldCOBOL.
    ENVIRONMENT DIVISION.
    DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 JAVA-ENVIRONMENT      POINTER.
    01 JAVA-CLASS           POINTER.
    01 METHOD-ID            POINTER.
    
    LINKAGE SECTION.
    01 RETURN-CODE          PIC S9(9) COMP.
    
    PROCEDURE DIVISION USING RETURN-CODE.
    
        CALL 'JNI_CreateJavaVM' USING
            BY REFERENCE JAVA-ENVIRONMENT
            BY VALUE 0
            BY REFERENCE 0.
    
        CALL 'JNI_FindClass' USING
            BY REFERENCE JAVA-ENVIRONMENT
            BY DESCRIPTOR 'HelloWorld'
            RETURNING JAVA-CLASS.
    
        CALL 'JNI_GetStaticMethodID' USING
            BY REFERENCE JAVA-ENVIRONMENT
            BY REFERENCE METHOD-ID
            BY REFERENCE JAVA-CLASS
            BY DESCRIPTOR 'sayHello()V'.
    
        CALL 'JNI_CallStaticVoidMethod' USING
            BY REFERENCE JAVA-ENVIRONMENT
            BY REFERENCE JAVA-CLASS
            BY REFERENCE METHOD-ID.
    
        CALL 'JNI_DestroyJavaVM' USING
            BY REFERENCE JAVA-ENVIRONMENT.
    
        EXIT PROGRAM.
    END PROGRAM HelloWorldCOBOL.
    


    This example assumes that the JVM is initialized, the Java class is found, and the method is called using JNI.
  5. Compile COBOL Program:
    Compile the COBOL program using the 64-bit compiler options:

    Code: Select all

    cob2 -x -C LP(64) HelloWorldCOBOL.cbl
  6. Run COBOL Program:
    Run the compiled COBOL program. Ensure that the Java class (HelloWorld.class) is in the classpath.

    Code: Select all

    ./HelloWorldCOBOL
This is a basic example, and in a real-world scenario, you would need to handle errors, manage memory, and possibly pass parameters between COBOL and Java. Additionally, ensure that your COBOL environment is set up correctly for 64-bit execution and JNI usage.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 23, 2023 4:26 pm
by danik1956
Thanks for the detailed response.
One question relating to binding the COBOL program with the required JNI callable services.
What should be included in the BIND step so that external names like 'JNI_CreateJavaVM' would get resolved ?

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Thu Nov 23, 2023 5:25 pm
by DB2 Guy
When you are binding a COBOL program that calls external functions like JNI_CreateJavaVM (Java Native Interface) from a shared library or DLL (Dynamic Link Library), you need to provide the necessary information for the linker to resolve these external names. This typically involves specifying the library or DLL that contains the implementation of the JNI functions.

Here is an example of what the BIND step might look like in a typical scenario. Note that the exact details can depend on your platform and toolchain:

Code: Select all

cobc -o YourProgram -L/path/to/java/library -ljvm YourProgram.cbl
Explanation:

-o YourProgram: Specifies the output executable name.
-L/path/to/java/library: Informs the linker where to find the Java library or DLL. You should replace /path/to/java/library with the actual path on your system.
-ljvm: Specifies the name of the library to link against. In this case, it's assumed that libjvm.so (on Unix-like systems) or jvm.dll (on Windows) is the library containing JNI functions.

Keep in mind that the specifics can vary based on your COBOL compiler and the platform you are working on. The above example assumes a Unix-like environment; for Windows, you might need to use a different syntax and possibly different libraries.

Also, make sure that the Java library containing the JNI functions is in the system library path or provide the full path to the library. Additionally, if there are other dependencies, you might need to include them in the BIND step.

Consult your COBOL compiler documentation for platform-specific details on linking external libraries.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Sun Nov 26, 2023 1:16 pm
by danik1956
"When you are binding a COBOL program that calls external functions like JNI_CreateJavaVM (Java Native Interface) from a shared library or DLL (Dynamic Link Library), you need to provide the necessary information for the linker to resolve these external names. This typically involves specifying the library or DLL that contains the implementation of the JNI functions."

This is exactly what I am trying to understand. On z/OS (not USS) when linking a COBOL program that is using JNI functions, where are these JNI extearnal names resolved from ?

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Mon Nov 27, 2023 3:11 am
by zum13
The linkage editor can read from USS, it's just a matter of providing the appropriate "INCLUDE" statements. I've managed to dig out a rather old example of linking a JNI program from my brief period of tinkering with it several years ago:

Code: Select all

 INCLUDE NCAL(TESTPROG)                               
 INCLUDE '/usr/lpp/java/IBM/J1.3/bin/classic/libjvm.x'
 INCLUDE '/usr/lpp/cobol/lib/igzcjava.x'              
 NAME TESTPROG(R)                                     
According to the docs, "igzcjava.x" should still be where it's specified above. The location of "libjvm.x" will depend upon which version of Java you are using.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Mon Nov 27, 2023 6:12 pm
by Anuj Dhawan
On z/OS, when linking a COBOL program that calls external functions like JNI_CreateJavaVM from a shared library or DLL, the resolution of these JNI external names is typically handled through the linkage editor, which is responsible for combining object modules and resolving external references.

In z/OS, the linkage editor resolves external names by searching specified libraries or explicitly provided modules in the link-edit JCL (Job Control Language). When you bind a COBOL program that uses JNI functions, you need to ensure that the JNI library or DLL containing the implementation of the JNI functions is included in the link-edit process.

As zum13 said, You specify the necessary information for the linker in the link-edit JCL by providing a library or DLL as input to the linkage editor. This can be done using statements like INCLUDE or LIB in the JCL to specify the libraries to be searched for unresolved external references.

Here's a simplified example of what the link-edit JCL might look like:

Code: Select all

//LINKJOB   JOB ...
//STEP1     EXEC PGM=IEWL
//SYSLST    DD  SYSOUT=A
//SYSUT1    DD  *
  //COBOL OBJECT MODULES
//SYSUT2    DD  *
  //ADDITIONAL OBJECT MODULES
//SYSUT3    DD  *
  //JNI LIBRARY OR DLL
//SYSIN     DD  *
  ENTRY COBOL_PROGRAM
  INCLUDE JNI_LIBRARY
  ...
/*

In the above example, COBOL_PROGRAM is the entry point of your COBOL program, and JNI_LIBRARY is the library or DLL that contains the implementation of the JNI functions. The INCLUDE statement ensures that the JNI functions are resolved during the link-edit process.

Make sure to replace placeholders like COBOL_PROGRAM and JNI_LIBRARY with the actual names used in your environment.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Fri Dec 29, 2023 12:24 pm
by danik1956
Here is the JCL we have used to compile & bind a COBOL program that invokes JNI services:

//RUN EXEC IGYQCB,LIBPRFX=CEE,LIBPRF1=CEE,
// PGMLIB='YOSI.TEST.LIB',GOPGM=HLWRLD64
//COBOL.SYSIN DD DSN=Z2V10.SAMP.LIB(CBLB#JNI),DISP=SHR
//COBOL.SYSLIB DD DSN=Z2V10.SAMP.LIB,DISP=SHR
// DD DSN=DANI.SOURCE.LIB,DISP=SHR
//COBOL.SYSLIN DD DSNAME=&&OBJECT(HLWRLD64),
// DISP=(NEW,PASS),SPACE=(CYL,(1,1,1)),DCB=(LRECL=80,RECFM=FB)
//BIND.OBJMOD DD DSN=&&OBJECT,DISP=(OLD,DELETE)
//BIND.SYSLIN DD *
INCLUDE OBJMOD(HLWRLD64)
INCLUDE '/usr/lpp/java/J8.08.15/J8.0_64/bin/j9vm/libjvm.x'
INCLUDE '/usr/lpp/IBM/cobol/igyv6r3/lib/igzcjava.x'
//

The result:

-STEPNAME PROCSTEP RC
-RUN COBOL 00
-RUN BIND 00

Compiler options used: cbl dll,lp(64),rent,pgmname(longmixed)

First JNI call is successfull:

CALL "JNI_CreateJavaVM"
USING JVM-PTR ENV-PTR VM-INIT-ARGS
RETURNING INTO RC2.
RETURNING RC2.

DISPLAY "rc2 " RC2 " " JVM-PTR " " ENV-PTR
IF RC2 NOT = 0
GOBACK
END-IF


The problem we have is related to the 2ed JNI call:


CALL FindClass USING BY VALUE ENV-PTR
CLASS-NAME-PTR
RETURNING MY-CLASS-REF.
DISPLAY "PNT1B"
DISPLAY "1." MY-CLASS-REF.
IF MY-CLASS-REF = 0
GOBACK
END-IF


This call returns null value although the requested java class exists as a jar file in the folder pointed by env
variable CLASSPATH.

There is nothing in STDOUT & STDERR datasets.

My question is how to turn on some tracing options so we can see why the FindClass JNI call is failing.

Re: Setting up JNI for calling Java from COBOL on z/OS

Posted: Fri Dec 29, 2023 8:11 pm
by zum13
The "FindClass" function does return an exception, but you need to explicitly call the exception's "printStackTrace" method in order to get the output for it. However, first things first; what does your working storage look like?