イトウ アスカ blog このページをアンテナに追加 RSSフィード

2009-03-18

COBOL ってすべてスレッドセーフじゃないのか?!

 OpenCOBOL で C ソースへの変換結果を見てみたら、WORKING-STORAGE が static 変数になってたんですよ。私の少ない他の COBOL 経験から考えても、たしかに WORKING-STORAGE は static 変数だったような……。

 と、いうことはです! こいつらを何も考えずに JavaEE 環境下に引きずり込んでマルチスレッドで使うのは自殺行為ということですね! 「WORKING-STORAGE を使わないで!」なんて言ったら、COBOLer に「寝言は寝て言え」と言われるのは目に見えてますし!

 ということで、ブリッジプログラム側などで同期化しなきゃいけないってことですね。

package bridge;

public class CobolBridge {
    static {
        // ライブラリのロード
        System.loadLibrary("j2cob");
    }

    /**
     * COBOL モジュールの初期化を行います。
     */
    public synchronized native void init();

    /**
     * COBOL モジュールを呼び出します。
     * 
     * @param moduleName
     *            COBOL モジュール名
     * @param parameter
     *            COBOL モジュールへのパラメータ
     * @throws CobolModuleException
     *            COBOL モジュールの呼び出しに失敗した場合
     */
    public synchronized native void callCobol(String moduleName,
           byte[] parameter) throws CobolModuleException;

 
    public static void main(String[] args) {
         CobolBridge cobolBridge = new CobolBridge();

         byte[] name = "Hoge      ".getBytes();

         cobolBridge.init();
         try {
             cobolBridge.callCobol("hello", name);
         } catch (CobolModuleException e) {
             e.printStackTrace();
         }
    }
}

2009-03-17

さらにブリッジプログラムをマシにする

 昨日のエントリのブリッジプログラムは、異常系の処理があまりにもやっつけだったので少し改良したいと思います。具体的には、COBOL モジュールが見つからなかった場合に例外をスローするようにします。

 まず、スローする例外は以下のようにします。

package bridge;

public class CobolModuleException extends Exception {
    public CobolModuleException(String message) {
        super(message);
    }
}

 これ踏まえて、COBOL モジュールを呼び出す Java 側のプログラムは以下のようになります。

package bridge;

public class CobolBridge {
    static {
        // ライブラリのロード
        System.loadLibrary("j2cob");
    }

    /**
     * COBOL モジュールの初期化を行います。
     */
    public native void init();

    /**
     * COBOL モジュールを呼び出します。
     * 
     * @param moduleName
     *            COBOL モジュール名
     * @param parameter
     *            COBOL モジュールへのパラメータ
     * @throws CobolModuleException
     *            COBOL モジュールの呼び出しに失敗した場合
     */
    public native void callCobol(String moduleName, byte[] parameter)
           throws CobolModuleException;

	
    public static void main(String[] args) {
         CobolBridge cobolBridge = new CobolBridge();

         byte[] name = "Hoge      ".getBytes();

         cobolBridge.init();
         try {
             cobolBridge.callCobol("hello", name);
         } catch (CobolModuleException e) {
             e.printStackTrace();
         }
    }
}

 throws 宣言が追加されても C のヘッダファイルに変更はありませんので、javah を実行する必要はありません。

 C プログラムは以下のようになります。前回は単に標準エラー出力エラーメッセージを出していましたが、今回は例外をスローするようになっています。Java 側でリファクタリングすることを考えると、本当は例外クラス名はプリプロセッサ定数にして Makefile あたりで定義した方がいいかもしれません。

#include <libcob.h>
#include "bridge_CobolBridge.h"

/* 動的 COBOL モジュールのプロトタイプ宣言 */
static int (*cobMod)(char *);

JNIEXPORT void JNICALL Java_bridge_CobolBridge_init
  (JNIEnv *env, jobject obj)
{
    /* COBOL プログラムの初期化 */
    cob_init(0, NULL);
}

JNIEXPORT void JNICALL Java_bridge_CobolBridge_callCobol
  (JNIEnv *env, jobject obj, jstring modName, jbyteArray param)
{
    /* モジュール名の取得 */
    const jbyte *mn = (*env)->GetStringUTFChars(env, modName, NULL);

    /* モジュールの取得 */
    cobMod = cob_resolve((char *)mn);

    /* モジュール名資源のリリース */
    (*env)->ReleaseStringUTFChars(env, modName, mn);

    if (cobMod == NULL) {
        /* 例外をスロー */
        jclass e = (*env)->FindClass(env, "bridge/CobolModuleException");
        (*env)->ThrowNew(env, e, cob_resolve_error());
        return;
    }
    /* 引数の配列の参照を取得 */
    jbyte *p = (*env)->GetByteArrayElements(env, param, NULL);
    /* COBOL プログラムの実行 */
    cobMod((char*)p);
    /* 資源のリリース */
    (*env)->ReleaseByteArrayElements(env, param, p, 0);
}

2009-03-16

もうちょっとマシなブリッジプログラムを作る

 つつましい COBOLer 、いや、COBOList の方々に COBOL プログラム作ったらブリッジの C プログラムも作っておいてなんて言ってもたぶん作りたがらないでしょう。なので、動的に COBOLモジュールを呼び出すブリッジプログラムを作りましょう。

 Java 側の仕様としては、まず最初に初期化処理を呼び出し、以降、モジュール名とパラメータCOBOLモジュールを呼び出すというものです。C のブリッジプログラムは、libj2cob.so というダイナミックリンク・ライブラリとします。

package bridge;

public class CobolBridge {
    static {
        // ライブラリのロード
        System.loadLibrary("j2cob");
    }

    /**
     * COBOL モジュールの初期化を行います。
     */
    public native void init();

    /**
     * COBOL モジュールを呼び出します。
     * 
     * @param moduleName COBOL モジュール名
     * @param parameter COBOL モジュールへのパラメータ
     */
    public native void callCobol(String moduleName, byte[] parameter);

    public static void main(String[] args) {
        CobolBridge cobolBridge = new CobolBridge();

        byte[] name = "Hoge      ".getBytes();

        cobolBridge.init();
        cobolBridge.callCobol("hello", name);
    }
}

 例のごとく javah でヘッダファイルを生成して、次は C のプログラムです。


#include <libcob.h>;
#include "bridge_CobolBridge.h"

/* 動的 COBOL モジュールのプロトタイプ宣言 */
static int (*cobMod)(char *);

JNIEXPORT void JNICALL Java_bridge_CobolBridge_init
  (JNIEnv *env, jobject obj)
{
    /* COBOL プログラムの初期化 */
    cob_init(0, NULL);
}

JNIEXPORT void JNICALL Java_bridge_CobolBridge_callCobol
  (JNIEnv *env, jobject obj, jstring modName, jbyteArray param)
{
    /* モジュール名の取得 */
    const jbyte *mn = (*env)->GetStringUTFChars(env, modName, NULL);

    /* モジュールの取得 */
    cobMod = cob_resolve((char *)mn);

    /* モジュール名資源のリリース */
    (*env)->ReleaseStringUTFChars(env, modName, mn);

    if (cobMod == NULL) {
        fprintf(stderr, "err %s\n", cob_resolve_error());
    } else {
        /* 引数の配列の参照を取得 */
        jbyte *p = (*env)->GetByteArrayElements(env, param, NULL);
        /* COBOL プログラムの実行 */
        cobMod((char*)p);
        /* 資源のリリース */
        (*env)->ReleaseByteArrayElements(env, param, p, 0);
    }
}

 これを機に Makefile も作ってしまいましょう。

TARGET=libj2cob.so
OBJS=bridge_CobolBridge.o

# JDK のありかは適宜修正してください
JAVA_HOME=/usr/local/java/latest
CC=/usr/bin/gcc
CFLAGS=-fPIC -g -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux/ `cob-config --cflags`
LIBS=`cob-config --libs`

all: $(TARGET)

clean:
    rm -f *.o $(TARGET)

$(TARGET): $(OBJS)
    $(CC) --shared -o $@ $^ $(LIBS)

.c.o:
    $(CC) $(CFLAGS) -c $<

 これで、make コマンドを一発たたけば libj2cob.so ができあがります。

 最後に COBOL です。今度はダイナミックリンク・ライブラリとしてビルドしなければなりませんので、前回の hello.cob を使い回しますが以下のように -m オプションをつけてビルドします。これで、hello.so ができあがります。libhello.so じゃありません。

$ cobc -m hello.cob

 実行時の注意としては、libj2cob.so は Java が呼びますので、Java のシステムプロパティ java.library.path 上になければなりませんが、hello.cob はそれとは関係なく、一般の C のライブラリパスに置いておく必要があります。とりあえずカレントディレクトリに置いておけば動きます。

Java 側から COBOL 側にパラメータを渡す。

 ほとんど COBOL とは関係ない世界になってきていますが、オープン系技術者の活躍どころです。

 前回までの内容でできることといったら Java をトリガとして COBOL プログラムを走らせるぐらいで、面白みというか有用性がほとんどありません。今回は、Java から COBOLパラメータを渡す方法をご紹介します。これができれば、Java の豊富なインタフェースを用いて COBOLパラメータを渡せるようになるので使いでがいっきに広がると思います。EJBCOBOL とか、Web サービスで COBOL とか。

 なお、コンパイル方法は前回と同じですのでそちらを参照してください。

 まず、こんな感じの COBOL プログラムがあるとします。外部から 10 バイトの文字列を受け取って、Hello, なんとかと表示するプログラムです。

       IDENTIFICATION DIVISION.
       PROGRAM-ID. hello.
       DATA DIVISION.
       LINKAGE SECTION.
       01 NAME PIC X(10).
       PROCEDURE DIVISION USING NAME.
       DISPLAY "Hello, " NAME.
       EXIT PROGRAM.

 次に Java 側です。byte 配列パラメータを渡します。注意しなければならないのは、後ろに空白を入れて 10 バイトに合わせなければならないことです。COBOL 界ではあたり前のことですけどね。

package bridge;

public class HelloCobol {
    static {
        // ライブラリのロード
        System.loadLibrary("Hello");
    }

    // ネイティブメソッドの宣言
    public native void sayHello(byte[] name);

    public static void main(String[] args) {
        HelloCobol helloCobol = new HelloCobol();
        byte[] name = "Hoge      ".getBytes();
	
        helloCobol.sayHello(name);
    }
}

 ネイティブメソッドの宣言が前回と違うので、javah コマンドで再度ヘッダファイルを生成しなおしてください。

 最後に C のプログラムです。COBOL プログラムは、リンケージセクションのレベル 1 のものがそのまま引数になります。Java からの引数は、第3引数の jbyteArray になります。これは直接参照できないので、下記の方法で jbyte 配列の先頭ポインタを取得します。jbyte は、unsigned char と同義です。最後に資源のリリースを行います。これによって nameRef の資源が開放され、その内容が Java 側に反映されます。

#include <libcob.h>
#include "bridge_HelloCobol.h"

/* COBOL プログラムのプロトタイプ宣言 */
extern int hello(char*);

JNIEXPORT void JNICALL Java_bridge_HelloCobol_sayHello(JNIEnv *env, jobject obj, jbyteArray name) {
    /* 引数の配列の参照を取得 */
    jbyte *nameRef = (*env)->GetByteArrayElements(env, name, NULL);

    /* COBOL の初期化 */
    cob_init(0, NULL);

    /* COBOL プログラムの実行 */
    hello((char*)nameRef);

    /* 資源のリリース */
    (*env)->ReleaseByteArrayElements(env, name, nameRef, 0);
}

 あとは前回と同じようにコンパイル、リンクを行えば動きます。

2009-03-15

JNI と C を使って Java から COBOL プログラムを呼び出す

 さて、今度は、COBOL に Hello, world! を言わせましょう。

 まずは、COBOL プログラムを以下のようにつくります。ファイル名は hello.cob とします。

       IDENTIFICATION DIVISION.
       PROGRAM-ID. hello.
       PROCEDURE DIVISION.
       DISPLAY "Hello, world!".
       EXIT PROGRAM.

 コンパイルを行います。リンクは行いません。

$ cobc -c hello.cob

 次に JavaCOBOL の間を取り持つ C のプログラムを作ります。前回の bridge_HelloCobol.c を書き換える形です。

#include <libcob.h>
#include "bridge_HelloCobol.h"

/* COBOL プログラムのプロトタイプ宣言 */
extern int hello(void);

JNIEXPORT void JNICALL Java_bridge_HelloCobol_sayHello(JNIEnv *env, jobject obj) {
    /* COBOL の初期化 */
    cob_init(0, NULL);

    /* COBOL プログラムの実行 */
    hello();
}

 コンパイルを行います。cob-config コマンドを使って、OpenCOBOL 用のオプションを与えている点に注意してください。

gcc -fPIC -g -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ `cob-config --cflags` -c bridge_HelloCobol.c

 最後にリンクします。ここでも cob-config コマンドを使って、OpenCOBOL 用のオプションを与えています。

$ gcc --shared `cob-config --libs` -o libHello.so *.o

 これで出来上がった libHello.so は、前回のエントリと同じ Java プログラムから呼び出すことができます。

2009-03-14

Ubuntu で OpenCOBOL

 COBOLCOBOLer)の延命治療のため粉骨砕身しているオープン系技術者のみなさんこんにちは。

 私も30を過ぎたころから少しずつ人が丸くなり、COBOL に対する憎悪を燃やしてばかりでもなくなりました。ということで、今日は私のようにやむを得ず COBOL と付き合うはめになった人のために Ubuntu で OpenCOBOL を使う方法のさわりの部分を紹介したいと思います。

 と言っても私もまだ触りたてなんですけどね。いっしょに勉強していきましょう。

 OpenCOBOL は、その名のとおりオープンソースCOBOL処理系で、gccトランスレータとして動作するそうです。昔の C++ みたいですね。

 まずは、インストールしましょう。パッケージはたぶんないので、ソースからビルドします。入手は以下のサイトからどうぞ。

http://sourceforge.net/project/showfiles.php?group_id=34923

 ビルドには gcc が必要なのはもちろんなのですが、私の環境では libgmp3-dev と libdb-dev もインストールする必要がありました。

$ sudo apt-get install libgmp3-dev

$ sudo apt-get install libdb-dev

 あとは、簡単。解凍したディレクトリで定番のコマンドを打つだけです。インストール先は /usr/local になります。

$ ./configure

$ make

$ sudo make install

 このままだと、COBOL の共有ライブラリが使えないので、キャッシュの最新化が必要です。

$ sudo ldconfig

 これで COBOL コンパイラCOBOL 関連の共有ライブラリインストールが終わりました。

 では、お決まりの Hello, World! をやってみましょう。hello.cob という名前で以下のようなファイルを作ります。

       IDENTIFICATION DIVISION.
       PROGRAM-ID. hello.
       PROCEDURE DIVISION.
       DISPLAY "Hello, world!".
       STOP RUN.

 コンパイルは以下のようにします。これで、hello という実行ファイルができます。

$ cobc -x hello.cob

 実行してみましょう。

$ ./hello
Hello, world!

 これで COBOL 界への門戸が開きました。