PreviousIntroduction Using Other Object DomainsNext

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. The diagram below shows how this works 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 need to have at least a basic knowledge of the Java language to be able to use this technology effectively. Sun's Java site is a good starting place.

2.2 Setting Up

See the section Setting Up Java/COBOL Support in chapter Calling Object COBOL from Java for instructions on how to set up your COBOL and Java environments.

2.3 Coding Your Java Program

A Java program calls a COBOL entry-point by using the functions in runtime.class, supplied in mfcobol.jar. This class contains a set of functions 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.3.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.3.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() function in runtime.class. For example, to load the programs inside mycbl.dll (on Windows) or mycbl.so (on some Unix systems).:

{
   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 wish 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.3.3 Using the cobcall() functions

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_ functions, The cobcall_ functions are all static functions of runtime.class, so you do not have to instantiate runtime.class before starting. Each cobcall_ function 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_ functions, 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. So you would call the COBOL program using the cobcall_int function. 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 here to declare COBOL data items that are going to be used for passing parameters between Java and COBOL. Using these datatypes helps keep your code portable between different COBOL platforms.

See the Java Run-time Class Library for the full list of cobcall_ functions - this is documented as part of the Class Library Reference. Click Help Topics on the Net Express Help menu, and look up Class library in the index. Select Class Library Reference from the Topics Found dialog box.

2.3.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() function to load the library file before making calls to legacy.

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() function used does not return a value, but you can pass in 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.3.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() function to load the library file before making calls to legacy.

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.3.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.4 Handling Strings from a Java Program

You can pass strings between Java and COBOL programs. You can pass a string to COBOL either using 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 handling strings 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 ptrt2tring 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.5 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() function provided in the mfcobol.runtime class passes through 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 domain support, as documented in the chapter Calling Java from COBOL.


2.5.1 Example of Throwing an Exception

The following short 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.6 Demonstration Programs

Net Express includes some demonstration programs that illustrate different aspects of calling procedural COBOL from Java. These demonstration programs are contained in directories beneath \Program Files\MERANT\Net Express\Base\Demo\Javademo\Cobol. Each directory includes all relevant program files, as well as a readme.txt file to explain the programs in more detail.

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.


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

PreviousIntroduction Using Other Object DomainsNext