オブジェクト COBOL からの Java の呼出し

ここでは、オブジェクト COBOL の Java ドメインを使用して Java メソッドを呼び出す COBOL プログラムの作成方法を説明します。

概要

Micro Focus の Java サポートを使用すると、オブジェクト COBOL のプログラムやクラスから Java オブジェクトにメッセージを送信できます。

Java サポートはオブジェクト COBOL の Java ドメインを通じて実現されます。Java ドメインによって、COBOL プログラム内で Java クラスを宣言し、そのクラスにメッセージを送信することが可能になります。図 4-1 に示すように、Java ドメインでは Java オブジェクトごとに、対応する COBOL プロキシオブジェクトを作成します。 宣言したクラス自体が、Java クラスの静的メソッドのプロキシになります。

Java プロキシ

図 4-1: Java プロキシ

この用途に使用する COBOL プログラムにはリポジトリ段落が必要で、Java メソッドを呼び出すときには、常に INVOKE 動詞を使用する必要があります。ただし、必ずしもオブジェクト COBOL のクラスとして実装する必要はありません。

COBOL プログラムからメッセージ付きで送信されたパラメータは、COBOL ランタイムシステムによって COBOL から Java のデータ型に変換されます。また、Java メソッドがパラメータを返す場合には、そのパラメータが Java データ型から COBOL データ形式に変換されます。

ここで説明する手法を使用すると、JDBC、Sockets、Swing など、あらゆる Java API を呼び出すことができます。

ここで説明する情報を活用するには、Java 言語に関する基本レベル以上の知識が必要になります。Java 言語の基本的な知識を得るには、Net Express Links のリンク先にある Sun 社の Java Web サイトが役立ちます。

COBOL プログラムを Java から呼び出すこともできます。その場合には、オブジェクト COBOL の Java ドメインは使用しません。詳細は、『Java からの手続き型 COBOL の呼出し』の章を参照してください。

作業環境の設定

実際の作業に着手する前に、COBOL と Java のランタイムシステムが連携できるように、作業環境を設定する必要があります。詳細は、『Java と COBOL の連携』の章の『Java と COBOL の環境の設定』の節を参照してください。

Java クラスの宣言

COBOL プログラムで使用する各 Java クラスは、COBOL のリポジトリ段落で個別に宣言する必要があります。宣言には、Java クラスが属するパッケージの名前を、直前に $java$ を付けて省略しないで記述します。この接頭語によって COBOL ランタイムシステムに、クラスを Java ドメインからロードするように指示します。

Java クラスを次のように宣言します。

repository.
     class COBOL-classname as "$java$class-name"
     .

名前の詳細は、次のとおりです。

COBOL-classname COBOL プログラム内でクラスを識別する名前
java Java ドメイン名
class-name Java ドメインでクラスを識別する名前。通常は、当該ドメインのオブジェクトモデルに登録されているクラス名を使用します。

次に例を示します。

repository. 
     class jRectangle as "$java$java.awt.Rectangle" 
     .

この例は、java.awt パッケージ内の Rectangle クラスの COBOL プロキシオブジェクトとして jRectangle を宣言しています。java.awt パッケージは、Java の classpath に登録されている必要があります。この条件を満たさないとランタイムエラーになり、プログラムは正しく実行できません。

Java オブジェクトのインスタンス化

Java の各クラスには、オブジェクトをインスタンス化するためのコンストラクタメソッドが 1 つ以上含まれています。Java では、コンストラクタメソッドはクラスと同じ名前を持ちます。これらのコンストラクタメソッドは、COBOL プロキシオブジェクトで new というメソッド名にマップされ、COBOL 側から呼出し可能になります。

同じ Java クラスでもコンストラクタが異なれば、インスタンスを初期化するために必要なパラメータの数や組み合わせが異なります。たとえば、Java の Rectangle クラスは、次に示す 2 通りの方法を含む、いくつかの異なる方法でインスタンス化できます。

 Rectangle r1 = new Rectangle () 
              // 起点座標 (0,0)、幅 0、高さ 0 の長方形
 Rectangle r2 = new Rectangle(4, 5, 10, 20) 
              // 起点座標 (4,5)、幅 10、高さ 20 の長方形

これに相当する COBOL のコードは次のようになります。

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

 procedure division. 
 ...
     invoke jRectangle "new" returning r1 
          *> 起点座標 (0,0)、幅 0、高さ 0 の長方形
     invoke jRectangle "new" using 4, 5, 10, 20 
                         returning r2 
          *> 起点座標 (4,5)、幅 10、高さ 20 の長方形

COBOL ランタイムシステムは、パラメータの数と型を基に、Java クラス内の適切なコンストラクタを呼び出します。Java は型チェックが厳密であるため、指定するパラメータの型は、Java クラスの定義に必ず一致させてください。COBOL データ形式と Java データ型のマッピングの詳細は、『Java データ型』の章を参照してください。コピーファイル javatypes.cpy には、Java のデータ型に直接的に対応する一連のデータ型が定義されており、Java と COBOL 間でデータをやり取りするときには、これらのデータ型を COBOL 側で使用することが推奨されます。

Java メソッドの呼出し

Java オブジェクト内のメソッドは、いずれもメソッドと同じ名前のメッセージを送信することによって呼び出します。また、COBOL プログラムのクラス制御段落で宣言したクラス名にメッセージを送信して、Java クラスの静的メソッドを呼び出すこともできます。Java ではメソッド名の大文字、小文字が区別されるため、COBOL プログラム内に記述するメッセージ名は、Java の対応するメソッドと大文字、小文字の区別まで一致させる必要があります。

Java にはメソッドのオーバーロード機能があり、渡されるパラメータの数と型に応じて、同じ名前のメソッドの実装を変えることができます。この機能に必要な COBOL 側の処理は内部的に実行されるため、常に適切な Java メソッドが呼び出されます。

たとえば、Rectangle クラスには互いに異なるパラメータを受け取る 3 種類の add() メソッドがあります。次の Java コードは、Rectangle の add() メソッドを呼び出す 3 通りの方法を示しています。

 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) ; // r1 を r1 自体と ポイント 4,5 を含む
               // 最小の長方形に変更します。
 r1.add(pt) ;  // r1 を r1 自体と ポイント pt を含む
               // 最小の長方形に変更します。
 r1.add(r2) ;  // r1 と r2 を結合した長方形を r1 にします。 

これに相当する COBOL のコードは次のようになります。

 repository.
     class jRectangle as"$java$java.awt.Rectangle"
     class jPoint as "$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 

r2 と pt はどちらもオブジェクト参照型のデータ項目ですが、COBOL ランタイムシステムは Java オブジェクトの型を判断して適切な Java メソッドを呼び出します。

Java 変数の利用

オブジェクト COBOL プロキシで「setname」メソッドと「getname」メソッドを呼び出すと、Java クラスのパブリックメンバと静的変数にアクセスできます。Java では変数名の大文字、小文字が区別されるため、COBOL プログラム内に記述する変数名部分 (name) は、Java コード内での宣言に大文字、小文字の区別まで一致させる必要があります。

たとえば、次の Java クラスに含まれるパブリック変数 (classVal、instVal) にアクセスする場合を考えてみましょう。

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

次の COBOL コード例は、静的変数 classVal を設定し、続いてメンバ instVal の値を取得します。

$set ooctrl(-f+p)
repository.
     class x as "$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

Java 例外の処理

Java からスローされた例外は、javexpt クラスに発生したオブジェクト COBOL 例外として COBOL に返されます。デフォルトでは、COBOL ランタイムシステムは例外を受信すると、その旨を警告するメッセージを表示して終了しますが、COBOL プログラムにイベントハンドラを追加して、例外をトラップすることも可能です。

次の手順は、Java Run-time Class Library に記載されている例外処理の情報に、すでに目を通していることを前提としています。

Java 例外をトラップする手順は次のとおりです。

  1. クラス制御段落で、Exceptionmanager、JavaExceptionManager、および Callback (または EntryCallback) の各クラスを宣言します。
    repository.
        ...
        class JavaExceptionManager as "javaexpt"
        class ExceptionManager as "exptnmgr"
        class Callback as "callback"
        class EntryCallback as "entrycll"
        ...
  2. 例外ハンドラ (クラスのメソッドまたは入口点) を作成した後に、対応する Callback または EntryCallback のコードを記述します。Callback を作成する場合は、次のように記述します。
    invoke Callback "new" using anObject z"methodName"
                      returning aHandler

    EntryCallback を作成する場合は、次のように記述します。

    invoke EntryCallback "new" using  z"entryPointname"
                      returning aHandler
  3. コールバックを JavaExceptionManger クラスに登録します。次に例を示します。
    invoke ExceptionManager "register"
             using JavaExceptionManager aHandler

以上の手順を実施すると、オブジェクト COBOL の Java ドメインを経由して呼び出している Java クラスからスローされた Java 例外が、常に作成した例外ハンドラに送信されるようになります。

Java プログラムからスローされた例外をキャッチする COBOL プログラムの例を次に示します。

$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.   *> ローカルエントリポイントをコールバックに
                    *> 使用するためのダミー項目です。
 linkage section.
 01 lnkException                 object reference.

 procedure division.
*>--- 例外ハンドラの設定を行います。
     invoke EntryCallback "new" using z"JException"
                            returning wsCallback
     invoke ExceptionManager "register" 
                                  using javaexceptionmanager
                                        wsCallback
*>--- クラスのインスタンス化を行います。
     invoke SimpleClass "new" returning theInstance

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


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

このプログラムでは局所記憶節によって再帰呼出しが可能になっており、COBOL ランタイムシステムが EntryCallback を再帰的に呼び出します。局所記憶節がないとランタイムエラーになり、このプログラムは正しく実行できません。呼び出される SimpleClass の Java コードは次のとおりです。

import java.lang.* ;

public class SimpleClass {

  public SimpleClass() {
  }

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

ネイティブ Java オブジェクトへのアクセス

オブジェクト COBOL の Java ドメインを使用する場合は、Java オブジェクトに直接アクセスすることはありません。『概要』で説明したように、常にプロキシを経由してアクセスします。一方、javasup クラスの getJavaObject メソッドを使用すると、実際の Java オブジェクトへのポインタを取得することができます。このメソッドを JNI (java native interface) と組み合わせて使用すると、オブジェクト COBOL の Java ドメインでは利用できない Java の機能も利用可能になります。

JNI ポインタを取得するには、javasup クラスの getEnv メソッドを呼び出します。JNI ポインタは関数テーブルへのポインタです。次の例に示すように、コピーファイル javatypes.cpy で定義されている JNINativeInterface 構造体を使用すると、JNI 関数テーブルに容易にアクセスできます。

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

*>
 procedure division.
     invoke javasup "getEnv" returning jEnv
*> JEnv で渡されたポインタを JNINativeInterface
*> 構造体の先頭アドレスにマップして、JNI 関数の
*> 呼出しを可能にします。
     set address of lnk-JNINativeInterface to JEnv
*>   

以上の処理を行うと、JNINativeInterface の型定義で指定されている名前を使用して、JNI 関数を呼び出すことが可能になります。実際の呼出し例は、『Java からの手続き型 COBOL の呼出し』の章の『例外スローの例』の節を参照してください。JNI の詳細は、Net Express Links のリンク先にある Sun 社の Java Web サイトを参照してください。

javasup クラスの詳細は、 『Java Run-time Class Library』を参照してください。

Java オブジェクトの破棄

Java ランタイムには、不要になったオブジェクトを自動的に破棄するガーベッジコレクタがあります。ガーベッジコレクタは、他のいっさいのオブジェクトから参照されていないオブジェクトを削除します。COBOL プログラムで Java オブジェクトへのプロキシを作成すると、その Java オブジェクトへの参照が COBOL ランタイムシステムで保持され、Java ガーベッジコレクタによるオブジェクトの破棄が防止されます。

Java オブジェクトが不要になったときには、そのオブジェクトへの参照を COBOL プログラム内で明示的に解放して、ガーベッジコレクタで破棄させる必要があります。この処理を行わないと、アプリケーションでメモリリークが発生します。具体的には、次の呼出しを実行します。

invoke javaobject "finalize" returning javaobject
        

returning パラメータは必須です。このパラメータを省略すると、COBOL ランタイムシステムは COBOL プロキシを破棄するかわりに、呼び出した Java オブジェクトにメッセージを渡します。このメッセージを受信した Java オブジェクトは、次の関数を呼び出します。

public void finalize()

この関数は、すべての Java クラスに継承または実装されており、Java ガーベッジコレクタによって、オブジェクトを破棄する前に呼び出されます。

通常、Java クラスの finalize() メソッドには戻り値はありません。ただし、使用する Java クラスが値を戻す finalize() メソッドを実装しているようであれば、オブジェクト COBOL プロキシを破棄する手段として、finalize のかわりに次の delete メソッドを使用してください。

invoke javaobject "delete" returning javaobject