Chapter 6: Calling Java from COBOL

This chapter describes how you can access Java objects from COBOL programs.

6.1 Overview

Micro Focus Java support enables you to send messages to Java objects from Object COBOL programs and classes. Java is supported through a Java domain in Object COBOL. The Java domain enables you to declare Java classes inside a COBOL program. You can then send messages to the Java classes. You can also send messages from Java classes to COBOL - this is covered in the chapter Calling COBOL from Java.

The Java domain support works by creating a COBOL proxy object for each Java object, as shown in Figure 6-1. The class itself that you declare is a proxy for the static methods of the Java class.

a message from COBOL to Java via a proxy

Figure 6-1: The Java proxy

The COBOL run-time system converts parameters sent with messages from COBOL data types to Java data types. If a method returns a parameter, it is converted from a Java data type to a COBOL data type.

Using the techniques described in this chapter, you can call any Java API, such as JDBC, Sockets and Swing.

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.

6.2 Setting Up Java/COBOL Support

The following subsections describe all the things you need to do before using Java and COBOL together. Some items apply only for calling COBOL from Java, and some only apply when calling Java from COBOL.

6.2.1 Setting Up the COBOL and Java Environments

You need a Java run-time system on any machine which is going to execute Java applications. If you are going to develop mixed Java and COBOL applications, you will also need a Java development environment. You can use either the Java Software Development Kit available from Sun, or any Java IDE which is based on either the Sun or Microsoft run-time environments listed below.

Net Express currently supports the following Java run-time systems on Windows:

Before you start writing COBOL and Java programs which interact, you need to set up some environment variables for the COBOL and Java run-time systems.

If you have COBOL programs which are going to call Java, you must tell the COBOL run-time system which Java run-time system you are using. To do this, set environment variable COBJVM to one of the following:

For example:

set cobjvm=SUN

In addition, if you are using the Sun Java run-time system, add the Sun jre\bin\classic subdirectory to your system path. For example:

set path=%path%;c:\jdk1.2.2\jre\bin\classic

This enables the COBOL run-time system to locate the jvm.dll file contained in this directory. Do not move jvm.dll to a different location; it must be in jre\bin\classic as it has dependencies on other files shipped as part of the Sun Java run-time system.

If you have Java programs which are going to call COBOL, you need to provide some Java classes which interface to the COBOL run-time system. To do this, ensure that mfcobol.jar is specified by the CLASSPATH environment variable for your Java run-time system. You should also ensure that CLASSPATH specifies the current directory, denoted by a period (.). For example:

set classpath=%classpath%;.;c:\program files\Micro Focus\net express\base\bin\mfcobol.jar

Note: You can also set the Java class path when you run a Java program, using the -classpath switch. For example:

java -classpath ".;c:\program files\Micro Focus\net express\base\bin\mfcobol.jar;%CLASSPATH%" MyClass

6.2.2 Compiler Directives for COBOL Programs

Any COBOL program which is going to call Java must be compiled with the following directive:

ooctrl(+p-f) 

This does two things:

6.2.3 Net Express Support for Compiling Java Programs

When you use the Net Express IDE to create Object COBOL classes for use with Java, the Class Wizard adds the Java wrapper classes for Object COBOL to your Net Express project. The Java classes are compiled by the IDE when you rebuild your project. To set up this support edit mfj.cfg, in your net express\base\bin directory, to contain the full path and filename of your Java compiler.

If your net express\base\bin directory does not contain a copy of mfj.cfg, you will need to create it. Net Express creates mfj.cfg file the first time you compile a Java program in the Net Express IDE, providing that it can find the location of a Java compiler in your PC's registry or on the PATH environment variable.

In addition to specifying the Java compiler to use, you can use mfj.cfg to specify any Java compiler command line arguments. If you edited mfj.cfg to contain the following:

d:\jdk\bin\javac.exe -verbose

every time you compiled a Java program from the Net Express IDE, Net Express would use javac.exe in the d:\jdk\bin directory, specifying the -verbose option.

6.2.4 Multi-threading COBOL Programs

All COBOL programs for use with Java must be linked with the multi-threaded run-time system.

On Net Express, click Multi-threaded on the Link tab of the Project Build Settings dialog box. If you are debugging programs, click Settings on the Animate menu, and check Use multi-threaded runtime.

6.3 Writing the COBOL Program

This section shows you how to code a COBOL program to call Java methods using the Object COBOL Java domain. Your program needs to have a Class-Control Section, and use the INVOKE verb each time you want to call a Java method, although it does not have to be written as an Object COBOL class.

You can also call COBOL programs from Java without using the Object COBOL Java domain; this is described in the chapter Calling Procedural COBOL from Java.

6.3.1 Declaring Java Classes

Each Java class you want to use in a COBOL program must be declared in the Class-Control Section. You need to provide the full name of the package the class is in, prefixed by $java$. The prefix tells the COBOL run-time system to load the class from the Java domain.

For example:

 class-control. 
     jRectangle is class "$java$java.awt.Rectangle" 
     .

This declares jRectangle as a COBOL proxy object for the Rectangle class in the java.awt package. The package must be available on your Java classpath or your program will fail at run-time.

6.3.2 Instantiating Java Objects

Each Java class has one or more constructor methods to instantiate objects. In Java, constructor methods have the same name as the class. To enable you to invoke these methods from COBOL, they are mapped to the "new" method name on the COBOL proxy object.

The different constructors on a Java class take different numbers and combinations of parameters to initialize the instance you are creating. For example, the Java Rectangle class can be instantiated in several different ways, including the two shown below in Java code:

 Rectangle r1 = new Rectangle () 
              //  rectangle (x,y) = 0,0, width=0, height=0
 Rectangle r2 = new Rectangle(4, 5, 10, 20) 
             // rectangle (x,y) = (4,5), width=10, height=20

The equivalent COBOL code is shown below:

 working-storage section. 
 01 r1                  object reference. 
 01 r2                  object reference. 
 ...

 procedure division. 
 ...
     invoke jRectangle "new" returning r1 
          *> rectangle (x,y) = 0,0, width=0, height=0
     invoke jRectangle "new" using 4, 5, 10, 20 
                         returning r2 
          *> rectangle (x,y) = (4,5), width=10, height=20

The COBOL run-time system uses the number and type of parameters to call the appropriate constructor on the Java class. You must be careful to provide parameters of the types expected by the Java class, as Java is a strongly typed language. The chapter Java Data Types explains how COBOL data types are mapped on to Java data types. The copyfile javatypes.cpy also defines a set of data types for use in COBOL which correspond directly to Java data types; you are recommended to use these for transferring data between Java and COBOL.

6.3.3 Calling Java Methods

You can call any of the methods on a Java object by sending it a message with the same name as the method. You can also call the static methods of a Java class - send the message to the classname declared in the Class-Control paragraph of your program. Method names in Java are case-sensitive, so the case of the message name in COBOL must match the case of the method in Java.

Java allows method overloading - where one method name has different implementations according to the number and type of parameters passed. COBOL handles this transparently for you, so that the correct Java method is always called.

For example, the Rectangle class has three different add() methods, which take different parameters. The Java code below shows three different ways you can call the add() method on a rectangle.

 Rectangle r1 = new Rectangle(0,0,0,0) ;
 Point pt = new Point(6,6) ; 
 Rectangle r2 = new Rectangle(3,4,9,9) ;  
 r1.add(4,5) ; // changes r1 to smallest rectangle 
               //  containing r1 and point 4,5
 r1.add(pt) ;  // changes r1 to smallest rectangle 
               //  containing r1 and Point pt. 
 r1.add(r2) ;  // changes r1 to union of r1 & r2 

The equivalent code in COBOL looks like this:

 class-control.
     jRectangle is class "$java$java.awt.Rectangle"
     jPoint is class "$java$java.awt.Point"
     .
 working-storage section. 
 01 r1                 object reference. 
 01 r2                 object reference. 
 01 pt                 object reference. 

 procedure division. 
     invoke jRectangle "new" returning r1
     invoke jPoint "new" using 4 5 returning pt
     invoke jRectangle "new" using 3 4 9 9 returning r2
     invoke r1 "add" using 4 5  
     invoke r1 "add" using pt 
     invoke r1 "add" using r2 

Although r2 and pt are both data items of type object reference, the COBOL run-time system determines the type of Java object represented and calls the correct Java method.

6.3.4 Accessing Java Variables

You can access the public members and static variables of a Java class by invoking "setname" and "getname" methods on the Object COBOL proxy. Variable names are case-sensitive in Java, so the case of name in your COBOL code must match the case declared in the Java code.

For example, the Java class below has public variables classVal and instVal:

public class x {
    static int classVal;
    int instVal;
};

This sample of COBOL code sets the static variable classVal, and then retrieves the member instVal.

$set ooctrl(-f+p)
 class-control.
     x is class "$Java$x"
     .

 working-storage section.
 copy "javatypes.cpy".
 01 anX                  object reference.
 01 anInt                jint.
 procedure division.
     invoke x "setclassVal" using by value 4
     invoke x "new" returning anX
     invoke anX "getinstVal" returning anInt

6.3.5 Handling Java Exceptions

An exception thrown by Java is passed back to COBOL as an Object COBOL exception raised against the javaexpt class. The default exception behavior is for the COBOL run-time system to display a message warning of the exception, and then terminate. Alternatively, you can trap the exception by adding an exception handler to your COBOL program.

The instructions below assume you have first read the information on exception handling in the on-line help. Click Help Topics on the Net Express Help menu, then select Programming, Object-oriented Programming, Class Library Frameworks, Exception Handling.

To trap Java exceptions:

  1. Declare the Exceptionmanager, JavaExceptionManager, and Callback or EntryCallback classes in the Class-Control paragraph:
    class-control. 
        ...
        JavaExceptionManager is class "javaexpt"
        ExceptionManager is class "exptnmgr"
        Callback is class "callback"
        EntryCallback is class "entrycll"
        ...
  2. Write an exception handler (either a method in a class, or an entry-point) and create a Callback or EntryCallback to it. For example, a Callback looks like this:
    invoke Callback "new" using anObject z"methodName"
                      returning aHandler

    An EntryCallback looks like this:

    invoke EntryCallback "new" using  z"entryPointname"
                      returning aHandler
  3. Register your callback against the JavaExceptionManger class. For example:
    invoke ExceptionManager "register"
             using JavaExceptionManager aHandler

Now all Java exceptions thrown by classes you are calling through the Object COBOL Java domain get sent to your exception handler.

This is an example of a COBOL program which catches an exception thrown by a Java program:

$set ooctrl (+p-f)
 program-id. ExceptionCatcher.

 class-control.
     SimpleClass is class "$JAVA$SimpleClass"
     EntryCallback is class "entrycll"
     JavaExceptionManager is class "javaexpt"
     ExceptionManager is class "exptnmgr"
     .

 working-storage section.
 01 theInstance                  object reference.
 01 wsCallback                   object reference.
 local-storage section.
 01 filler pic x.   *> dummy storage to allow the local entry
                    *> point to be used for the callback
 linkage section.
 01 lnkException                 object reference.

 procedure division.
*>---Set up Exception handler
     invoke EntryCallback "new" using z"JException"
                            returning wsCallback
     invoke ExceptionManager "register" 
                                  using javaexceptionmanager
                                        wsCallback
*>---Instantiate the class
     invoke SimpleClass "new" returning theInstance

     display "instantiated"
     invoke theInstance "TestException"
     display "excepted"
     stop run.


 entry "Jexception" using lnkException.
     invoke lnkException "display"
     .

The Local-Storage Section in the above program is simply to allow recursion - the COBOL run-time system treats invoking the EntryCallback as a recursive call. Without a Local-Storage Section, this will result in a run-time system error.

This is the Java code for SimpleClass:

import java.lang.* ;

public class SimpleClass {

  public SimpleClass() {
  }

  public void TestException() throws Exception
  {
     Exception e = new Exception ("Test error" );
     throw e;
  }
}

6.3.6 Accessing Native Java Objects

Using the Object COBOL Java domain means that you never access Java objects directly - you are always going through a proxy as explained in the Overview. You can obtain a pointer to the actual Java object using the "getJavaObject" method of the javasup class. You can use this in conjunction with the Java Native Interface (JNI) if you want access to Java functionality not provided by the Object COBOL Java domain.

To get a JNI pointer invoke "getEnv" on the javasup class. The JNI pointer is a pointer to a table of functions; data type JNINativeInterface in copyfile javatypes.cpy provides a structure that makes the JNI function table easier to use, as shown in the example below:

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

*>
 procedure division.
     invoke javasup "getEnv" returning 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
*>   

You can now call JNI functions using the names provided by the JNINativeInterface type definition. You can see an example of this in the section Example of Throwing an Exception in the chapter Calling Procedural COBOL from Java. For more information on JNI, see Sun's Java site.

For more information on the javasup class, see your Class Library Reference. Click Help Topics on the Net Express Help menu, and then click Reference, OO Class Library, Class Library Reference, and click the shortcut to start the Class Library Reference.

6.3.7 Finalizing Java Objects

The Java run-time system includes a garbage collector which automatically destroys unwanted objects. The garbage collector deletes any object which is not referenced by any other object. Once you have a proxy to a Java object in a COBOL program, the COBOL run-time system has a reference to the Java object which prevents the Java garbage collector from removing it.

When you have finished with a Java object, you must release the reference to it so that the garbage collector can delete the object - otherwise your application has a memory leak. To finalize the object:

invoke javaobject "finalize" returning javaobject

The returning parameter is important, as otherwise the COBOL run-time system passes the message on to the actual Java object instead of destroying the COBOL proxy. If the message is passed to the Java object, it invokes this function:

public void finalize()

This function is either inherited or implemented by all Java classes, and is called by the Java garbage collector before it destroys an object.

In the unlikely event that your Java class implements a finalize() method which returns a value, use the "delete" method to destroy the Object COBOL proxy instead:

invoke javaobject "delete" returning javaobject

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