タブの始まり
上記リンクをクリックすると、ページ内の該当箇所に移動します
超入門「COBOL」
JavaとCOBOL、二つの世界はまったく相容れないもののように思えます。この二つを連携させる技法について解説するわけですが、その前にCOBOLという言語がどんなものかをおさらいしておきましょう。細かな話は置いておき、ここではCOBOLがいかにJavaと違っているかを、Javaプログラマー向けに端的に説明することを試みます。
Javaの普及につれてプログラミング言語に対する常識も変化してきたように見受けられます。プラットフォームに依存しないバイトコードはJavaの最大の特徴ですが、少なくともJavaが登場するまでは、これはプログラミング言語の常識ではありませんでした。COBOLやC、FORTRANといった言語はほとんどの場合、コンパイラによってターゲットマシンのネイティブコードに変換され、オペレーティング・システム上の実行形式モジュールにリンクされて実行されます。「えっ?パソコンでコンパイルしたプログラムをUNIXマシンにコピーして動作させることはできないの?」そんな素朴な疑問も聞かれるようになってきました。Javaの影響力の大きさを物語っています。
さて、プログラミングの面から見ると、JavaとCOBOLの最も大きな違いは、COBOLが徹底的にstaticな言語であることです。基本的にはCOBOLプログラムは、main()メソッドとstaticなメンバーのみを持っているJavaクラスみたいなものです。2002年に国際標準化されたCOBOLの最新規格では、COBOLもオブジェクト指向になっていて、クラスメソッドの定義や動的なインスタンス化ができるようになっています。しかし現存する膨大なCOBOL資産のほとんどはそんなふうに書かれてはいません。
もう一つ、COBOLの極めて特徴的な点は、非常に多くの機能を言語の文法でサポートしていることです。JavaやC、C++では、コンパイラが構文解析する文法の要素はCOBOLに比べて非常に少ないです。言語としてのJavaの文法は、パッケージやクラス、メソッドなどの宣言を行う基本的なプログラムの枠組みに、ループや条件分岐などの制御構造を表す構文、それに若干の既定義データ型(基本型)、これがすべてです。にもかかわらずJavaプログラマーがシステム資源にアクセスするために豊富な手段を享受できるのは、文法以外に用意された膨大なクラス・ライブラリーがあるからです。これに対して、COBOLは、時代の要求にこたえるためにデータ処理に必要な機能を次々に言語に取り込んで40年以上発展し続けてきました。
例えば、COBOLにはファイルのソートを行うSORT文、文字列の連結を行うSTRING文などが文法としてサポートされています。
レガシーのCOBOL資産は手強いか?
次にCOBOLのデータ型についてです。COBOLでは数字データを表現するために非常に豊富なバリエーションの型が利用できます。
Javaでは、プログラマーが操作の対象とするのはオブジェクトであり、例えばBigDecimalのオブジェクトは、持っている桁数で表現される抽象的な数値のみを表しています。ところが、COBOLプログラマーは自分が宣言した数字タイプの変数(COBOLでは「データ項目」と呼びます)が、メモリ上で物理的にどのようなバイト並びで格納されているかを知っています。例えば「パック十進形式」という数字のデータ型があります。これは1バイトを2つの4ビットのフィールドで区切って、それぞれに10進数の位取りを格納するものです。
また、COBOLでは複数の基本型の変数を階層構造でまとめて一つの複合構造を定義できます。これを「レコード」と呼びます。Javaで複数の基本型メンバーを持つクラスを作成して処理するのに似ていますが、COBOLのレコードはメモリ上の物理的なレイアウトが意識されています。宣言された順番にメモリ上に並んでいることが言語として保証されているので、プログラマーはそれを意識したプログラミングを行うことができます。例えば「再定義」という機能を使うとメモリ上の同じ領域を異なる形式のレコードとして取り扱うことができます。
こんな「物理的な」データ形式を前提として、外部からデータを受け取るようなレガシーのCOBOLプログラムが大量に存在しています。このようなCOBOL資産にJ2EE環境からデータを受け渡して動作させることができるのか?とちょっと不安になりませんか。
さらに、半角カタカナという意外な強敵がいます。メインフレームでは歴史的な理由で半角カタカナが多用されてきていて、今でも多く利用されています。メインフレームでは英数字と半角カタカナは一文字1バイト、漢字は一文字2バイトでしたので、多くのCOBOLプログラムはこのことを前提にして書かれています。この前提はUnicodeになった途端に崩れます。
COBOLのアプリケーション・サーバー
これらの不安はこの後のパートで解消します。しかし、他にも不安を感じた方がいるかもしれません。
COBOLはstaticな言語です、と書きましたが、それではどうやってマルチスレッドのJ2EEアプリケーションと共存できるのでしょうか。もっともな疑問です。
JCAやWebサービスでJ2EEからCOBOLロジックを呼び出す場合には、COBOLプログラムはJ2EEの外側で、COBOL専用のアプリケーション・サーバー(以下、COBOLサーバー)
(※1)の上で動作させます。COBOLサーバー提供JCAリソース・アダプターはJ2EE側にいてJavaアプリケーションが発行するJCAサービス要求を、Socketを経由してCOBOLサーバーリスナーに送信します。COBOLサーバーマネジャーはサービス要求を受信すると、その配下で管理しているプロセスプールから待ち状態になっているプロセスに処理を振り分け、COBOLプログラムを実行させます。このプールされたプロセスをCOBOLコンテナと呼んでいます。[図1]
COBOLサーバーはスレッドプール方式ではなくプロセスプール方式なので、staticなCOBOLプログラムでも問題なく並行稼働させることができるのです。このやり方はCICSなどのCOBOL言語をサポートするTPMonitorがどれも採用している方法です。
※1:マイクロフォーカスではCOBOL専用アプリケーションサーバーとして「Micro Focus Enterprise Server」を提供しています。

図1 Java ConnectorとしてのCOBOLサーバー
Webサービスの場合も同様です。この場合はCOBOLサーバーが装備しているSOAPリスナーがサービス要求を受け付けます。COBOLプログラムをCOBOLサーバーにWebサービスとしてディプロイするときには、COBOLのディプロイ・ツールがWSDLを自動生成してくれますので、J2EEアプリケーションからはCOBOLであると意識することなしに呼び出すことができます。
当然ながらCOBOLサーバーは、J2EEと同じマシンで稼働している必要はありません。
またJ2EEと同じ機種やOSで稼働している必要もありません。例えばJ2EEはLinux、COBOLはWindows、データベースはAIXというような構成も可能です。COBOLサーバーはCOBOL専用のアプリケーション・サーバーですので、COBOLのことは大得意です。サーバー下で動作するサービスを対話型デバッガでステップ実行してデバッグすることもできます。また、COBOL特有の例外が発生した場合も、サーバーが監視しており、適切な再ロギングの後にCOBOLコンテナを初期化して運転を続行します。
JavaからレガシーCOBOLにデータを受け渡しするには?
この連載の第一回で、JCAを使用してCOBOLサーバー上のサービスを呼び出すJavaプログラミングの実例を見ました。これで呼び出せることはわかりましたが、どうもレガシーのCOBOL資産はそう簡単に使いこなせないのではないか、という不安があります。
始めからJ2EEから呼び出されることを意識して書かれたCOBOLプログラムならば、入り口点・出口点で受け渡しするデータをJavaプログラマーに対して親切に作るかもしれませんが、もともとCOBOLからしか呼び出されない前提で書かれたプログラムであれば、COBOL特有の「ややこしい」データ形式を含んでいるかも知れません。この問題はCOBOLサーバーのサービス・ディスパッチャーとディプロイ・ツールに相当する開発環境が解決してくれます。
COBOLサーバーマネジャーは、サービス要求を受信すると空いているCOBOLコンテナに処理をディスパッチしますが、このときにJ2EEアプリケーションから渡されてきた入力データを、COBOLのデータ型に変換します。例えば、Javaの基本数字型とBigDecimalオブジェクトは、それが表現する数学的な数値を基にして、対応するCOBOLの数字型に変換されます。StringオブジェクトはUnicodeからCOBOLサーバーの実行ロケールにあわせてコード変換されCOBOLの文字型に格納されます。byte[]を使用して無変換でバイナリデータを渡すことも可能です。この型変換の規則は開発者によって自由に設定することもできます。レガシーCOBOLプログラムの入出力データをJavaからどのように取り扱うかは、Java、COBOL双方のプログラマーの都合で変わりますので、事情に合わせて自由に対応付けを設定できる必要があります。
COBOLサーバーのためのディプロイ・ツールを使用すると、既存のCOBOLプログラムに記述されている入出力データの定義に、Javaのデータ型をどのように対応付けるかをポイント&クリックで定義することができます。(※2)この定義情報は、COBOLサーバーへのディプロイ時にコンパイル済みCOBOLプログラムとともにサーバーに配備されますので、COBOLサーバーのディスパッチャーはこれを参照して正しい型変換を行います。[図2]

図2
※2:マイクロフォーカスではCOBOLサーバーのためのディプロイ・ツールとして「Interface Mapping Toolkit」をCOBOL開発環境に含めて提供しています。
Java開発者にやさしいCOBOL環境
さて、JCAを使用したJavaプログラミング自体は、ConnectorのJavaAPI仕様にしたがってコーディングすれば良いわけですが、COBOLサーバー上のサービスを呼び出す目的であればある程度定型的なコーディングとなります。逆に必ず行わなければいけない決まり事もあります。そこで、COBOLサーバーのためのディプロイ・ツールは、ディプロイ時に同時に、このJava側のコーディングも自動生成してくれます。幸いにもディプロイ・ツールは、開発者が指定したJavaデータ型とCOBOLデータ型との型変換規則を知っていますので、規則に従ってJavaからCOBOLにデータを渡す順番も正しく生成します。生成されたJavaコードは、一つの完成されたEJBとしてプログラマーに提供されます。このEJBにはビジネスロジックとしてJCA経由でCOBOLサーバーにサービス要求を投げるメソッドが含まれていますから、プログラマーはJCAのクラス仕様を一切気にせずに単にこのメソッドにJavaデータ型のインスタンスを添えて呼び出すだけです。これであたかもCOBOLプログラムを直接呼び出したように処理が行えるわけです。なお、ディプロイ・ツールはこのEJBを.jarや.earにまでパッケージ化して生成してくれます。お使いのJ2EEアプリケーション・サーバーによってディプロイメント記述子の形式が異なりますが、生成時のオプションでWebSphereと指定すればWebSphereにそのままインストールできるパッケージが生成されます。[図3]

図3
レガシーCOBOLプログラムは往々にして膨大な量の入出力データを持っています。普通はレコードとして受け渡ししますが、基本型で数百個というのはざらにあります。これをそのままJava基本型に対応付けてEJBを生成すると、数百のインスタンスを持つEJBメソッドが生成されてしまいます。これでは使えません。
このような場合に対応するためにディプロイ・ツールはCOBOLレコードを一つのJavaクラスに対応付けることをサポートしています。COBOLレコード中の個々の基本型はJavaクラスのメンバーに対応します。この方法によれば、Java側では各メンバーに値をセットしてからEJBメソッドのクラスインスタンスとして渡すことができます。
トランザクション・マネジャーとしてのCOBOLサーバー
J2EEでは、JCAはJTAトランザクションのリソースマネジャーとしてサポートしなければならないことが規定されています。従ってCOBOLサーバー提供JCAリソース・アダプターもJTAのXATransactionインターフェイスを実装しています。これを使用すると、J2EE側のトランザクションスコープと、COBOLサービス内でのデータベース更新とが同期します。J2EE側でトランザクションがロールバックされれば、COBOLサーバー上のデータベース更新もロールバックされます。
また、COBOLサービスの実行時に例外が発生すれば、それはJ2EE側にも例外としてスローされますのでJTAトランザクションもロールバックされます。つまりCOBOLサービスの呼び出しもJTAの分散トランザクションの一つとして自然に2層コミットの対象になっているわけです。COBOLプログラマーは従来どおりの方式でトランザクションプログラムを書くことができ、JavaプログラマーはJTAに従って、JDBCやJMSのトランザクションと混在してCOBOLサービスを使用することができます。
どうでしょうか?JavaとCOBOLは二つの相容れない世界ではなくなっていることがわかってきましたね。
次回予告
最終回である次回は、再び日本アイ・ビー・エム様にバトンを渡し、「COBOLプログラム活用のHint&Tips」と題して、このテクノロジーに関するJ2EE側の注意点などを語っていただきます。ご期待ください。
IBM、IBMロゴ、WebSphereおよびCICSはInternational Business Machines Corporationの米国およびその他の国における商標です。
他の会社名、製品名およびサービス名等はそれぞれ各社の商標または登録商標です。
