こんにちは。長年、金融や流通の基幹システムでIBMメインフレームと向き合ってきたシステムアーキテクトの私だ。
レガシーシステムのモダナイゼーションやクラウド移行が叫ばれる昨今だが、現場の第一線では、いまなおCOBOLやPL/Iで書かれたオンライン・バッチプログラムが日本経済の心臓部を動かし続けている。特にCICS(Customer Information Control System)を組み合わせたオンライン画面処理において、VSAMファイルへのアクセスと例外処理の作法を誤ると、生産現場で取り返しのつかないデータ破損やトランザクションのデッドロックを引き起こす。
今回は、PL/IによるCICSプログラミングにおいて、多くの開発者がハマりがちな「`EXEC CICS HANDLE CONDITION`による例外制御と、PL/Iのデータ制御・スコープの罠」について、現場の知見を交えて徹底的に解説しよう。
—
1. PL/IとCICS例外制御における「最大の罠」
PL/Iの言語仕様を知り尽くしたベテランほど、CICSの例外処理設計で頭を抱える瞬間がある。それは、「PL/I本来の強力な例外処理機構(`ON-unit`)と、CICS独自の例外制御(`HANDLE CONDITION`)が、メモリ上でどのように競合するか」という点だ。
PL/Iには、ゼロ割りやデータ変換エラーなどを捕捉する洗練された`ON条件`(`ON ERROR`, `ON CONVERSION`など)が存在する。しかし、CICS環境下(EXEC CICS直下)で発生する `NOTFND`(VSAMレコードなし)や `DUPREC`(重複キー)といったトランザクション系の例外は、PL/Iの`ONユニット`ではなく、CICSランタイムが管理する「コンディション・ハンドラー・スタック」によって制御される。
ここで、PL/Iのブロック構造(手続きやBEGINブロック)とCICSの`HANDLE CONDITION`の有効範囲(スコープ)を混同すると、「エラーが発生したのに意図しないパラグラフへジャンプし、デバッグに数日を費やす」という悪夢を見る羽目になる。
HANDLE CONDITIONのスコープを正しく理解する
`EXEC CICS HANDLE CONDITION`は、「それを実行したプログラム(正確にはCICSの論理レベル)の配下において、そのタスクが異常終了せずに特定の条件を捕捉するための宣言」だ。
- 動的スコープの原則: `HANDLE CONDITION`は静的なソースコードの位置ではなく、実行時(ダイナミック)にどのルーチンを通ったかで有効範囲が決まる。
- サブプログラムへの伝播: メインのPL/I手続きから別の子手続き(サブ・プロシージャ)を呼び出した際、親で有効だった`HANDLE CONDITION`は、子の中でも明示的に無効化(`IGNORE`または別の`HANDLE`)されない限り引き継がれる。これが原因で、サブルーチン側で予期せぬCICS異常分岐が発生する事故が後を絶たない。
—
2. 【実践】VSAMアクセスとHANDLE CONDITIONを伴うPL/Iコード例
百聞は一見にしかずだ。実際の現場でそのまま使える、VSAMのKSDS(キー順データセット)からレコードを読み込み、存在しない場合の例外(`NOTFND`)を安全に捕捉するPL/Iプログラムの構造を見てほしい。
IBMメインフレーム標準のコーディング規約に則り、すべて大文字、かつ適切なインデントと日本語コメントを付与している。
1
/================================================================/
/ プログラム名: CUSTSRCH /
/ 概要 : CICSオンライン環境におけるVSAMレコード検索処理 /
/ EXEC CICS HANDLE CONDITIONによる例外制御の実例 /
/================================================================/
CUSTSRCH: PROC(DFHCOMMAREA) OPTIONS(MAIN REENTRANT);
DCL DFHCOMMAREA CHAR(100); / 通信エリア /
/ ワーク変数の定義 /
DCL WS-CUST-ID CHAR(5) INIT(‘ ‘);
DCL WS-MSG CHAR(40) INIT(‘ ‘);
DCL RESP-CODE FIXED BIN(31) INIT(0);
/ 顧客マスターレコードの構造体(コピーブック展開を想定) /
DCL 1 CUST-RECORD,
3 CUST-ID CHAR(5),
3 CUST-NAME CHAR(30),
3 CUST-STATUS CHAR(1);
/ 1. CICS例外条件のハンドル設定 /
/ NOTFND(レコード未検出)の場合はラベル ‘CUST-NOT-FOUND’ へ制御を移す /
EXEC CICS HANDLE CONDITION
NOTFND(CUST-NOT-FOUND)
ERROR(CICS-ERROR-ROUTINE);
/ 入力パラメータの取得処理(省略形) /
WS-CUST-ID = SUBSTR(DFHCOMMAREA, 1, 5);
/ 2. VSAM KSDSからのレコード読み込み(READ) /
EXEC CICS READ
DATASET(‘CUSTFILE’)
INTO(CUST-RECORD)
RIDFLD(WS-CUST-ID)
RESP(RESP-CODE);
/ 通常正常終了時の処理 /
WS-MSG = ‘顧客情報が正常に取得されました。’;
GO TO SEND-MAP;
/—————————————————————-/
/ 例外処理 1: NOTFND(該当レコードなし) /
/—————————————————————-/
CUST-NOT-FOUND:
/ 重要: 別のCICSコマンドを発行する前に、必要に応じてHANDLEをリセットする /
/ 今回は単にメッセージを設定して画面へ抜ける /
WS-MSG = ‘指定された顧客IDは存在しません。’;
GO TO SEND-MAP;
/—————————————————————-/
/ 例外処理 2: CICS全体のエラー(ERRORコンディション) /
/—————————————————————-/
CICS-ERROR-ROUTINE:
/ システム異常などのクリティカルエラー時にabendを回避しログを記録 /
WS-MSG = ‘システムエラーが発生しました。管理者へ連絡してください。’;
/ 実際にはここでDUMP取得やログ出力を行う /
SEND-MAP:
/ 画面へメッセージを返却する処理(説明のため簡略化) /
/ … 処理終了 … /
RETURN;
END CUSTSRCH;
—
3. ベテランが教えるデバッグとコーディングの鉄則
上記のコードを見て、「おや?」と思った鋭い後輩もいるかもしれない。実務において`HANDLE CONDITION`を使う際、私はチームメンバーに必ず以下の3点を「鉄則」として指導している。
鉄則その1: `RESP`オプション併用時の注意点
CICSコマンドに `RESP(RESP-CODE)` を指定すると、`HANDLE CONDITION` よりも `RESP` による戻り値のチェックが優先される…と言いたいところだが、実は仕様上、`HANDLE CONDITION` がコーディングされていると、エラー発生時に `RESP` 値がセットされる前に自動分岐(GO TO動作)が優先されてしまうケースがある。
そのため、モダンなCICSプログラミングでは、`HANDLE CONDITION` を使わずにすべて `RESP` オプションで戻り値を判定し、自前で `IF` 分岐を書くスタイルが主流になりつつある。もし `HANDLE` を使うなら、プログラム内でポリシーを完全に統一すること。
鉄則その2: 意図しない「引き継ぎ」を防ぐ `NOHANDLE` の活用
子手続きや共通サブルーチンを呼び出す際、親で設定した `HANDLE CONDITION` がそのまま生き残り、予期せぬ場所でループや意図せぬラベルへのジャンプを引き起こすことがある。
これを防ぐためには、例外が発生してもプログラム側で制御したい個別のCICSコマンドには、`NOHANDLE` を明示的に付与するか、一時的に `EXEC CICS HANDLE CONDITION IGNORE` で無効化する防衛的プログラミングが不可欠だ。
鉄則その3: PL/Iのビルトイン関数によるデータ検証
CICSへ渡す前段階のデータ制御において、PL/Iの強力なビルトイン関数(`VERIFY`, `TRANSLATE`など)を活用して入力値を厳密にバリデーションすること。CICS側の例外制御に頼り切るのではなく、アプリケーション層で弾けるエラーは事前に弾くのが、可用性の高いメインフレームシステムを作る極意である。
—
おわりに
PL/IとCICSを組み合わせたシステム開発は、一見すると古めかしく見えるかもしれない。しかし、その背後にあるメモリ管理、トランザクション制御、そして例外処理の緻密な仕組みは、現代の分散システムやマイクロサービスアーキテクチャにおいても通じる「堅牢性への思想」に満ちている。
仕様の隙間を縫うようなバグに直面したときこそ、言語仕様とCICSランタイムの挙動に立ち返ってみてほしい。君たちの手元にあるそのPL/Iソースコードは、正しく扱えば世界で最も信頼性の高いコードベースの一つなのだから。
