前へはじめに COBOL から Java を呼び出す次へ

第 2 章 Java から手続き型 COBOL を呼び出す

この章では、Object COBOL ドメイン サポートを使用しないで Java から既存の手続き型 COBOL にアクセスする方法について説明します。

2.1 概要

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 プログラムに渡す様子を示しています。

Java から COBOL への呼び出し

図 2-1: mfcobol.runtime を経由して COBOL プログラムを呼び出し中の Java プログラム

この技術を効果的に使用するには、少なくとも Java 言語の基本知識が必要です。Java については、Sun Microsystems の Java サイトを参照することをお勧めします。

2.2 設定

COBOL と Java の環境設定方法については、「Java から Object COBOL を呼び出す」の章の「Java/COBOL サポートの設定」の項を参照してください。

2.3 Java プログラムのコーディング

Java プログラムは、mfcobol.jar にある runtime.class 内の関数を使用して COBOL のエントリポイントを呼び出します。このクラスには、cobcall_returntype() という名前の関数のセットがあります。returntype は、呼び出す COBOL プログラムまたはエントリポイントによって戻されるデータ型です。たとえば、整数を戻す COBOL プログラムを呼び出すには、cobcall_int() を使用します。

Java プログラムで COBOL サポートを使用できるようにするには、Java ソース ファイルの先頭に次の文を含めてください。

import mfcobol.*; 

2.3.1 マルチスレッドの注意事項

Java ランタイム システムはマルチスレッドなので、Java とともに使用する COBOL プログラムは、COBOL マルチスレッド ランタイム システムにリンクする必要があります。これは、COBOL プログラムを呼び出す Java プログラムがマルチスレッドを使用するかどうかに関わらず必要です。マルチスレッドの Java プログラムから COBOL プログラムを呼び出す場合は、スレッドからアクセスされる COBOL のデータが別のスレッドによって破壊されないように注意する必要があります。

この問題を避けるには、次のような方法があります。

2.3.2 COBOL プログラムまたはライブラリのロード

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 内の複数のプログラムにある場合は、これらのプログラムをそれぞれ順にロードする必要があります。たとえば、mycblcalcintcalctax の 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"} ;
   } 
}

2.3.3 cobcall() 関数の使用

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 オンラインマニュアル] を選択して、「クラス ライブラリ リファレンス」 のショートカット ボタンをクリックします。

2.3.4 Java から COBOL を呼び出す例

この項では、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.cbllegacy.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) ;
   }
}

2.3.5 Java オブジェクト内のデータ メンバーの変更

この項の例は前項の例に似ていますが、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.cbllegacy2.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) ;
   }
}

2.3.6 COBOL プログラムのキャンセル

メモリ リークを防ぐために、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());
    }
}

2.4 Java プログラムから文字列を処理する

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) ;

  }

}

2.5 COBOL で JNI を使用する

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 例外を発生させる場合です。

2.5.1 例外発生の例

次の簡単な 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 を呼び出す次へ