| はじめに | COBOL からの Java の呼び出し |
この章では、オブジェクト COBOL ドメイン サポートを使用しないで、 Java から既存の手続き型 COBOL にアクセスする方法について説明します。
Java から手続き型 COBOL プログラムを呼び出すことができます。これは、『COBOL からの Java の呼び出し』の章および『Java からのオブジェクト COBOL の呼び出し』の章で説明されている Object COBOL Java ドメイン サポートに替わる方法です。この章で説明する手続き型のサポートは、既存の COBOL プログラムで使用する場合に適しており、Object COBOL の知識を必要としません。しかし、Java オブジェクトへアクセスできるのは、 Java Native Interface (JNI) 経由、または Object COBOL Java ドメインを使用する場合のみです。
このサポートは、mfcobol.runtime という Java クラスを経由して実現されます。これにより、COBOL プログラムのロード、呼び出し、およびキャンセルを行う関数が使用できるようになります。また、Java 配列を使用して COBOL プログラムへパラメータを渡すこともできます。mfcobol.runtime の関数は、配列をアンパックして、使用可能なフォームで COBOL プログラムへデータを渡します。図 2-1 は、Java プログラムが COBOL プログラムを呼び出してパラメータを 2 つ COBOL プログラムに渡す様子を示しています。

図 2-1: mfcobol.runtime を経由して COBOL プログラムを呼び出す Java プログラム
このテクノロジを使用する前に、 COBOL および Java 環境をセットアップする必要があります。詳しくは 『始める前に』 を参照してください。
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.so 中のプログラムをロードするには、次のようなコードを作成します。
{
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_ 関数の一覧は、『Class Library Reference』 マニュアルの『Java Classes for COBOL Support』に記載されています。
この項では、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 の Java ドメインサポートを使用して、COBOL から Java にアクセスすることもできます。この方法については、『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 + ")");
}
}
}
Server Express には、Java からの手続き型 COBOL の呼び出しのいくつかの方法を説明するデモプログラムが付属しています。これらのデモプログラムは、$COBDIR/demo/java/cobol の下のサブディレクトリにあります。各サブディレクトリには、該当するすべてのプログラムファイルと、プログラムを詳しく説明した、各デモ用のテキストファイルが含まれています。テキストファイルの名前は、demonstration-name.txt という形式です(demonstration-name はデモプログラムの名前を示します)。これらのデモプログラムは、Net Express にも付属しており、\Program Files\Micro Focus\Net Express\Base\Demo\Javademo\Cobol ディレクトリの下のサブディレクトリにあります。
以下の表は、デモプログラムを含むディレクトリと、そのデモの目的の概要です。
| ディレクトリ
|
デモの目的
|
|---|---|
| arrays | COBOL から Java 配列を読み込む |
| COBOL から Java 配列を更新する | |
| pi | pi を計算する |
|
Strings 経由で数を Java に返す |
|
| primtypes | COBOL に初歩的な Java 型を渡す |
| COBOL から 初歩的な Java 型を更新する |
Copyright © 2002 Micro Focus International Limited. All rights reserved.
本書、ならびに使用されている固有の商標と商品名は国際法で保護されています。
![]() |
はじめに | COBOL からの Java の呼び出し |