COBOL - Group home

An in-depth look at calling static Java methods from COBOL using the new non-OO COBOL/Java interoperability framework in Enterprise COBOL 6.4

  

In a previous post, I gave a high-level overview of the features of the new, non-OO COBOL/Java interoperability framework for batch and IMS environments that was introduced in Enterprise COBOL 6.4.

Towards a new COBOL/Java interoperability for batch and IMS environments
See also: Calling COBOL from Java using the non-OO COBOL/Java interoperability framework

In this post, I'd like to focus on one aspect of the new support in particular:

  • Using the CALL statement in a COBOL program to call a static method in a Java application

The discussion will focus on a simple "Hello, world!" program that can easily be extended to something more sophisticated, while at the same time providing a vehicle for an in-depth discussion of exactly how to build and launch such a program, both from z/OS UNIX and also JCL. For this discussion, we'll assume an AMODE 31 COBOL program and an AMODE 64 Java application, which is likely to be the most common configuration in practice.

Summary of goals

  • Demonstrate the ease of calling a static method in an AMODE 64 Java application from an AMODE 31 COBOL program using the COBOL CALL statement

  • Show the details of building and running this interoperable application from both z/OS UNIX and JCL. This includes examples of using the new cjbuild utility that builds the DLL that is associated with the COBOL/Java interoperable application

The one directory method

When I explore COBOL/Java interoperability using the new, non-OO interoperability framework, I like to do it from z/OS UNIX using what I call the "one directory" method. (Don't worry, we'll talk about how to run from JCL a little later.)

In the "one directory" method, you create a single directory on your z/OS UNIX file system where you put the COBOL and Java source files that you want to interoperate. Build and run commands are all executed from this directory, and all artifact files produced by the compiler are generated in this directory, as well, including the DLL generated for the application by the new cjbuild utility.

NOTE: The "one directory" method is convenient because all the files you need to work with when exploring are in one location, and you don't need to think about where the application DLL(s), Java .class files, and temporary artifact files generated by the compiler and cjbuild utility should be located. Everything is located conveniently in the current directory which is sufficient for exploration. For production, more careful consideration needs to be given to where these files will ultimately be located -- more on that later.

For this discussion, we'll assume this special "one directory" that we're going to use has the following path on your z/OS UNIX file system:

/home/userid/jtoc/ccj

If you're following along, it would be a good idea to ensure that this directory exists now on your system.

As mentioned before, we will use a simple "Hello, world!" program that is modified to allow the passing of some simple parameters in order to demonstrate the building/running of a COBOL/Java interoperable application where COBOL calls Java.

Files involved in a COBOL/Java interoperable application

An interoperable application consists of several pieces:

  • COBOL user source file(s)

  • Java user source file(s)

  • the artifact files generated by the COBOL compiler, which include COBOL "stub" files and possibly Java class files, which would happen if, for instance, any of the user COBOL programs contain the JAVA-SHAREABLE directive

  • the "methods" file, which is a file that contains 2 types of information: i) the name of all COBOL programs involved in the interopable application that contain either a JAVA-CALLABLE or JAVA-SHAREABLE directory; ii) the name of every static Java method called from a COBOL program in the interoperable application. The method name must be in the same form as the name used in the COBOL program (e.g., Java.<fully-qualified-class-name>.<static-method-name>.

  • the DLL for your interoperable application generated by the cjbuild utility.

NOTE: Associated with every COBOL/Java interoperable application is a DLL for that application that is needed at run time. This DLL consists of the glue code (or "stub" files) that are needed to connect COBOL and Java.

Note also that depending on how you structure your application, it's possible that you may decide to view your existing COBOL assets as consisting of multiple different "services" that can be leveraged from Java, with each service consisting of one or more COBOL files that is callable from Java, for example. In this case, cjbuild could be used to group the stub files associated with each independent service into its own DLL that can be used by many different interoperable applications. Each application would depend on more than one DLL in this case (i.e., there would be one DLL per service used by the application).

In the end, there is flexibility in how you build your application, but for our examples, we will build all stub files needed for a single interoperable application into a single DLL. This method is simple and should work fine, even in production. If using this method results in a single stub file being included in the DLLs of multiple different interoperable applications (because the associated COBOL program was used in each of those different interoperable applications), that is perfectly fine because stub files are only small pieces of code anyway.

COBOL program file prog1.cbl

       cbl pgmname(longmixed)
       identification division.
       program-id. 'prog1'.
       data division.
       working-storage section.
       01 num1 pic s9(9)v9(2) comp-3 value -12.36.
       01 num2 pic s9(9)v9(2) comp-3 value 123456789.
       01 msg pic x(24) value 'Hello, from COBOL prog1!'.
       procedure division.
       MainProgram.
           display '<<Entering COBOL program: prog1>>'
           call 'Java.myjavaapps.TestApp.doStuff' using num1 num2 msg
             on exception
               display 'Error calling myjavaapps.TestApp.doStuff'
           end-call
           display '<<Exiting COBOL program: prog1>>'
           goback.
       end program 'prog1'.

Important things to note about the COBOL program:

  • The program is compiled with the PGMNAME(LONGMIXED) option because the literal in the CALL statement is greater than 8 characters

  • One consequence of the PGMNAME(LONGMIXED) option is that it requires the name of the program to be a literal as opposed to a mnemonic, so in this case, the name of the program must be 'prog1' instead of simply prog1.

  • The CALL statement is using the ON EXCEPTION phrase (available since the April 2023 Enterprise COBOL 6.4 PTF), to catch exceptions generated during the call to Java. These exceptions may be system level exceptions in the JNI layer (e.g., the target method could not be found), or they could be exceptions thrown in user code on the Java side

Java program TestApp.java

package myjavaapps;

import java.math.*;
import com.ibm.jzos.ZUtil;

public class TestApp
{
  public static void doStuff(BigDecimal b1, BigDecimal b2, String msgFromCOBOL)
  {
    //ZUtil.redirectStandardStreams();
    System.out.println("<<Entering Java: TestApp>>");
    System.out.println("doStuff: Hello from TestApp.doStuff!");
    System.out.println("doStuff: Message from COBOL: " + msgFromCOBOL);
    System.out.println("doStuff: doStuff: num1 = " + b1);
    System.out.println("doStuff: doStuff: num2 = " + b2);
    System.out.println("<<Exiting Java: TestApp>>");
  }
}

Important things to note about the Java program:

  • In order to demonstrate calling a static method of a class that belongs in a package, we have made class TestApp part of a package called myjavaapps

  • The first line of code (commented out) to redirect output streams is useful when launching the program from JCL but is not needed when running from z/OS UNIX. This is discussed more below in the section showing the output from running the program.

Building and running on z/OS UNIX

In this section, we'll provide z/OS UNIX shell scripts that can be used to build/run the application from z/OS UNIX.

It should be noted that, in general, the way a non-OO COBOL/Java interoperable application is built is as follows:

  • user COBOL programs are compiled specifying the JAVAIOP option, but are not linked

  • the cjuild utility is run to build the DLL for the interoperable application

    • currently, this step requires an input file that lists all the COBOL programs that contain either the JAVA-SHAREABLE or JAVA-CALLABLE directive as well as the name of any static Java methods called from COBOL -- this is the so-called "methods" file

    • this step produces a "side deck" of exported DLL symbols that must be used in subsequent steps when linking the user COBOL programs

  • user COBOL programs are linked as normal with the exception that programs are now linked against the side deck generated in the previous step

"methods" file to use for cjbuild

The following is the contents of a file called methods that exists in the current directory in this example and is specified as input to the cjbuild utility (see the build.sh file below).

NOTE: the purpose of the "methods" file is to allow users to maintain a single directory (not talking about the "one directory" method here) that contains all the COBOL "stub" files for all COBOL programs in your shop that are involverd in COBOL/Java interoperability, and then define different interoperable applications from subsets of stub files, maintaining a different "methods" file for each interoperable application.

Here is the contents of the methods file (called "methods" in our directory) for this example:

Java.myjavaapps.TestApp.doStuff

build script build.sh

#!/bin/sh

export JAVA_HOME=/usr/lpp/java/J11.0_64
# ATTN: ensure the STEPLIB environment variable is set appropriately
# ATTN: ensure the directory containing cob2 is in your UNIX PATH

# STEP 1: Compile user COBOL programs, specifying JAVAIOP option
cob2 -c prog1.cbl "-qJAVAIOP(JAVA64)"

# STEP 2: Build stub file DLL using cjbuild
cjbuild -v -m MIX_31_64 -d . methods app1

# STEP 3: Link user programs
cob2 prog1.o -o prog1 libapp1.x

# STEP 4: Build Java parts of the application
${JAVA_HOME}/bin/javac -d . TestApp.java

Things to note about this build script:

  • Since we're using the "one directory" approach to explore COBOL/Java interoperability, the OUTPATH suboption of the JAVAIOP compiler option is not specified when compiling prog1 (it defaults to the current directory, as desired)

  • the JAVA64 suboption of the JAVAIOP option is specified in order to let the compiler know that this interoperable application is targeting AMODE 64 Java.

    • Note: the JAVA64 suboption is only supported when the LP(32) option is in effect. When LP(64) is in effect, it is assumed that AMODE 64 Java is the target.

  • the cjbuild command specifies the -m MIX_31_64 option as the build mode to let cjbuild know that we're creating an application that is a mix of AMODE 31 COBOL and AMODE 64 Java.

  • the location of the DLL produced by cjbuild (see -d option) is the current z/OS UNIX directory (that's just for convenience for this exploration). In reality, the location of the DLL can be any general z/OS UNIX path (and that path must be included in your LIBPATH environment variable at run time) or any PDS or PDSE data set (in this case the name of the data set must be specified to cjbuild in a format similar to the following //'USERID.COBOL.LOAD', and at run time that data set must be in your STEPLIB).

  • wherever cjbuild is run, a "side deck" of exported symbols for the DLL produced will be available in that directory with the file extension .x. This file is required during linking of the user COBOL program in a subsequent step (see STEP 3 in the above script).

    • NOTE: If alternatively the DLL is targeted for a data set named USER.COBOL.LOAD and the DLL is called LIBAPP1, then a side deck will also be created as a member named LIBAPP1 in a data set called USER.COBOL.EXP. That data set will be created if it does not already exist. This is useful when invoking the linker from JCL.

What to expect when running the build script

Before running the build script, ensure that the STEPLIB environment variable in your shell is set appropriately to point to your COBOL compiler and runtime, and ensure that your COBOL zFS install directory is included in your PATH environment variable. For example, your COBOL zFS install directory may be something such as /usr/lpp/cobol/igyv6r4.

Run the build.sh from the current directory by issuing the following command:

./build.sh

The following output should be produced:

 PP 5655-EC6 IBM Enterprise COBOL for z/OS  6.4.0 in progress ...
 End of compilation 1,  program prog1,  no statements flagged.

Running cjbuild COBOL/Java build tool
=====================================

 NATIVE_METHODS_FILE = methods
 DLL_BASE_NAME       = app1
 COBOL_DIR           = .
 DLL_OUT_DIR         = .
 JAVA_CLASS_DIR      = .
 JAVA_SRC_DIR        = .
 PACKAGE_NAME        = enterprise.COBOL
 INTEROP_MODE        = MIX_31_64

JAVA_HOME: /usr/lpp/java/J11.0_64
COBOL_INSTALL_DIR: /usr/lpp/cobol/igyv6r4

User env STEPLIB: TSC390.COBOL.IGY.V6R4M0.GOOD.SIGYCOMP:TSCTEST.COBRTE.NIGHTLY.SCEERUN2:TSCTEST.COBRTE.NIGHTLY.SCEERUN:TSCTEST.CEEZ240.SCEERUN:TSCTEST.CEEZ240.SCEERUN2

Including file in app DLL: Java.myjavaapps.TestApp.doStuff
# Compiling Java call stub method Java.myjavaapps.TestApp.doStuff
/usr/lpp/cobol/igyv6r4/bin/cob2 -qlist -qopt(1),nsymbol(national),dbcs -c /home/userid/jtoc/ccj/Java.myjavaapps.TestApp.doStuff.cbl -I /usr/lpp/cobol/igyv6r4/include
# Command return code 0

# Compiling WS access object invalidate routine
/usr/lpp/cobol/igyv6r4/bin/cob2 -qlist -qopt(1),nsymbol(national),dbcs -c igycjimc.cbl -I /usr/lpp/cobol/igyv6r4/include
# Command return code 0

# Compiling Call to JAVA exception check routine
/usr/lpp/cobol/igyv6r4/bin/cob2 -qlist -qopt(1),nsymbol(national),dbcs -c igycjest.cbl -I /usr/lpp/cobol/igyv6r4/include
# Command return code 0

# Linking native method DLL
/usr/lpp/cobol/igyv6r4/bin/cob2 -bdll -o ./libapp1.so Java.myjavaapps.TestApp.doStuff.o igycjimc.o igycjest.o /usr/lpp/java/J11.0_64/lib/j9vm/libjvm31.x /usr/lpp/cobol/igyv6r4/lib/igzxjni2.x
# Command return code 0

# Building java application files

cjbuild completed successfully.
===============================

At this point, at a minimum, the following files should be in your directory:

Java.myjavaapps.TestApp.doStuff.cbl*
Java.myjavaapps.TestApp.doStuff.lst*
Java.myjavaapps.TestApp.doStuff.o*
TestApp.java*
build.sh*
clean.sh*
igycjest.cbl
igycjest.lst*
igycjest.o*
igycjimc.cbl
igycjimc.lst*
igycjimc.o*
libapp1.so*
libapp1.x
methods*
myjavaapps/
myjavaapps/TestApp.class
prog1*
prog1.cbl*
prog1.lst*
prog1.o*
run.sh*

The user should not concern themselves with most files in this directory, which is largely a work directory for the compiler. The only files that the user needs to be aware of at the end is libapp1.so, which is the DLL for the application created by cjbuild, prog1, which is the COBOL executable, and myjavaapps/TestApp.class, which is the .class file for the user Java program TestApp.java. The remaining files are stub files (e.g., Java.myjavaapps.TestApp.doStuff.cbl), build artifact files (e.g., prog1.o, prog1.lst, etc.) and generated code files that were needed temporarily so they could be included in the DLL. In short, of the output files produced, only the DLL and .class files and prog1 are needed after the build operation. The rest of the generated files may be cleaned up. This is a task left for the user.

run script run.sh

#!/bin/sh

#ATTN: ensure the STEPLIB environment variable is set appropriately
export JAVA_HOME=/usr/lpp/java/J11.0_64
export _CEE_RUNOPTS="XPLINK(ON),POSIX(ON)"
export CLASSPATH=.:$CLASSPATH
export LIBPATH=.:${JAVA_HOME}/lib:${JAVA_HOME}/lib/j9vm:${LIBPATH}

prog1

Things to note about this run script:

  • We assume that we're targeting Java 11 here.

What to expect when running the application

To run the application, first ensure that your STEPLIB environment variable is set appropriately, then issue the following command:

./run.sh

The following output should be produced:

<<Entering COBOL program: prog1>>
<<Entering Java: TestApp>>
doStuff: Hello from TestApp.doStuff!
doStuff: Message from COBOL: Hello, from COBOL prog1!
doStuff: doStuff: num1 = -12.36
doStuff: doStuff: num2 = 123456789.00
<<Exiting Java: TestApp>>
<<Exiting COBOL program: prog1>>

Successfully generating output in production when using JCL:

When running the interoperable application in production using the JCL shown below, in order to get the output sent to either the job log or some other file, it is necessary to uncomment the following line in the above Java program:

//ZUtil.redirectStandardStreams();

Make sure you allocate STDOUT/STDERR ddnames in your JCL to the location that will receive the outpout in this case. See the JCL below for an example.

Making it work in production with JCL

Once you're done exploring and have written the final version of your interoperable application and tested it thoroughly, you'll eventually want to move it into production.

NOTE: For this discussion, we'll assume that, ultimately, you will want to build and launch your application from JCL.

It turns out that building your interoperable application for production is relatively easy. You just need to think about the locations of all the different types of files involved. These locations can be a mix of data sets and z/OS UNIX directories. The locations you are already using for most of those files in your system for non-COBOL/Java interoperable applications can continue to be used, typically. However, there are a few new locations to think about, as well, with the two most important ones being:

  1. The location of stub files output from the COBOL compiler which will be consumed by the cjbuild utility and built into a DLL.

  • Specify this path explicitly in a production build using the OUTPATH suboption of the JAVAIOP option

  1. The location of the DLL for your application built by cjbuild

  • we recommend it be a data set for production.

  • this data set just needs to be in your STEPLIB at run time

  • in the JCL below, see Step 2 for how a data set is specified as the output location for the DLL and see Step 5 for how that data set is included in the STEPLIB at run time

  1. The location of any .class files generated by cjbuild. This location should be a z/OS UNIX directory that is included in your Java CLASSPATH at run time. Note that the Java package name you choose for the generated Java code also plays a part in determining the location of the files.

NOTE: the default package name used for generated Java files is enterprise.COBOL. It is fine to accept that (and most defaults) when using the "one directory" method for exploration, but for production we strongly suggest you customize the package name (e.g, com.acme.interopapp1).

Let's go through all the different file locations relevant to your application and discuss in more detail:

  • COBOL user source files

    • in general, COBOL user source files can continue to reside where they always reside in your environment, which we assume in our JCL is in a data set

  • Java user source files

    • in general, Java user source files can continue to reside where they always reside in your environment, which we assume in our JCL is on the z/OS UNIX file system

  • the z/OS UNIX directory that will receive all the "stub" files and other artifact files generated by the Enterprise COBOL compiler when compiling programs that use non-OO COBOL/Java interoperability constructs.

    • this location is specifed using the OUTPATH suboption of the JAVAIOP compiler option, e.g., JAVAIOP(OUTPATH('output-path'),JAVA64)

    • this same path should be specified using -c or --coboldir option when running cjbuild

  • the z/OS work directory for building the application (NOTE: cjbuild requires a z/OS UNIX work directory even when run from JCL)

    • this is the directory from which cjbuild is run. Many of the work files it generates (files that are generally not relevant for users) will be generated here

    • it is strongly recommended that in production, your work directory is different from the location of the stub files and the location were class files are output (unlike the "one directory method")

  • output location for COBOL program objects corresponding to user COBOL programs

    • in general, COBOL program objects can be stored in the same locations they are always stored, which is assumed to be data sets in the JCL below

    • NOTE: For Java-calls-COBOL scenarios, this location must be a data set because that scenario depends on the COBOL dynamic calling mechanism.

  • the output location that will receive the DLL for your interoperable application as mentioned previously, we recommend the location for the DLL be a data set the location of the data set is specified using the -d or --dlloutdir option of the cjbuild utility, e.g,. -d "//'USERID.COBOL.LOAD'"

  • the output location that will receive .class files generated by cjbuild this location can be specified to cjbuild using the -j or --javaclassdir option of the cjbuild utility also be sure to specify the -p or --pkgname option of cjbuild to indicate the Java package with which the generated Java code will be associated.

Setting these locations in the JCL sample code

The JCL sample is written to allow users to specify all the relevant locations (and a few more not discussed above) using JCL variables defined at the top of the JCL.

The JCL contains detailed descriptions of every variable you should set, but the most relevant ones are:

  • JAVAHOME - the location on z/OS UNIX of your Java SDK (this should be whatever you set the JAVA_HOME environment variable to

  • ZFSCBDIR - the location of your COBOL zFS install directory

  • OUTDIR - the location of stub files and other artificat files produced by the Enterprise COBOL compiler (should correspond to the OUTPATH subopion of the JAVAIOP compiler option used to build your COBOL files)

  • WORKDIR - the z/OS UNIX directory from which cjbuild will be run

  • CLASSOUT - the z/OS UNIX directory where your .class files will be placed by cjbuild.

  • PKGNAME - the Java package with which to associated all generated Java files

  • DLLNAME - the suffix of the DLL file created by the cjbuild - when the DLL is targeted to a data set, the name specified must be 5 characters or less, as the prefix 'LIB' will be pre-pended to the provided name, and there is a maximum of 8 characters in a data set member name.

See the JCL for more details about how to set these variables, and replace any instances of TREMAIN or tremain in the paths or data set names with your own userid.

Related links

  1. New blog article: An in-depth look at Java calling COBOL using the new non-OO COBOL/Java interoperability framework (includes JCL)
  2. COBOL/Java interoperability outside the object-oriented COBOL framework

  3. Calling static Java methods from COBOL

  4. Mapping between COBOL and Java data types in the non-OO COBOL/Java interoperability framework

JCL for build and run

//TREMAIN2 JOB 'JAVA/COBOL interop',
// MSGLEVEL=(1,1),MSGCLASS=S,CLASS=C,NOTIFY=&SYSUID,
// REGION=0M
//********************************************************************/
//*                IBM Enterprise COBOL for z/OS                     */
//********************************************************************/
//********************************************************************/
//*                                                                   *
//*      LICENSED MATERIALS -- PROPERTY OF IBM.                       *
//*                                                                   *
//*      5655-EC6                                                     *
//*      COPYRIGHT IBM CORP. 2024                                     *
//*                                                                   *
//*      US GOVERNMENT USERS RESTRICTED RIGHTS - USE, DUPLICATION OR  *
//*      DISCLOSURE RESTRICTED BY GSA ADP SCHEDULE CONTRACT WITH IBM  *
//*      CORP.                                                        *
//*                                                                   *
//********************************************************************/
//*********************************************************************
//*
//*  Sample JCL for building and running a non-OO COBOL/Java
//*  interoperable application starting on the COBOL side with
//*  COBOL calling a static Java method.
//*
//*********************************************************************
//*
//*  Prerequisites:
//*
//*  1) User COBOL source files must be copied to data set:
//*     USER.COBOL.COBOL.
//*
//*  2) A PDSE data set named USER.COBOL.OBJECT must exist
//*     to receive object program files generated by the COBOL
//*     compiler.
//*
//*  3) A PDSE data set USER.COBOL.LOAD must exist to receive
//*     program object files. These program objects come from
//*     linking object files from user programs and also from
//*     linking the object files associated with stub programs
//*     into a DLL.
//*
//*     A PDS data set (FB 80) called USER.COBOL.EXP must also
//*     exist prior to running this JCL.  This data set will
//*     receive the side deck for the DLL produced by cjbuild
//*     and is used during subsequent link steps.
//*
//*  4) You must ensure the following JCL variables are set below,
//*     which control various build parameters. This includes the
//*     z/OS UNIX directory into which stub files will be generated
//*     by the compiler and the z/OS UNIX work directory to be
//*     used by the cjbuild utility. The cjbuild utility uses this
//*     directory to build the DLL needed for the interoperable
//*     application.
//*
//*     For this sample, we assume the z/OS UNIX directory
//*     structure to be used for building the application is as
//*     follows:
//*
//*     /home/tremain/jtoc/test/simple/ccj/work
//*       - main work directory
//*     /home/tremain/jtoc/test/simple/ccj/out
//*       - stub file output directory (compiler places stub
//*         files here)
//*     /home/tremain/jtoc/test/simple/ccj/class
//*       - directory where cjbuild places any generated .class
//*         files
//*
//*     The following variables must be set:
//*
//*     JAVAHOME
//*     ZFSCBDIR
//*     OUTDIR
//*     INTMODE
//*     WORKDIR
//*     CLASSOUT
//*     JVSRCOUT
//*     PKGNAME
//*     DLLNAME
//* 
//*     See below for detailed descriptions of how to set these
//*     variables. Example values are provided.
//*
//*     NOTE: z/OS UNIX directories for stub files and the work
//*           directory for cjbuild do not have to exist.  The
//*           JCL below create the directories if they do not
//*           exist prior to running this job (see STEP 0).
//*
//*  5) The contents of the "methods" file, used as input to the
//*     cjbuild utility, is specified in the instream data
//*     set STDPARM in STEP 2 that contains the UNIX commands
//*     that are required to run the cjbuild utility.  To change
//*     the list of methods whose associated stub files should
//*     be included in the DLL, change the "echo" commands that
//*     are used to append each method name to the methods file.
//*
//*********************************************************************
// EXPORT SYMLIST=*
//*********************************************************************
//* Set compiler and run time library levels below
//*******************************************************************
//         SET LIBPRFX='TSCTEST.CEEZ240'
//         SET LNGPRFX='TSC390.COBOL.IGY.V6R4M0.GOOD'
//*******************************************************************
//* JAVAHOME: Set this to your Java home directory. This will be used
//*           to set the JAVA_HOME environment variable needed by
//*           Java operations
//*******************************************************************
//         SET JAVAHOME='/usr/lpp/java/J11.0_64'
//*******************************************************************
//* ZFSCBDIR: This should point to the location of your COBOL zFS
//*          install directory (e.g., /usr/lpp/cobol/igyv6r4)
//*
//*          The bin directory of this directory is where the cob2
//*          and cjbuild utilities reside.  They will be needed
//*          when building the interoperable application
//*******************************************************************
//         SET ZFSCBDIR='/home/tremain/cobol/igyv6r4'
//*******************************************************************
//* OUTDIR: Set this to the z/OS UNIX directory that will receive
//*         the stub files and other artifact files generated by
//*         the COBOL compiler compiling programs that contain
//*         the JAVA-CALLABLE and JAVA-SHAREABLE directives or
//*         programs that make calls to Java using a CALL statement
//*         of the form:
//*
//*             CALL 'Java.class-name.static-method-name' ...
//*
//*         The cjbuild uility (see STEP 2) will get the files from
//*         this directory (see --coboldir option) so the directory
//*         specified for OUTDIR should be used both in the OUTPATH
//*         suboption of JAVAIOP when compiling the user COBOL
//*         program as well as the --coboldir option of cjbuild.
//*******************************************************************
//         SET OUTDIR='/home/tremain/jtoc/ccj/out'
//*******************************************************************
//* INTMODE: Set this to the interoperability mode to be used
//*          by your application (needed by the cjbuild utility)
//*
//*          PURE31 - AMODE 31 COBOL, AMODE 31 Java
//*          PURE64 - AMODE 64 COBOL, AMODE 64 Java
//*          MIX_31_64 - AMODE 31 COBOL, AMODE 64 Java
//*
//*          NOTE: if you specify MIX_31_64, you must add the
//*                JAVA64 suboption to the JAVAIOP option of
//*                all related user COBOL programs.
//*******************************************************************
//         SET INTMODE='MIX_31_64'
//*******************************************************************
//* WORKDIR: Set this to the z/OS UNIX directory to be used as a work
//*          directory for building the interoperable application.
//*          cjbuild will be run from this directory and basic
//*          artifact files it generates (object files, listing,
//*          etc.) go to this directory automatically.
//*
//*          This is where the "methods" file will be located for
//*          this sample.
//*******************************************************************
//         SET WORKDIR='/home/tremain/jtoc/test/simple/ccj/work'
//*******************************************************************
//* CLASSOUT: Set this to the directory you want any .class files
//*           generated by cjbuild to go.  These are the .class
//*           files for Java accessor classes for JAVA-SHAREABLE
//*           items, and also .class files for any Java files
//*           generated by cjbuild, such as cobol.java, progs.java,
//*           and strg.java.
//*******************************************************************
//         SET CLASSOUT='/home/tremain/jtoc/test/simple/ccj/class'
//*******************************************************************
//* JVSRCOUT: Set this to the directory you want any .java files
//*           generated by cjbuild to go (cobol.java, progs.java,
//*           strg.java).
//*******************************************************************
//         SET JVSRCOUT='/home/tremain/jtoc/test/simple/ccj/work'
//*******************************************************************
//* PKGNAME:  Set this to the name of the Java package that you
//*           want the 'progs' and 'strg' class to be a part of.
//*           The default is enterprise.COBOL.  This affects how
//*           COBOL programs are invoked from Java.  e.g., if the
//*           package name is enterprise.COBOL., then you would
//*           invoke a COBOL program called PROG1 as follows:
//*
//*           enterprise.COBOL.progs.PROG1();
//*******************************************************************
//         SET PKGNAME='enterprise.COBOL'
//*******************************************************************
//* DLLNAME: The name of the DLL that cjbuild creates and which
//*          is needed at runtime when interoperable activities
//*          occur in the application.
//*
//*          If the DLL is targeted for a data set (which is
//*          the usual way, see --dlloutdir option of cjbuild)
//*          then the DLL will be stored as a member of the
//*          specified data set, and is called LIB<DLLNAME>.
//*
//*          If the DLL is targeted to the z/OS UNIX file system,
//*          it will be called lib<dllname>.so.
//*******************************************************************
//         SET DLLNAME=APP1
//*******************************************************************
//PROCLIB JCLLIB ORDER=(&LNGPRFX..SIGYPROC)
//*******************************************************************
//*******************************************************************
//* STEP 0: Ensure required z/OS UNIX directories exist
//*******************************************************************
//*******************************************************************
//CRDIRS   EXEC PGM=BPXBATCH
//STDPARM  DD *,SYMBOLS=EXECSYS
SH mkdir -p &OUTDIR;
mkdir -p &WORKDIR;
mkdir -p &CLASSOUT;
mkdir -p &JVSRCOUT
/*
//STDOUT   DD SYSOUT=*
//STDERR   DD SYSOUT=*
//STDENV   DD *
NO_CUSTOM_SHELL=1
/*
//*******************************************************************
//*******************************************************************
//* STEP 1: Compile user COBOL programs, specifying JAVAIOP option
//*******************************************************************
//*******************************************************************
// SET INFILE='PROG1'
//*******************************************************************
// IF (CRDIRS.RUN AND (CRDIRS.RC EQ 0)) THEN
//COBOL1   EXEC IGYWC,
//  LNGPRFX=&LNGPRFX,
//  LIBPRFX=&LIBPRFX,
//  PARM.COBOL='OPTFILE'
//SYSLIB   DD DSN=&SYSUID..COBOL.COBOL,DISP=SHR
//SYSIN    DD DSN=&SYSUID..COBOL.COBOL(&INFILE),DISP=SHR
//SYSLIN   DD DSN=&SYSUID..COBOL.OBJECT(&INFILE),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSOPTF  DD *,SYMBOLS=EXECSYS
 JAVAIOP(OUTPATH('&OUTDIR'),JAVA64)
/*
//SYSOUT   DD SYSOUT=*
// ENDIF
//*******************************************************************
//* STEP 2: Build stub file DLL using cjbuild
//*******************************************************************
// IF (COBOL1.COBOL.RUN AND (COBOL1.COBOL.RC EQ 0)) THEN
//CJBUILD EXEC PGM=BPXBATCH
//STDPARM  DD *,SYMBOLS=EXECSYS
SH export JAVA_HOME=&JAVAHOME;
PATH=.:/bin:/usr/bin:/usr/local/bin:&ZFSCBDIR/bin;
cd &WORKDIR;

echo Java.myjavaapps.TestApp.doStuff > methods;

&ZFSCBDIR/bin/cjbuild
 -v
 --coboldir &OUTDIR
 --dlloutdir "//'&SYSUID..COBOL.LOAD'"
 --pkgname &PKGNAME
 --javaclassdir &CLASSOUT
 --javasrcdir &JVSRCOUT
 --mode &INTMODE
 methods &DLLNAME
/*
//STEPLIB DD  DSN=&LNGPRFX..SIGYCOMP,DISP=SHR
//        DD  DSN=&LIBPRFX..SCEERUN,DISP=SHR
//        DD  DSN=&LIBPRFX..SCEERUN2,DISP=SHR
//STDOUT  DD SYSOUT=*
//STDERR  DD SYSOUT=*
//STDENV  DD *,SYMBOLS=EXECSYS
NO_CUSTOM_SHELL=1
_C89_LSYSLIB=&LIBPRFX..SCEELKED
/*
// ENDIF
//*******************************************************************
//*******************************************************************
//* STEP 3: Link user programs.  User programs may call out to
//*         programs in the DLL produced by cjbuild in STEP 2
//*         thus should be linked with the side deck produced in
//*         that step
//*******************************************************************
//*******************************************************************
// SET INFILE='PROG1'
//*******************************************************************
// IF (CJBUILD.RUN AND (CJBUILD.RC EQ 0)) THEN
//LKED1 EXEC PGM=IEWL,
// PARM='RENT,LIST,LET,DYNAM(DLL),CASE(MIXED)'
//SYSLIB DD DSN=&LIBPRFX..SCEELKED,DISP=SHR
//       DD DSN=&LIBPRFX..SCEELKEX,DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSTERM DD SYSOUT=*
//SYSLMOD DD DSN=&SYSUID..COBOL.LOAD(&INFILE),DISP=SHR
//SYSDEFSD DD DUMMY
//OBJMOD DD DSN=&SYSUID..COBOL.OBJECT(&INFILE),DISP=SHR
//SIDEDK DD DSN=&SYSUID..COBOL.EXP(LIB&DLLNAME),DISP=SHR
//SYSLIN DD *
 INCLUDE OBJMOD
 INCLUDE SIDEDK
/*
// ENDIF
//*******************************************************************
//*******************************************************************
//* STEP 4: Builds Java application
//*******************************************************************
//*******************************************************************
// IF (LKED1.RUN AND (LKED1.RC EQ 0)) THEN
//BLDJAVA EXEC PGM=BPXBATSL
//STDPARM DD *,SYMBOLS=EXECSYS
PGM &JAVAHOME/bin/javac
 -d &CLASSOUT
 &WORKDIR/TestApp.java
/*
//STDOUT  DD SYSOUT=*
//STDERR  DD SYSOUT=*
//STDENV  DD *,SYMBOLS=EXECSYS
JAVA_HOME=&JAVAHOME
CLASSPATH=.:&CLASSOUT
PATH=.:/bin:/usr/bin:/usr/local/bin:&ZFSCBDIR/bin
/*
// ENDIF
//*******************************************************************
//*******************************************************************
//* STEP 5: Run COBOL/Java interoperable application
//*******************************************************************
//*******************************************************************
// IF (BLDJAVA.RUN AND (BLDJAVA.RC EQ 0)) THEN
//GO      EXEC PGM=PROG1
//STEPLIB DD DSN=&SYSUID..COBOL.LOAD,DISP=SHR
//        DD DSN=&LNGPRFX..SIGYCOMP,DISP=SHR
//        DD DSN=&LIBPRFX..SCEERUN,DISP=SHR
//        DD DSN=&LIBPRFX..SCEERUN2,DISP=SHR
//STDOUT  DD SYSOUT=*
//STDERR  DD SYSOUT=*
//CEEOPTS DD *,SYMBOLS=EXECSYS
POSIX(ON)
ENVAR(
"JAVA_HOME=&JAVAHOME",
"PATH=.:/bin:/usr/bin:/usr/local/bin",
"CLASSPATH=&CLASSOUT",
"LIBPATH=&JAVAHOME/lib:&JAVAHOME/lib/j9vm")
/*
// ENDIF

Comments

Fri March 08, 2024 02:48 AM

Many thanks for this great article! Best description of how to call Java from COBOL I have seen so far.

Wed February 28, 2024 06:41 AM

I was able to run this sample via JCL but only after updating the Java program source and adding "throws Exception" to the "public static" decleration.

I ran the sample on z/os 2.5 with Java 8.