【入門編】CICSにおけるEXEC CICS START/RETRIEVEによる非同期処理 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダンな、あるいは別のビジネス言語をバリバリ書いてきた方にとって、初めて目にするPL/I(ピーエルアイ)のソースコードや、IBMのオンライン制御プログラムであるCICS(キックス)の世界は、ちょっと要塞のようにお堅く、近寄りがたく見えるかもしれませんよね。

「なんだこの暗号のような変数は…」「予約語がないってどういうこと!?」と、冷や汗をかいている方もいるのではないでしょうか。
でも、大丈夫です。安心してください。今回は、そんなレガシー特有の少しユニークなルールを優しく解きほぐしながら、CICSにおける非同期処理のキモである `EXEC CICS START` と `RETRIEVE` のコンビネーションについて、現場の空気感を交えながらじっくり紐解いていきます。

—

1. まずは心を穏やかに:PL/Iには「予約語」がない?

Javaなら `public` や `class`、COBOLなら `MOVE` や `DISPLAY` のように、「この単語はプログラミング言語のシステム専用だから、変数名に使っちゃダメだよ」という予約語(Reserved Words)が必ず存在しますよね。

ところが、PL/Iのすごさは(そして時に恐ろしいところは)、コンテキスト依存の言語であるという点です。つまり、厳密な意味での「予約語」という概念がほぼありません。
どういうことかというと、極端な話 `IF` という名前の変数を作ることすらできてしまいます(コンパイラが前後の文脈を必死に読んで「あ、ここは条件分岐の IF だな」「こっちは変数の IF だな」と健気に判断してくれるのです)。

とはいえ、現場でそんなトリッキーな名前をつける人はいませんから安心してください。JavaやCOBOL出身者であれば、「普段使っているキーワードを変数名にしちゃいけないんだっけ…?」といった余計な心配をせず、直感的にコーディングできる優しい言語、それがPL/Iなのです。

—

2. CICS非同期処理のドラマ:なぜ `START` と `RETRIEVE` が必要なのか?

さて、本題のCICSオンライン処理に入りましょう。
画面からユーザーがボタンを押したとき、その場でサクッと処理が完結するものばかりなら楽なのですが、現実はそう甘くありません。

  • 「重たい月次集計のバッチプログラムを裏でキックしたい」
  • 「別の端末(画面)へ、データをこっそり引き渡して自動で次画面を開かせたい」

こんなときに使うのが、非同期処理(タスクの切り離し)です。
親となるトランザクション(A)が、子となるトランザクション(B)を、自分の寿命とは関係なく独立して裏側で起動させたい。そのときに使われるのが `EXEC CICS START` コマンドです。

そして、裏で起動された子トランザクション(B)側が、「親から渡された荷物(データ)を受け取る」ために使うのが `EXEC CICS RETRIEVE` コマンドになります。

イメージとしては、「実家から仕送り(データ)を宅配便(START)で送り、一人暮らしのアパートに着いた息子が受け取りBOXから荷物を取り出す(RETRIEVE)」ような関係性です。

—

3. 実践!PL/Iで書く `START` と `RETRIEVE` の世界

言葉だけではイメージしにくいと思いますので、実際にPL/Iで書かれたCICSプログラムのサンプルを見てみましょう。大文字で書かれた重厚な佇まいですが、一つずつ紐解けば怖くありませんよ。

【親側プログラム】データを渡して、別タスクを起動する

親側では、「こういうデータを積んで、あのトランザクションを動かしてくれ!」とCICSにお願いします。

1
/ ———————————————— /
/ 親トランザクション:子タスクを非同期で起動する /
/ ———————————————— /
PARENT_PROG: PROC OPTIONS(MAIN);

/ 渡したいデータ(メッセージ領域)の定義 /
DCL 1 SEND_DATA,
3 EMP_ID CHAR(5) INIT(‘10234’), / 社員番号 /
3 REQ_MSG CHAR(50) INIT(‘月次データの集計をよろしく頼む!’);

/ 非同期で子トランザクション(SUB1)を起動し、データを渡す /
EXEC CICS START
TRANSID(‘SUB1’) / 起動する子トランザクションID /
FROM(SEND_DATA) / 渡すデータのエリア /
LENGTH(LENGTH(SEND_DATA))/ データの長さ /
TERMID(‘L001’) / 出力先端末(必要に応じて) /
NOCHECK; / 相手が起動していなくてもエラーにしない /

/ 起動命令を出したら、親は自分の処理をさっさと終わらせる(非同期) /
EXEC CICS RETURN;

END PARENT_PROG;

Javaのマルチスレッドや非同期キューイング、あるいはSpring Batchのジョブ起動などに似ていますよね。「私はここまで!あとはよろしく!」と裏側に仕事を投げ渡す爽快感が、CICSの `START` にはあります。

—

【子側プログラム】受け取りBOXからデータを取り出す

では次に、`START` によって裏で呼び出された子トランザクション側の動きを見てみましょう。ここでは `RETRIEVE` を使って、親が置いていったデータをごっそり回収します。

1
/ ———————————————— /
/ 子トランザクション:親から送られたデータを受け取る /
/ ———————————————— /
CHILD_PROG: PROC OPTIONS(MAIN);

/ 親から送られてくるデータを受け取るための入れ物(バッファ) /
DCL 1 RECV_DATA,
3 EMP_ID CHAR(5),
3 REQ_MSG CHAR(50);

Dcl EIBTRMID CHAR(4) EXTERNAL; / CICSのシステム領域:端末ID /

/ 親が `FROM` で置いていったデータを `RETRIEVE` で拾い上げる /
EXEC CICS RETRIEVE
INTO(RECV_DATA) / 受け取り先の変数 /
LENGTH(LENGTH(RECV_DATA)); / 受け取るデータの最大長 /

/ エラー(データがない場合など)のハンドリングは省略していますが、
通常はここに正常系の処理が続きます /

/ 受け取った社員番号を使って、何かしらの裏方処理を行う /
/ 例:データベースの更新やファイル出力など… /

/ 処理が終わったらタスクを終了する /
EXEC CICS RETURN;

END CHILD_PROG;

どうでしょう? `START` で投げたボールを、子側が `RETRIEVE` で綺麗にキャッチしているのがお分かりいただけたでしょうか。この仕組みのおかげで、メインフレームの重たい処理を分散させたり、画面の応答性能を落とさずに裏でバッチ処理を走らせたりすることが可能になっているのです。

—

4. アーキテクトからのワンポイントアドバイス(現場の知見)

ここで、長年レガシーシステムの現場を渡り歩いてきた私から、実務で絶対にハマる「落とし穴」をこっそり共有しておきますね。

1. データの長さ(LENGTH)には細心の注意を!
PL/Iの `LENGTH(変数名)` は非常に賢く、コンパイル時に構造体の正確なバイト数を計算してくれます。しかし、親側と子側で `SEND_DATA` と `RECV_DATA` のレイアウト(桁数やデータ型)が少しでもズレると、メモリ上で大惨事(ストレージ違反やデータ化け)を引き起こします。マイグレーションの際、ここを変更漏れするバグが本当によくあるので注意してください。
2. 非同期ゆえの「デバッグの難しさ」
非同期で動くため、「今、どの順番で動いているのか」がパッと見で追いづらくなります。CICSのトランザクションログやCEDF(CICSの対話型デバッガー)を駆使して、「あ、ちゃんと親からデータが渡っているな」と一歩ずつ確認するスキルが、この世界ではとても重宝されます。

—

おわりに

PL/Iの構文やCICSの非同期処理、最初は独特の単語に気圧されるかもしれませんが、本質を掴んでしまえば非常にシンプルで、理路整然とした美しい仕組みで作られていることに気づくはずです。

「予約語がない自由な世界」で、自分だけの綺麗なデータ構造を設計し、`START` と `RETRIEVE` でタスクを縦横無尽に連携させる――。そんなメインフレーム開発の面白さを、ぜひこれからのマイグレーションや保守の現場で味わってみてください。

あなたのレガシーライフが、実り多いものになりますように。それではまた次の技術でお会いしましょう!

タイトルとURLをコピーしました