![]() | はじめに | COBOL から Java を呼び出す | ![]() |
この章では、Object COBOL ドメイン サポートを使用しないで Java から既存の手続き型 COBOL にアクセスする方法について説明します。
Java から手続き型 COBOL プログラムを呼び出すことができます。これは、「COBOL から Java を呼び出す」の章および「Java から Object COBOL を呼び出す」の章で説明されている Object COBOL Java ドメイン サポートに替わる方法です。この章で説明する手続き型のサポートは、既存の COBOL プログラムで使用する場合に適しており、Object COBOL の知識を必要としません。しかし、Java オブジェクトへアクセスできるのは、 Java Native Interface (JNI) 経由、または Object COBOL Java ドメインを使用する場合だけです。
このサポートは、mfcobol.runtime という Java クラスを経由して実現されます。これにより、COBOL プログラムのロード、呼び出し、およびキャンセルを行う関数が使用できるようになります。また、Java 配列を使用して COBOL プログラムへパラメータを渡すこともできます。mfcobol.runtime の関数は、配列をアンパックして、使用可能なフォームで COBOL プログラムへデータを渡します。次のダイアグラムは、Java プログラムが COBOL プログラムを呼び出してパラメータを 2 つ COBOL プログラムに渡す様子を示しています。
図 2-1: mfcobol.runtime を経由して COBOL プログラムを呼び出し中の Java プログラム
この技術を効果的に使用するには、少なくとも Java 言語の基本知識が必要です。Java については、Sun Microsystems の Java サイトを参照することをお勧めします。
COBOL と Java の環境設定方法については、「Java から Object COBOL を呼び出す」の章の「Java/COBOL サポートの設定」の項を参照してください。
Java プログラムは、mfcobol.jar にある runtime.class 内の関数を使用して COBOL のエントリポイントを呼び出します。このクラスには、cobcall_returntype() という名前の関数のセットがあります。returntype は、呼び出す COBOL プログラムまたはエントリポイントによって戻されるデータ型です。たとえば、整数を戻す COBOL プログラムを呼び出すには、cobcall_int() を使用します。
Java プログラムで COBOL サポートを使用できるようにするには、Java ソース ファイルの先頭に次の文を含めてください。
import mfcobol.*;
Java ランタイム システムはマルチスレッドなので、Java とともに使用する COBOL プログラムは、COBOL マルチスレッド ランタイム システムにリンクする必要があります。これは、COBOL プログラムを呼び出す Java プログラムがマルチスレッドを使用するかどうかに関わらず必要です。マルチスレッドの Java プログラムから COBOL プログラムを呼び出す場合は、スレッドからアクセスされる COBOL のデータが別のスレッドによって破壊されないように注意する必要があります。
この問題を避けるには、次のような方法があります。
SERIAL を使用すると、COBOL ランタイム システムは COBOL コードへのアクセスをスレッド間でシリアル化します。一度に 1 つのスレッドしかプログラムにアクセスできません。この方法が最も安全ですが、実行スピードについてオーバーヘッドが最も高くなることがあります。この方法が適しているのは、COBOL プログラムが共有リソース (たとえば、プリンタ) にアクセスする場合や、Java から呼び出された COBOL プログラムがマルチスレッドが使用可能にされていない別の COBOL プログラムを次に呼び出す場合です。
REENTRANT(2) を使用すると、COBOL ランタイム システムはそれぞれのスレッドに個別のユーザ データと FD ファイル領域を割り当てます。これにより、プログラム内の競合やデータ破損を防ぎます。しかし、REENTRANT(2) は、他のスレッド化されていないプログラムを呼び出す場合や、他の共有リソースにアクセスする場合には、スレッドの安全性を保障しません。したがって、このような場合には、SERIAL を使用するほうが安全です。REENTRANT(2) 指令では、スレッドが次のスレッドの終了を待たないので、SERIAL よりもパフォーマンスが上がります。
Thread-Local-Storage がスレッドごとに割り当てられるので、別のスレッドが使用するデータをスレッドが破壊することがなくなります。この方法は効果がありますが、既存のコードと使用するのに常に適しているとは限りません。
この方法は、スレッドローカルなデータとスレッド間で共有されているデータをユーザが制御できるので、非常に効果的です。この方法を既存の COBOL コードに使用するには、Java ランタイム環境と既存のプログラムの間に使用する COBOL ドライバ プログラムを作成する必要があります。作成するドライバ プログラムが既存プログラムへのアクセスを制御します。また、2 つのスレッドが同じコードに同時にアクセスしないように、ドライバ プログラムにセマフォや同様の機構を使用する必要があります。
COBOL プログラムがライブラリ ファイルにリンクされている場合、または COBOL プログラム中のエントリポイントをエクスポーズする場合は、ライブラリ ファイルまたはプログラムをロードしてから呼び出しを行う必要があります。ロードを行うには、runtime.class の runtime.cobload() 関数を使用します。たとえば、mycbl.dll (Windows) または mycbl.so (Unix システムの一部) 中のプログラムをロードするには、次のようなコードを作成します。
{ if (runtime.cobload("mycbl", null) != 0) System.out.println("ライブラリをロードできませんでした\n") ; else System.out.println("ライブラリを正常にロードしました\n") ; }
しかし、Java で使用するためにエクスポーズするエントリポイントが mycbl 内の複数のプログラムにある場合は、これらのプログラムをそれぞれ順にロードする必要があります。たとえば、mycbl に calcint と calctax の 2 つのプログラムがあり、Java から呼び出したいエントリポイントがそれぞれのプログラムにある場合は、次のようなコードを作成します。
{ if (runtime.cobload("mycbl", "calcint") != 0) System.out.println("calcint をロードできませんでした\n"} ; else { if (runtime.cobload("mycbl", "calctax") != 0) System.out.println("calctax をロードできませんでした\n"} ; } }
Java アプリケーションで必要なライブラリやプログラムをロードしたら (詳細は前の項を参照)、cobcall_ 関数を使用して COBOL への呼び出しを行うとができます。cobcall_ 関数はすべて runtime.class の静的関数なので、使用する前に runtime.class をインスタンス化する必要はありません。それぞれの cobcall_ 関数には、次のようなパラメータが 2 つか 3 つあります (3 番目のパラメータはオプションです)。
パラメータは、「Java データ型」の章で説明しているように、Java データ型と COBOL データ型の間で変換されます。Java プログラムのコーディング の項で説明したとおり、呼び出す COBOL プログラムやエントリによって戻されるデータ型と等価の Java データ型に応じて、さまざまな cobcall_ 関数が用意されています。たとえば、符号付き整数 (pic s9(9) comp-5) を戻す COBOL プログラムは Java データ型 int を戻します。したがって、この場合は cobcall_int 関数を使用して COBOL プログラムを呼び出します。デフォルトでは、すべてのパラメータは by reference で渡されます。
コピーファイル javatypes.cpy にも、Java データ型に対応する COBOL データ型の定義が記述されています。Java と COBOL 間のパラメータ受け渡しに使用する COBOL データ項目は、このファイル内で定義されているデータ型を使用して宣言することをお勧めします。これらのデータ型を使用することにより、異なる COBOL プラットフォーム間でコードの移植が可能になります。
すべての cobcall_ 関数の一覧については、「Java ランタイム クラス ライブラリ」 を参照してください。これは、「クラス ライブラリ リファレンス」 の一部です。 Windowsの[スタート]メニューの[プログラム]から、Net Expressのフォルダ内の[NetExpress オンラインマニュアル] を選択して、「クラス ライブラリ リファレンス」 のショートカット ボタンをクリックします。
この項では、Java から COBOL プログラムを呼び出す簡単な例を 2 つ示します。最初の例では、次の機能を使用します。
次は、簡単な COBOL サブルーチン legacy.cbl です。
working-storage section. copy "javatypes.cpy". 01 wsResult jint. linkage section. 01 wsOperand1 jint. *> 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.
次に、このサブルーチンを呼び出す Java プログラムです。
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) ; } }
この Java クラス SimpleCall は、legacy.cbl が legacy.ext というライブラリ ファイルに組み込まれていることを前提としています。しかし、このサブルーチンが別のライブラリ ファイルに組み込まれている場合は、runtime.cobload() 関数を使用してそのライブラリ ファイルをロードしてから legacy への呼び出しを行う必要があります。
import mfcobol.* ; class SimpleCall { static { // static イニシャライザからクラス用にライブラリをロード 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) ; } }
次の例は、複数の異なる usage 句に相当する配列を使用して COBOL プログラムへデータを渡す方法を示します。 使用する cobcall() 関数は値を戻しませんが、最初のパラメータとしてオブジェクトを渡し、COBOL プログラムから同じオブジェクト型を戻すことができます。次の SimpleCall2 は、最初のパラメータを by reference で、2 番目のパラメータを by value で、3 番目のパラメータを by content で渡します。
import mfcobol.* ; class SimpleCall2 { public static void main(String argv[]) throws Exception { // パラメータを含む配列を設定 Object theParams[] = { new Integer(1), new Integer(2), new Integer(3) }; // usage 情報を含む配列を設定 int theUsage[] = {runtime.BY_REFERENCE, runtime.BY_VALUE, runtime.BY_CONTENT} ; runtime.cobcall(null, "usages", theParams, theUsage) ; } }
この項の例は前項の例に似ていますが、COBOL プログラムからの結果を使用して Java オブジェクト内の 1 つのデータ メンバーの値を変更する点が異なります。
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
この Java クラス SimpleCall2 は、legacy2.cbl が legacy2.ext というライブラリ ファイルに組み込まれていることを前提としています。しかし、このサブルーチンが別のライブラリ ファイルに組み込まれている場合は、runtime.cobload() 関数を使用してそのライブラリ ファイルをロードしてから 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("呼び出し前\n"+firstDemo) ; int i = runtime.cobcall_int("legacy2", theParams) ; System.out.println("呼び出し後\n"+firstDemo) ; } }
メモリ リークを防ぐために、Java オブジェクトがガーベッジ コレクタによって破棄される前に、Java から ロードした COBOL プログラムをすべてキャンセルする必要があります。Java オブジェクトの "finalize" メソッドから次の呼び出しを使用します。
runtime.cobcancel("program")
ここで、program は runtime.cobload() 呼び出しを使用してロードした COBOL プログラムの名前を指定します。 次は、Java プログラム中の "finalize" メソッドの例です。
protected void finalize() { try { runtime.cobcancel("demoFinalizers"); System.out.println("demoFinalizers - 終了しました"); } catch(Exception e) { System.out.println("終了中のエラー: "+e.getMessage()); } }
Java プログラムと COBOL プログラムの間で文字列を受け渡すことができます。Java の String クラスまたは StringBuffer クラスを使用して、COBOL へ文字列を渡すことができます。COBOL プログラムは、渡された String の内容を変更できませんが、StringBuffer を変更することはできます。文字列の処理を簡単にするために、javatypes.cpy に MF-JSTRING 型が定義されています。このデータ型の構造は次のとおりです
01 mf-jstring is typedef. 03 len jint. 03 capacity jint. 03 ptr2string pointer.
len フィールドでは文字列の長さが定義されます。capacity フィールドではバッファのサイズが定義されます。このフィールドは、String の場合は常に 0 です。ptrt2tring は実際の文字列の先頭を指すポインタです。StringBuffer の場合は、既存のバッファを変更するか、または新しいバッファを割り当てて ptr2string を新しいバッファの先頭に対して設定することができます。
次の COBOL プログラムは、Java から String と StringBuffer を連結して、StringBuffer に結果を戻します。
program-id. StringAdd. thread-local-storage section. copy "javatypes.cpy". 01 lsNewPtr pointer. 01 lsSize jint. *> 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 *> lnkJStringBuffer が Java StringBuffer であることを確認 if capacity of lnkJStringBuffer > 0 add len of lnkJString to len of lnkJStringBuffer giving lsSize *> 連結バッファがオーバーフローしないことを確認 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
次の Java クラス StringsToCobol は、"fred" と "ginger" を StringAdd へ渡してから、結果を表示します。
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) ; } }
JNI (Java Native Interface) は、Java 以外のプログラムが Java オブジェクトと Java クラスにアクセスできるようにします。mfcobol.runtime クラスにある cobcall() 関数のバリアントの 1 つが JNI ポインタを経由して、呼び出し中の COBOL プログラムへ渡ります。JNI ポインタは、Java 以外のコードが Java ランタイム システムにアクセスできるようにする Java 関数のテーブルへのポインタです。
COBOL で JNI を簡単に使用できるように、javatypes.cpy にデータ型 JNINativeInterface が定義されています。JNINativeInterface は、JNI Java 関数への手続きポインタのセットからなるグループ データ項目です。COBOL で JNI 使用する例は、COBOL プログラムから Java 例外を発生させる場合です。
次の簡単な COBOL プログラムは、JNI 関数を使用して例外を発生させます。
identification division. program-id. "except". special-names. ************************************************ * JNI 呼び出しに使用する呼び出し規約をここで * 定義する必要があります。Win32 システムでは、 * 74 です (これは stdcall 呼び出し規約に対応 * します)。 ************************************************ $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. * 最初のパラメータ (JEnv) は、JNI 関数テーブルへの * ポインタです。これは、すべての JNI 関数呼び出しで最初の * パラメータとして (by reference で) 渡される必要があります。 * procedure division using by reference JEnv. * JEnv で渡されたポインタを JNINativeInterface * 構造体にマップして、JNI 関数を呼び出せるように * します。 set address of lnk-JNINativeInterface to JEnv * 例外クラスへの参照を取得 call javaapi FindClass using by reference JEnv by reference z"java/lang/IllegalArgumentException" returning ExceptionClass end-call * 例外クラスが見つからない場合は、そのまま exit if ExceptionClass = NULL exit program end-if * 新規例外を発生させる call javaapi ThrowNew using by reference JEnv by value ExceptionClass by reference z"Thrown from COBOL code" end-call exit program .
次は、呼び出し側の Java プログラムです。
import mfcobol.*; class testexcept { public static void main(String argv[]) throws Exception { try { /* 最後のパラメータは JNIEnv で渡すために true.... */ runtime.cobcall(null,"throwex",null,null,true); } catch(Exception e) { System.out.println("通過 - 例外をキャッチ ("+ e + ")"); } } }
Copyright © 2000 MERANT International Limited. All rights reserved.
本書、ならびに使用されている固有の商標と商品名は国際法で保護されています。
![]() | はじめに | COBOL から Java を呼び出す | ![]() |