PreviousBefore You Start Calling Java from COBOLNext

Chapter 2: Calling Procedural COBOL from Java

This chapter describes how you can access legacy procedural COBOL programs from Java without using Object COBOL domain support.

2.1 Overview

You can call procedural COBOL programs from Java. This is an alternative to the Object COBOL Java domain support described in the chapters Calling Java from COBOL and Calling Object COBOL from Java. The procedural support described in this chapter is more suitable for use with existing COBOL programs and does not require any knowledge of Object COBOL. However, access to Java objects is only available through either the Java Native Interface (JNI), or by use of the Object COBOL Java domain.

The support is provided through a Java class called mfcobol.runtime. This provides functions which enable you to load, call and cancel COBOL programs. It also enables you to pass parameters to a COBOL program using a Java array. The functions in mfcobol.runtime unpack the array and pass the data to your COBOL program in a form it can use. Figure 2-1 shows a Java program calling a COBOL program and passing it two parameters.

call from Java to COBOL

Figure 2-1: A Java Program Calling a COBOL Program Through mfcobol.runtime

You must have set up your COBOL and Java environments before you can use this technology; see the chapter Before You Start.

2.2 Coding Your Java Program

A Java program calls a COBOL entry point by using the methods in runtime.class, supplied in mfcobol.jar. This class contains a set of methods named cobcall_returntype(), where returntype is the data type returned by the COBOL program or entry point you want to call. For example, use cobcall_int() to call a COBOL program which returns an integer.

To make COBOL support available to your Java program, include the following statement at the start of your Java source file:

import mfcobol.*; 

2.2.1 Multi-threading Considerations

Java run-time systems are multi-threaded, so any COBOL program you are going to use with Java must be linked with the COBOL multi-threaded run-time system whether or not the Java program calling it uses multi-threading. If your COBOL program is going to be called from a multi-threaded Java program, you need to take care that COBOL data accessed from one thread is not corrupted by another thread.

There are several ways you can deal with this:

2.2.2 Loading a COBOL Program or Library

If your COBOL programs are linked into a library file, or if you want to expose entry points inside a COBOL program, you need to load the library file or programs before making any calls. You can do this using the runtime.cobload() method in runtime.class. For example, to load the programs inside mycbl.so:

{
   if (runtime.cobload("mycbl", null) != 0)
       System.out.println("Could not load library\n") ;
   else
       System.out.println("Library loaded successfully\n") ;
 }

However, if any programs within mycbl have entry points which you want to expose for use within Java, you need to load each of those programs in turn. For example, if mycbl contained two COBOL programs, calcint and calctax, each of which had entry points you wanted to call from Java, you would need to code the following:

{
   if (runtime.cobload("mycbl", "calcint") != 0)
       System.out.println("Could not load calcint\n"} ;
   else
   {
       if (runtime.cobload("mycbl", "calctax") != 0) 
           System.out.println("Could not load calctax\n"} ;
   } 
}

2.2.3 Using the cobcall() Methods

Once you have loaded the libraries or programs required by your Java application (see the previous section for details) you can make calls to COBOL using the cobcall_ methods. The cobcall_ methods are all static methods of runtime.class, so you do not have to instantiate runtime.class before starting. Each cobcall_ method takes two or three parameters (the third parameter is optional):

Parameters are converted between Java and COBOL data types as described in the chapter Java Data Types. As explained in the section Coding Your Java Program there are different cobcall_ methods, each named according to the Java equivalent of the data type returned by the COBOL program or entry being called. For example, a COBOL program that returns a signed integer (such as a PIC S9(9) COMP-5) is returning the Java data type int. In this case you would call the COBOL program using the cobcall_int method. By default all parameters are passed by reference.

The copyfile javatypes.cpy also provides a set of COBOL type definitions for Java data types. We advise you to use the data types defined in this file to declare COBOL data items that are going to be used for passing parameters between Java and COBOL. Using these data types helps keep your code portable between different COBOL platforms.

See the section Java Support Classes for COBOL in your Class Library Reference for the full list of cobcall_ methods.

2.2.4 Examples of Calling COBOL from Java

This section shows you two short examples of calling a COBOL program from Java. The first example shows the following features:

This is a simple COBOL subroutine, named legacy.cbl:

 working-storage section.
 copy "javatypes.cpy". 
 01 wsResult       jint.

 linkage section.
 01 wsOperand1     jint. *> type defined in javatypes.cpy 
 01 wsOperand2     jint. 
 01 wsOperation    pic x.

 procedure division using wsOperand1 wsOperand2 wsOperation.
     evaluate wsOperation
      when "a"
         add wsOperand1 to wsOperand2 giving wsResult
      when "s"
         subtract wsOperand1 from wsOperand2 giving wsResult
     end-evaluate

     exit program returning wsResult.

This is a Java program which calls the subroutine:

import mfcobol.* ;

class SimpleCall
{
   public static void main(String argv[]) throws Exception
   {
       Object theParams[] = {new Integer (4),
                             new Integer(7),
                             new Byte((byte)'a')} ;
       int i = runtime.cobcall_int("legacy", theParams) ;
       System.out.println(i) ;
       theParams[2] = new Character ('s') ;
       i = runtime.cobcall_int("legacy", theParams) ;
       System.out.println(i) ;
   }
}

The Java class SimpleCall assumes that legacy.cbl is built into a library file called legacy.ext. However, if this subroutine were built into a different library file, you would need to use the runtime.cobload() method to load the library file before making calls to legacy.cbl.

import mfcobol.* ;

class SimpleCall
{
   static
   {
    // load the library from the static initializer for 
    // the class
    runtime.cobload("cobolapps", null) ;
   }
   public static void main(String argv[]) throws Exception
   {
       Object theParams[] = {new Integer (4),
                             new Integer(7),
                             new Character ('a')} ;

       int i = runtime.cobcall_int("legacy", theParams) ;
       System.out.println(i) ;
       theParams[2] = new Byte((byte)'s') ;
       i = runtime.cobcall_int("legacy", theParams) ;
       System.out.println(i) ;
   }
}

The second example shows you how to pass data to a COBOL program with the equivalent of different USAGE clauses. The cobcall() method used does not return a value, but you can pass an object as the first parameter, and return the same object type from your COBOL program. SimpleCall2 passes the first parameter by reference, the second by value, and the third by content.

import mfcobol.* ;

class SimpleCall2
{
   public static void main(String argv[]) throws Exception
   {
       // Set up an array containing the parameters
       Object theParams[] = { new Integer(1),
                              new Integer(2),
                              new Integer(3)
                             };
       // Set up an array containing the usage information
       int theUsage[] = {runtime.BY_REFERENCE,
                         runtime.BY_VALUE,
                         runtime.BY_CONTENT} ;
       runtime.cobcall(null, "usages", theParams, theUsage) ;
   }
}

2.2.5 Changing Data Members in a Java Object

The example in this section is similar to the example in the previous section, except that the result from the COBOL program is also used to change the value of one of the data members in the Java object.

 thread-local-storage section.
 copy "javatypes.cpy".

 linkage section.
 01 wsOperand1               jint.
 01 wsOperand2               jint.
 01 wsOperation              pic x.
 01 wsResult                 jint.

 procedure division using wsOperand1 wsOperand2 wsOperation
                          wsResult.
     evaluate wsOperation
     when "a"
         add wsOperand1 to wsOperand2
     when "s"
         subtract wsOperand1 from wsOperand2
     end-evaluate

     exit program returning wsResult

The Java class SimpleCall2 assumes that legacy2.cbl is built into a library file called legacy2.ext. However, if this subroutine were built into a different library file, you would need to use the runtime.cobload() method to load the library file before making calls to legacy2.cbl.

import mfcobol.* ;

class SimpleCall2

{
   Integer simpleInteger1;
   Integer simpleInteger2;
   Integer simpleResult;

   public SimpleCall2(int a, int b)
   {
        simpleInteger1 = new Integer(a);
        simpleInteger2 = new Integer(b);
        simpleResult   = new Integer(0);
   }


   public String toString()
   {
        return new String(
                  "simple1Integer1 = "+simpleInteger1+"\n" +
                  "simple1Integer2 = "+simpleInteger2+"\n" +
                  "simpleResult    = "+simpleResult);
          
   }

   public static void main(String argv[]) throws Exception
   {
     
       SimpleCall2 firstDemo = new SimpleCall2(4,7);
       Object theParams[] = { firstDemo.simpleInteger1,
                              firstDemo.simpleInteger2,
                              new Byte((byte) 'a'),
                              firstDemo.simpleResult };

       System.out.println("Before call\n"+firstDemo) ;
       int i = runtime.cobcall_int("legacy2", theParams) ;
       System.out.println("After call\n"+firstDemo) ;
   }
}

2.2.6 Canceling a COBOL Program

To prevent memory leaks you should cancel any COBOL programs you have loaded from Java before the owning Java object is garbage-collected. Use the following call from the "finalize" method of the Java object:

runtime.cobcancel("program")

where program is the name of a COBOL program loaded using a runtime.cobload() call. The following is an example of a "finalize" method in a Java program:

protected void finalize()
{
    try
    {
        runtime.cobcancel("demoFinalizers");
        System.out.println("demoFinalizers - finalize'ed");
    }
    catch(Exception e)
    {
        System.out.println(
                 "Error during finalize : "+e.getMessage());
    }
}

2.2.7 Passing Java Data to COBOL Structures

Server Express enables you to specify structures in the Linkage Sections of your COBOL programs and Object COBOL methods that you can use to pass data from a Java class. For more information on how to access Java data from your COBOL programs or Object COBOL methods in this way, see the section Using Structures in the chapter Java Data Types.

2.3 Handling Strings from a Java Program

You can pass strings between Java and COBOL programs. You can pass a string to COBOL using either the Java String or StringBuffer classes. A COBOL program cannot modify the contents of a String passed to it, but it can change a StringBuffer. To make string handling easier, javatypes.cpy defines the type MF-JSTRING. This data type has the following structure:

01 mf-jstring is typedef.
    03 len                  jint.
    03 capacity             jint.
    03 ptr2string           pointer.

The len field defines the length of the string. The capacity field defines the size of the buffer - this field is always 0 for a String. The ptr2string field is a pointer to the start of the actual string. In the case of a StringBuffer, you can either modify the existing buffer, or allocate a new one and set ptr2string to the start of the new buffer.

The COBOL program below concatenates a String and StringBuffer from Java, and returns the result in the StringBuffer.

 program-id. StringAdd.  
 thread-local-storage section.
 copy "javatypes.cpy".
 01 lsNewPtr                 pointer.
 01 lsSize         jint. *> type defined in javatypes.cpy
 01 i              jint.
 01 lsStatus       jint.
 01 lsBigBuffer              pic x(1024).
 linkage section.
 01 lnkJString               mf-jstring.
 01 lnkJStringBuffer         mf-jstring.

 01 lnkString                pic x(256).
 01 lnkStringbuffer          pic x(256).
 procedure division using lnkJString lnkJStringBuffer.
     set address of lnkStringBuffer to
                              ptr2string of lnkJStringBuffer
     set address of lnkString to ptr2string of lnkJString
*>   Check that lnkJStringBuffer is a Java StringBuffer
     if capacity of lnkJStringBuffer > 0
         add len of lnkJString to len of lnkJStringBuffer
                                   giving lsSize
*>       Check we don't overflow concatenation buffer
         if lsSize < 1024
             move len of lnkJString to i
             move lnkString(1:i) to lsBigBuffer
             add 1 to i
             move lnkStringBuffer to lsBigBuffer(i:lsSize)
             set ptr2string of lnkJStringBuffer
                                 to address of lsBigBuffer
             move lsSize to len of lnkJStringBuffer
         end-if
     end-if

The Java class StringsToCobol passes "fred" and "ginger" to StringAdd and then displays the result.

import mfcobol.*;

public class StringsToCobol {

  public static void main(String[] args) throws Exception
  {
      StringBuffer sb1 = new StringBuffer("ginger") ;
      Object theParams[] = {"fred" ,
                             sb1} ;

       runtime.cobcall_int("stringadd", theParams) ;
       System.out.println(sb1) ;

  }

}

2.4 Using JNI with COBOL

The Java Native Interface (JNI) enables non-Java programs to access Java objects and classes. One of the variants of the cobcall() method provided in the mfcobol.runtime class passes a JNI pointer to the COBOL program being called. The JNI pointer is a pointer to a table of Java functions which enable non-Java code to access the Java run-time system.

For ease of use with COBOL, javatypes.cpy defines data type JNINativeInterface. JNINativeInterface is a group data item consisting of a set of procedure pointers to the JNI Java functions. One use of JNI with COBOL is to throw a Java exception from a COBOL program.


Note: The other way to access Java from COBOL is to use the Object COBOL Java domain support, as documented in the chapter Calling Java from COBOL.


2.4.1 Example of Throwing an Exception

The following COBOL program throws an exception using a JNI function.

 identification division.
 program-id.  "except".
 special-names.
************************************************
* The call convention used for JNI calls needs
* to be defined here. For Win32 systems, it is
* 74 (which corresponds to the stdcall calling
* convention).
************************************************
$if UNIX defined
     call-convention 0 is javaapi.
$else
     call-convention 74 is javaapi.
$end
 local-storage section.
 copy "javatypes.cpy".
 01 JavaException               pointer.
 01 ExceptionClass              pointer.

 linkage section.
 01 JEnv                        pointer.
 01 jobject                     pointer.
 01 lnk-JNINativeInterface      JNINativeInterface.

*  The first parameter (JEnv) is a pointer to the JNI
*  function table. It must be passed (by reference) as
*  the first parameter on all JNI function calls.
*
 procedure division
      using by reference JEnv.

*    Map the pointer passed in JEnv to the
*    JNINativeInterface structure so that we
*    can call JNI functions.
     set address of lnk-JNINativeInterface to JEnv

* Get a reference to the exception class
      call javaapi FindClass
             using by reference JEnv
                   by reference
                   z"java/lang/IllegalArgumentException"
             returning ExceptionClass
      end-call

* Unable to find the exception class, so simply exit
     if ExceptionClass = NULL
             exit program
     end-if

* Throw the new exception
     call javaapi ThrowNew
             using by reference JEnv
                   by value ExceptionClass
                   by reference z"Thrown from COBOL code"
     end-call
     exit program
     .

This is the Java program which calls it:

import mfcobol.*;

class testexcept
{
    public static void main(String argv[]) throws Exception
    {
        try
        {   /* Last parameter is true for passing in 
               JNIEnv.... */
            runtime.cobcall(null,"throwex",null,null,true);
        }
        catch(Exception e)
        {
            System.out.println(
                      "PASS - exception caught ("+ e + ")");
        }
    }
}

2.5 Demonstration Programs

Server Express includes some demonstration programs that illustrate different aspects of calling procedural COBOL from Java. These demonstration programs are contained in directories $COBDIR/demo/java/cobol. Each directory includes all relevant program files, as well as a text file for each demonstration to explain the program in more detail. The text files have names of the form demonstration-name.txt, where demonstration-name is the name of the demonstration program. These demonstration programs are also supplied in Net Express, in directories under Base\Demo\Javademo\Cobol in the directory where Net Express was installed.

The following table shows the directories containing the demonstration programs and gives a brief description of the purpose of the demonstration:

Directory
Illustrates
arrays Reading a Java array from COBOL.
Updating a Java array from COBOL.
pi Calculating pi.
Passing numbers back to Java via Strings.
primtypes Passing primitive Java types to COBOL.
Updating primitive Java types from COBOL.
RecordDemo Writing a simple COBOL Java object. Receiving structures in a COBOL program from Java objects that implement the DataType interface. Different ways of passing parameters BY REFERENCE

Copyright © 2002 Micro Focus International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.


PreviousBefore You Start Calling Java from COBOLNext