第 9 章 : Object COBOL からの Java の呼び出し

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

9.1 概要

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

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



図 9-1 : Java プロキシ

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

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

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

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

COBOL プログラムを Java から呼び出すこともできます。その場合には、Object COBOL の Java ドメインは使用しません。方法については、『Java からの手続き型 COBOL の呼び出し』で説明しています。

9.2 作業環境のセットアップ

実際の作業に着手する前に、COBOL と Java のランタイムシステムが連携できるように、作業環境をセットアップする必要があります。具体的な手順については、『Java と COBOL の連携』の章にある『Java と COBOL の環境のセットアップ』を参照してください。

9.3 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 に登録されている必要があります。この条件を満たさないとランタイムエラーになり、プログラムは正しく実行できません。

9.4 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 側で使用することをお奨めします。

9.5 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 メソッドを呼び出します。

9.6 Java 変数の利用

Object 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

9.7 Java 例外の処理

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

次の手順は、docs/mfcobol.docs.zip に格納されている『Java Run-time Class Library Reference』に記載されている例外処理の情報に、すでに目を通していることを前提にしています。

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

以上の手順を実施すると、Object 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;
  }
}

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

Object COBOL の Java ドメインを使用する場合、Java オブジェクトに直接アクセスすることはありません。『概要』の項で説明したように、常にプロキシを経由してアクセスします。 一方、javasup クラスの getJavaObject メソッドを使用すると、実際の Java オブジェクトへのポインタを取得することができます。 このメソッドを JNI (Java Native Interface) と組み合わせて使用すれば、Object 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
*>   Env で渡されたポインタを JNINativeInterface
*>   構造体の先頭アドレスにマッピングして、JNI 関数の
*>   呼び出しを可能にします。
     set address of lnk-JNINativeInterface to JEnv
*>   

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

javasup クラスの詳細については、docs/mfcobol.docs.zip に格納されている『Java Run-time Class Library Reference』を参照してください。

9.9 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() メソッドを実装しているようであれば、Object COBOL プロキシを破棄する手段として、finalize のかわりに次の delete メソッドを使用してください。

invoke javaobject "delete" returning javaobject

Copyright © 2004 Micro Focus International Limited. All rights reserved.