Consuming a Web Service from Batch — Calling Java from COBOL (MVS)

By | July 30, 2013

This is a application development pattern sample demonstrating how a batch COBOL program might consume a Web Service by invoking methods on java objects. This procedure might be an alternative to the often used design pattern which is to code the batch COBOL program to call a CICS program using EXCI and the CICS program in turn invoking the web service and passing the response back to the batch program. The tooling provided by CICS makes this pattern fairly easy to develop. Instead, here we leverage the wsimport utility provided by the Java JDK to do all the java coding for us and then invoke the java classes/methods from the generated web service client by a batch COBOL program.

In the demonstration, we are using a web service provided by Aonaware that does a dictionary lookup on The web service is described on this web page.


Logon to the z/OS Unix Systems Services shell via telnet or TSO OMVS command.
Create a directory for this project.

$ mkdir DictService

Build the java artifacts for the desired webservice from the WSDL using the wsimport utility.

$ cd DictService
$ wsimport -s . ""

This command generates java source code and compiles the source. The current working directory is used as a base for the package subdirectories. The artifacts generated from this WSDL will be in subdirectory com/aonaware/services/webservices:

$ ls com/aonaware/services/webservices/
ArrayOfDefinition.class          DictionaryListExtended.class
ArrayOfDictionary.class          DictionaryListExtendedResponse.class
ArrayOfDictionaryWord.class      DictionaryListResponse.class
ArrayOfStrategy.class             DictionaryWord.class
Define.class                               Match.class
DefineInDict.class                   MatchInDict.class
DefineInDictResponse.class       MatchInDictResponse.class
DefineResponse.class               MatchResponse.class
Definition.class                       ObjectFactory.class
DictService.class                     ServerInfo.class
DictServiceHttpGet.class         ServerInfoResponse.class
DictServiceHttpPost.class        Strategy.class
DictServiceSoap.class             StrategyList.class
Dictionary.class                       StrategyListResponse.class
DictionaryInfo.class               WordDefinition.class
DictionaryInfoResponse.class     package-info.class

There is more here than we need for the demonstration. The web service implements nine operations, we will only use the Define operation here.

We wrote java code to prove that the service works as expected and that we understood the classes that will be required since these will all be the same for a COBOL program. This is not necessary but is useful to illustrate how the Java and Object Oriented COBOL code correspond. There was quite a bit of trial & error and reviewing the generated java source–this is what we came up with:

import java.util.List;
public class DictWS {
  public void callWebService() {
    System.out.println("DictWS Entered");
    DictService aDictService
      = new DictService();
    DictServiceSoap aDictServiceSoapPort
      = aDictService.getDictServiceSoap();
    WordDefinition aWordDefinition
      = aDictServiceSoapPort.define("java");
    ArrayOfDefinition aDefinitionArray
      = aWordDefinition.getDefinitions();
    List aDefinitionList
      = aDefinitionArray.getDefinition();
    int DefinitionListSize = aDefinitionList.size();
      + " definitions returned\n");
    for (int idx = 0; idx < DefinitionListSize; idx++) {
      Definition aDefinition
        = (Definition) aDefinitionList.get(idx);
    System.out.println("Returning from DictWS");
  public static void main(String[] args) {
  new DictWS().callWebService();

At this point you might just want to run this code using java on the mainframe. And, you could. Here a some JCL that will run this as a batch job:

//* Run a DictWS java program via JZOS
//* Assumes DictWS is in same directory as DictService/com/...
//* Check APP_HOME for correct path
//* see SYS1.SAMPLIB(JVMJCL60) for instructions
# This is a shell script which configures
# any environment variables for the Java JVM.
# Variables must be exported to be seen by the launcher.
. /etc/profile
export JAVA_HOME=/usr/lpp/java/J6.0
export PATH=/bin:"${JAVA_HOME}"/bin
# Customize your CLASSPATH here
APP_HOME=<path-to-classes>/DictService                        # << Check This
# Add Application required jars to end of CLASSPATH
for i in "${APP_HOME}"/*.jar; do
# Set JZOS specific options
# Use this variable to specify encoding for DD STDOUT and STDERR
# Use this variable to prevent JZOS from handling MVS operator commands
# Use this variable to supply additional arguments to main
#export JZOS_MAIN_ARGS=""
# Configure JVM options
IJO="-Xms16m -Xmx128m"
# Uncomment the following to aid in debugging "Class Not Found" problems
#IJO="$IJO -verbose:class"
# Uncomment the following if you want to run with Ascii file encoding..
#IJO="$IJO -Dfile.encoding=ISO8859-1"

but, we have digressed…

Code the OO COBOL program. đŸ˜‰

There are certain requirements to implement Object Oriented programming in COBOL and other requirements for invoking java classes. The resulting load module is a DLL which must be stored in a PDSE Library (or HFS) instead of a PDS. It also should be reentrant. The first line of the sample program specifies these overrides to the normal compiler options and the Program-ID stanza specifies that the program may be run recursively.

 cbl dll,thread,dbcs,pgmname(longmixed),lib
 Identification division.
 Program-id. "DICTWS" recursive.

The next difference from traditional batch COBOL pgramming is the Repository where the program identifies and creates aliases for external classes that will be referenced–similar to the import statements in a java program.

 Environment division.
 Configuration section.
 Class jobject is "java.lang.Object"
 Class jstring is "jstring"
 Class JavaException is "java.lang.Exception"
 Class JavaList is "java.util.List"
 Class DictService is ""
 Class DictServiceSoapPort is ""
 Class WordDefinition is ""
 Class ArrayListOfDefs is ""
 Class Definition is ""
 Class Dictionary is ""

Then, instead of putting variable declaration in Working-Storage, we put them in Local-storage which helps have the program be reentrant (and permits recursion). We declare references to external objects and classes with the object reference data type.

 Data Division.
 Local-storage section.
 77 anException object reference JavaException.
 77 aWord object reference jstring.
 77 aString object reference jstring.
 77 aDictService object reference DictService.
 77 aDictServiceSoapPort object reference DictServiceSoapPort.
 77 aWordDefinition object reference WordDefinition.
 77 aDefinitionArray object reference ArrayListofDefs.
 77 aDefinitionList object reference JavaList.
 77 aDefinition object reference Definition.
 77 aDefinition-Redef-JObject redefines aDefinition
    object reference jobject.
 77 aDictionary object reference Dictionary.
 77 Word-To-Define PIC X(32).
 77 DefinitionListSize PIC S9(9) COMP-5.
 77 idx PIC S9(9) binary.
 77 String-Length PIC S9(9) binary.
 77 String-Buffer PIC X(8192).

So, for example, aDefinition is an object reference to a Definition which (according to the Repository) is defined in the class aDefinition is also redefined as aDefinition-Redef-JObject which is a jobject (java.lang.Object). Redefining object references effectively allows us to do casting and we need this to manipulate Definitions with ArrayOfDefintion methods which is a java.util.List List (and expects java.lang.Object objects).

Some additional setup enables the use of some Java-Native Interface (JNI) routines. This provides some callable services to convert data types from java to COBOL use and vice versa. For example, fetching a Java String and storing it in a PIC X() field:

Linkage section.
copy JNI.
Procedure division.
  Set address of JNIEnv to JNIEnvPtr
  Set address of JNINativeInterface to JNIEnv

The JNI copybook is supplied as an HFS file, /usr/lpp/cobol/include/JNI.cpy, however the batch COBOL compiler cannot access it from there, so we had to copy it to a PDS using the TSO OCOPY command and list that PDS in the SYSLIB for the compiler job step.

You use the INVOKE COBOL verb to invoke a method of a class or object. For Java classes and object, all Invoke verb parameters are BY VALUE.

After each method invocation, we check for a java exception and print the stack trace and exit when there is one. The output would go the the JAVAERR DD statement in the job step JCL.

Following is the rest of the COBOL code with the Java version of the program shown as comments:

* public void callWebService() {


* System.out.println("DictWS Entered");

  Display "DictWS Entered".

* DictService aDictService
* = new DictService();

  Invoke DictService New
    Returning aDictService
  Perform JavaExceptionCheck

* DictServiceSoap aDictServiceSoapPort
* = aDictService.getDictServiceSoap();

  Invoke aDictService "getDictServiceSoap"
    Returning aDictServiceSoapPort
  Perform JavaExceptionCheck

* Put the word to define into a jstring

  Move z"java" to Word-To-Define

  Call "NewStringPlatform"
    using by value JNIEnvPtr
                   Address of Word-To-Define
                   Address of aWord

* WordDefinition aWordDefinition
* = aDictServiceSoapPort.define("java");

  Invoke aDictServiceSoapPort "define"
    Using By Value aWord
    Returning aWordDefinition
  Perform JavaExceptionCheck
* System.out.println(aWordDefinition.getWord());

  Invoke aWordDefinition "getWord"
    Returning aString
  Perform JavaExceptionCheck

  Call "GetStringPlatformLength"
    using by value JNIEnvPtr
                   Address of String-Length
  Call "GetStringPlatform"
    using by value JNIEnvPtr
                   Address of String-Buffer 
                   Length of String-Buffer
  Display String-Buffer(1:String-Length)

* ArrayOfDefinition aDefinitionArray
* = aWordDefinition.getDefinitions();

  Invoke aWordDefinition "getDefinitions"
    Returning aDefinitionArray
  Perform JavaExceptionCheck

* List aDefinitionList
* = aDefinitionArray.getDefinition();

  Invoke aDefinitionArray "getDefinition"
    Returning aDefinitionList
  Perform JavaExceptionCheck

* int DefinitionListSize = aDefinitionList.size();

  Invoke aDefinitionList "size"
    Returning DefinitionListSize
  Perform JavaExceptionCheck

* System.out.println(DefinitionListSize
* + " definitions returned");

  Display DefinitionListSize ' definitions returned'
  Display ' '

* for (int idx=0; idx < DefinitionListSize; idx++) {

  Perform Varying idx from 0 by 1
    Until idx = DefinitionListSize

* Definition aDefinition
* = (Definition) aDefinitionList.get(idx);

  Invoke aDefinitionList "get"
    Using by value idx
    Returning aDefinition-Redef-JObject
  Perform JavaExceptionCheck

* System.out.println(aDefinition.getDictionary().getName());

  Invoke aDefinition "getDictionary"
    Returning aDictionary
  Perform JavaExceptionCheck

  Invoke aDictionary "getName"
    Returning aString
  Perform JavaExceptionCheck

  Call "GetStringPlatformLength"
    using by value JNIEnvPtr
                   Address of String-Length
  Call "GetStringPlatform"
    using by value JNIEnvPtr
                   Address of String-Buffer
                   Length of String-Buffer
  Display String-Buffer(1:String-Length)
* System.out.println(aDefinition.getWordDefinition());

  Invoke aDefinition "getWordDefinition"
    Returning aString
  Perform JavaExceptionCheck

  Call "GetStringPlatformLength"
    using by value JNIEnvPtr
                   Address of String-Length
  Call "GetStringPlatform"
    using by value JNIEnvPtr
                   Address of String-Buffer
                   Length of String-Buffer
  Display String-Buffer(1:String-Length)
* }


* System.out.println("Returning from DictWS");

  Display "Returning from DictWS"
* }
* Check for thrown Java exceptions
  Call ExceptionOccurred using by value JNIEnvPtr
    Returning anException
  If anException not = null then
    Call ExceptionClear using by value JNIEnvPtr
    Display "Caught an unexpected exception in DICTWS"
    Invoke anException "printStackTrace"
    move 16 to return-code
    Stop run
End program "DICTWS".

Compile and link edit the COBOL program. Compiling the program is normal. The link edit step, however, needs to bring in the COBOL/Java support and the java JVM DLL side decks. In our case (for JAVA 6), we used:

INCLUDE '/usr/lpp/java/J6.0/bin/j9vm/libjvm.x'
INCLUDE '/usr/lpp/cobol/lib/igzcjava.x'

Also, the Linkage editor DYNAM(DLL) and CASE(MIXED) parameters on the EXEC statement must be specified and the output library (SYSLMOD) must be a PDSE Library or HFS directory.

Run the batch job.

To execute the program properly, the runtime must be able to locate the java runtime and the classes built by the wsimport step. We did this with a STDENV DD statement in the job and we referred to it with the __CEE_ENVFILE parameter on the PARM ENVAR. Also, XPLINK(ON) and POSIX(ON) are required. Like this:

//JAVAOUT DD PATH='DictService/javaout',
//JAVAERR DD PATH='DictService/javaerr',


Performance of this demonstration is not great. Running this program on a model 2094 with 21621 SU/sec capacity uses about 8 CPU seconds. We suspected that most of this time is in building the JVM, however, this does not compare favorably with java on other platforms. We ran a modified version of the program that made five requests instead of one and displayed time stamps before and after each request and we saw that the first request took about 45 seconds and the four subsequent requests took about 1/2 second each (which is reasonable considering network delay and system load). The CPU time for the five request version of the program was the same as the single request version; i.e. within normal variances.

Update: Retesting the five request verion of the program on an updated CPU, model 2817, 38369 SU/sec, was much better. The job ran 6.5 seconds using 2.5 CPU seconds.

Putting invocations of java methods in a loop probably necessitates adding calls to DeleteLocalRef so that the execution does not run out of storage.

If you have any suggestions to improve the performance, please leave me a comment.


Aonaware DictService
JAX-WS wsimport utility.
Developing object-oriented programs in COBOL
Access JNI services from Enterprise COBOL 4.2.
Preparing and running OO applications in JCL or TSO/E

May 2015 Update

I didn’t attend SHARE in Seattle 2015, but there was an interesting session by Tom Ross of IBM (opinion based upon the powerpoint slides) called Practical Experiences about COBOL Programming. Make SOA Possible in z/OS batch COBOL  Almost makes me think the presenter reviewed this post at some point. 😃 Wish I’d been there.

One thought on “Consuming a Web Service from Batch — Calling Java from COBOL (MVS)

  1. Albib92

    Consuming a service on batch mode is only available if your “request” is < 1000 calls… due to cost of one call… 3ms to transport/transfer information point to point ! Unapplicable in case of million calls.

    Webservice is a transactionnal service (IMS or CICS). For batch mode choose an another way !


Leave a Reply

Your email address will not be published. Required fields are marked *