![]() | Before You Start | Calling Java from COBOL | ![]() |
This chapter describes how you can access legacy procedural COBOL programs from Java without using Object COBOL domain support.
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.

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.
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.*;
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:
SERIAL causes the COBOL run-time system to serialize access to your COBOL code between different threads. Only one thread can access your program at a time. This is the safest option, although it has potentially the highest overhead for execution speed. It is suitable when the COBOL program is providing access to a shared resource (for example, a printer), or when the COBOL program called from Java is in turn calling other COBOL programs which have not been enabled for multi-threading.
REENTRANT"2" causes the COBOL run-time system to allocate separate user data and FD file areas to each different thread. This prevents any conflicts or data corruption within the program. But REENTRANT"2" can't guarantee thread-safety if it calls other non-threaded programs, or accesses other shared resources - in these sorts of cases, SERIAL is a safer option. The REENTRANT"2" directive can provide better performance than SERIAL as one thread is not kept waiting for the next thread to finish.
Thread-local storage is allocated per thread, so there is no possibility of one thread corrupting the data used by another thread. This is fairly efficient, but might not always be an option with legacy code.
This can be very efficient as you control which data is thread-local and which data is shared between threads. You could use this option in COBOL driver programs that sit between the Java run-time environment and your legacy COBOL programs. Your driver program would be responsible for controlling access to the legacy programs, and would need to use semaphores or some similar mechanism to prevent two threads from accessing the same code at the same time.
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"} ;
}
}
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.
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) ;
}
}
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) ;
}
}
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());
}
}
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.
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) ;
}
}
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.
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 + ")");
}
}
}
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.
![]() | Before You Start | Calling Java from COBOL | ![]() |