メニューボタン
IBMi海外記事2013.07.22

Rational Open Access:RPG Editionのワークショップ パート2

Craig Pelkie 著

状況条件およびハンドラーからクライアントへのデータ返却

Rational Open Access: RPG Editionのワークショップ パート1」で、クライアント・プログラムの HANDLER F スペック・キーワードを使用して Open Access (OA) ハンドラー・プログラムを指定する方法を確認したことを思い出すかもしれません。また、私たちは、簡単なハンドラー・プログラムを開発して、クライアント・プログラムとハンドラーの連携動作、およびクライアントでの命令コードへの応答の様子を確認しました。
本号では、状況条件 (end-of-file およびエラー) との連携方法、およびハンドラーからクライアントへのデータ返却方法について深く掘り下げてゆきます。さらにハンドラーを継続的に開発するため、パート1で使用した例を構築していきます。

新しいクライアント・プログラム

まず初めに、IFS02 クライアント・プログラムを修正しました。図1 は新しいクライアント・プログラム、IFS03 を示しています。これには次のような変更が行われています。

  • ハンドラー・プログラムをOARPG/IFSHDLR2に変更 (コールアウトA)
  • OPEN命令のエラー処理を追加 (コールアウトB)
  • READ命令にend-of-fileおよびエラー処理を追加 (コールアウトC)

また、プログラムのセクションを明確に区切るために、コメント「フラワー・ボックス」を追加しました。
本号を理解するには、IFS03 をダウンロードおよびコンパイルしてください。ただし、まだ実行はしないでください。最初に IFSHDLR2 プログラムをダウンロードしてコンパイルする必要があるためです。

IFSHDLR2 プログラム

図2 は、新しいハンドラー・プログラム、IFSHDLR2 を示しています。このハンドラーは、パート1で開発したハンドラーとほとんど同じですが、次の点が変更されています。

  • QOA.rpgStatus サブフィールドを OPEN ハンドラーに設定するコードを追加 (コールアウトA)
  • QOA.eof サブフィールドを READ ハンドラーに設定するコードを追加 (コールアウトB)

このプログラムをダウンロードしてコンパイルしたら、新しいクライアント・プログラムとそのハンドラーをテストできます。

最初のテスト: OPEN でのエラー

IFS03 クライアント・プログラムを呼び出すと、最初に OPEN 命令が実行されます。その命令はハンドラーで制御されます。ハンドラー・プログラムで、QOA.rpgStatus データ構造サブフィールドを値 01217 に設定します。この値は、明示的 OPEN または CLOSE 命令のエラーの RPG 状況コードになります。ハンドラーが指定された状況を設定すると、*INLR を設定し、戻ります。
次に、クライアント・プログラムのコード (図1 のコールアウトB) は、ハンドラーに状況値を設定した結果、%error 組み込み関数が設定されたことを検出します。クライアント・プログラムは、状況コードを表示して、終了します。図3 は、このテストの出力を示しています。
私は、任意に 01217 状況コードを指定しませんでした (つまり、値を構成しませんでした)。RPG OA ハンドラー・プログラムをコーディングする場合、指定する値を意識している必要があります。ある意味では、ハンドラーを開発する作業は、(この例では) 通常の RPG データベース管理処理をエミュレートするようなものです。
クライアントで明示的 OPEN 操作のエラーをチェックし、ハンドラーで OPEN を処理するときにエラー状況を設定するのは、完全に妥当性があります。この例では、ハンドラーは最終的に IFS のテキスト・ファイルと連携することになるため、 OPEN のエラーをチェックするのは特に関連性があります。ハンドラー・プログラムを完全に開発したら、ハンドラーの OPEN 操作処理により、テキスト・ファイルが存在し、それにアクセスする権限があることが確認されます。ファイルが存在しない、またはアクセス権限がない場合は、ハンドラーはクライアントに正しいエラー状況を返す必要があります。
ILE RPG Language Reference 7.1 マニュアルの第 5 章に、状況コードの定義が記載されています (http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/topic/rzasd/sc092508.pdf)。この章では、約 45 個の状況コードを記載しています。ハンドラーを開発するときにしなければならないことは、リストをレビューし、処理している状況について最も適切なコードを選択することです。RPG で定義されているように標準状況コードを使用することで、クライアント・プログラムで「通常の」RPG コードを作成し、クライアントで従来のエラー処理技法を使用できます。当然、クライアントのエラーも無視できます。その場合、RPG ランタイム エラー処理は、ハンドラーから返された状況を代行受信します。

rpgStatus サブフィールドはどこで定義するのか?

この時点で、ハンドラーに対する *ENTRY パラメーターであるデータ構造 QOA の検証を、これ以上引き延ばすことはできません。このパラメーター・データ構造は、クライアントとそのハンドラーがどのように通信するか、その意味を理解するためのカギです。パラメーター・データ構造を理解しさえすれば、RPG OA 機能に完全にアクセスできるようになるでしょう。
パラメーター・データ構造は、 QOAR/QRPGLESRC (QRPNOPENACC) インクルード・ファイルに定義されています。このファイルは、ハンドラー・プログラムの /copy ステートメントにあります。IBM はインクルード・ファイルを定義し、ライブラリー QOAR にある他の RPG OA オブジェクトとともに、 IBM i システムにインストールしています。
パラメーター・データ構造に加えて、インクルード・ファイルには、図4 に記載の主なセクションが含まれています。私が、IFS ハンドラーの例を開発したときに、同時に Operations セクションおよび Template-Open Access セクションに取り組みました。
最初に OA を開発し始めたときに、このインクルード・ファイルの各部が、ハンドラー・プログラムとどのように連携するのかすぐには理解できませんでした。ファイル中では意味のあるコメントが定義と混ざっていたため、ファイルの検証は困難と思いました。RPG バージョンのインクルード・ファイルは、ソース・ファイル QOAR/H(QRNOPENACC に含まれている C 言語バージョンを、直接入れ替えたようなものです。C 言語バージョンのインクルード・ファイルは、きちんとフォーマットされており、定義とコメントがわかりやすくなっています。
RPG インクルード・ファイルの理解を深めるために、「フラワー・ボックス」コメント・ハンドラーを追加し、フィールド・レベルのコメントをすべて別のセクションに入れることで、それをフォーマットし直しました。フィールド名および値など、実際の定義すべてを正確に元のまま残しました。この記事のオンライン版で、私の作成したインクルード・ファイル (QRNOPENACZ) をご覧いただくことが出来ます。今後 IBM がファイルに変更を行うことを加味して、プログラムには、 IBM の作成したインクルード・ファイル (QRNOPENACC) を使用し続けることを提案します。
インクルード・ファイルの QrnOpenAccess_T データ構造定義を見てみましょう。ファイル中の最後のデータ定義です。QrnOpenAccess_T 内で、データ構造のほぼ中ほどに、 rpgOperation (符号なし整数 10.0) サブフィールドと rpgStatus (整数 10.0) サブフィールドが定義されているのがわかるでしょう。このシリーズのパート 1 では、rpgOperation サブフィールドが最初のハンドラー・プログラム IFSHDLR1 で使用されていました。ハンドラーを起動すると、RPG OA ランタイムは、サブフィールドの値をインクルード・ファイルのQrnOperation_section で定義された定数の 1 つに設定しています。
現在のハンドラー (IFSHDLR2) で使用されている別のサブフィールドは、end-of-file (eof) です。QrnOpenAccess_T データ構造の最後のサブフィールドから数えて 10 番目のサブフィールドで、indicator-type フィールドとして定義されています。
QrnOpenAccess_T データ構造を確認すると、RPG ファイルの入出力で使用されるすべてのタイプの条件および値を処理するサブフィールドを確認できます。例えば、openFeedback、ioFeedback、deviceFeedback のサブフィールドは、INFDS データ構造の同じような名前の RPG セクションと等しくなります。RPG ファイル入出力を正確に模倣するハンドラーを作成する場合、ハンドラーでの命令の処理の結果を正確にクライアント・プログラムに返すよう、それらのサブフィールドの値を設定する必要があります。IFS ファイル・ハンドラーでは、INFDS で提供される詳細レベルは必要ありません。rpgStatus サブフィールドおよび eof サブフィールドのみ必要です。
ハンドラーを開発する場合、ファイル入出力機能をどの程度提供するかを文書化する必要があります。例えば、アプリケーションで INFDS の詳細が必要ではない場合、ファイルの INFDS キーワードをクライアント・プログラムでコーディングしないよう、INFDS はハンドラーで未サポートであると文書化することができます。同様に、すべての命令コードをサポートする必要はありません。IFS ファイル・ハンドラーでは、OPEN、CLOSE、および READ の各命令、およびおそらく WRITE 命令もサポートするのはわかります。より高度なハンドラーでは、CHAIN や DELETE などの命令のハンドラー・コードも実装すると思いますが、IFS ファイル・ハンドラーでは、おそらく EXFMT 命令はサポートしないでしょう。

End-of-File 条件をテストする

IFS03 プログラムを呼び出す場合、IFSHDLR2 ハンドラーを呼び出します。この時点で、ハンドラーは常に OPEN 命令で状況値 01217 を返します。
end-of-file 条件をテストするには、IFSHDLR2 プログラムで、QOA.rpgStatus 値を設定するステートメントをコメントアウトします (図2 のコールアウト A)。変更を行い、ハンドラーを再コンパイルしたら、プログラム IFS93 を呼び出します。図 5 に示す出力をご覧ください。
このハンドラーで起きていることを、パート1のハンドラーで発生したことと比較できます。そのハンドラー (IFSHDLR1) では、READ 命令が単純にクライアントに戻りました。私たちは、ハンドラーにファイル・フィールドを設定しなかったため、クライアント・プログラムは 10進データ・エラーで終了しました。今回は、 end-of-file フィールドを設定したため、READ がハンドラーからクライアントに戻ると、クライアントはファイル入力バッファーのデータを検出しようとしません。READ 命令はすぐに end-of-file (ファイルの終わり) になるので、クライアント・プログラムは正常に終了します。

ハンドラーからクライアントへデータを返す

データをクライアントへ返すハンドラーに取り組む準備ができました。図6 は、IFSHDLR3 という新しいハンドラー・プログラムを示しています。コード例を実行してこの記事をたどっている場合、IFS03 プログラムに戻り、HANDLER キーワード値を OARPG/IFSHDLR3 に変更し (図1のコールアウトA)、そのプログラムを再コンパイルします。
IFSHDLR3 プログラムは、ファイルからの読み取りをシミュレートします。返された「データ」は、プログラムでハードコーディングされています。この時点で、ハンドラーからクライアントへ単純にデータ値を返しますが、まだ IFS でテキスト・ファイルに取り組む必要はありません。
ハンドラー・プログラムは2種類作成できます。この例が示す最初のタイプは、クライアント・プログラムで定義されたレコード・フォーマット (複数の場合もあり) を使用します。2番目のタイプはより高度で、RPG OAの追加機能を使用して、クライアントで使用中のレコード・フォーマット、およびフィールドを動的に決定します。この例の技法は、プログラムを作成するときに既知のファイルのデータを提供するなど、特定の目的でハンドラーを作成している場合にうまく機能します。しかし、実行時までわからない、さまざまなレコード・フォーマットに対応できる「汎用」ハンドラーを開発する必要がある場合は、動的機能の使用を検討します。
クライアント・プログラムはデータベース・ファイル QIWS/QCUSTCDTと連携しているため、そのファイルの定義をハンドラーに組み込みます (図6 のコールアウト A)。この F スペック定義はキーワード template を使用して、コンパイラーがレコード・フォーマットおよびファイルに関連付けられたフィールド定義のみ取り込むよう指示します。ハンドラー・プログラムでは、ファイル上で入出力操作を実行するつもりはありません。
図6のコールアウトBでは、データ構造 qcustcdt を QIWS/QCUSTCDT ファイルの cusrec レコード・フォーマットのように定義しています。この定義で使用されている based キーワードは、基点ポインターをデータ構造に割り当てています。値は QOA.inputBuffer ポインター値に設定されています (inputBuffer は QrnOpenAccess_T データ構造の7番目のサブフィールドです。このデータ構造は、この記事のオンライン版の QRNOPENACZ インクルード・ファイルでご覧いただくことができます。
基点ポインターは、クライアントのファイル入出力バッファーとハンドラーを接続します。言い換えれば、ハンドラーは、ファイルのフィールドについて、クライアント・プログラムで使用されているメモリーにアクセスできるようになりました。ハンドラーが qcustcdt データ構造サブフィールドを変更すると、変更はクライアント・プログラムで「見えます」。値をサブフィールドにつなぐことで、ハンドラーは READ 操作について従来の RPG データベース管理ルーチンが行う内容をエミュレートします。
IFS03 クライアント・プログラムでは、基点ポインターの値は READ 操作を実行すると設定されます。ハンドラー・プログラムを呼び出して READ 操作を管理する前に、RPG OA ランタイム・サポートは、 inputBuffer ポインターをクライアント・プログラムの cusrec バッファーの開始をポイントするよう設定します。
ハンドラーが正しく動作するようにするには、ハンドラーのデータ構造を、クライアントが使用する内容と一致するレコード・フォーマットと関連付けます。inputBuffer ポインターに基づく長い文字フィールドを単に使用できますが、クライアントで使用されている同じレコード・フォーマットを使用して、 LIKEREC キーワードを使用する (図6 のコールアウトB) 方が、ずっと簡単です。
しかし、例えば、マルチフォーマット論理ファイル、またはプリンター・ファイル、あるいはディスプレイ・ファイルのハンドラーを作成したい場合など、複数フォーマット・ファイルを扱っている場合はどうでしょうか。処理しているファイルにどれだけ多くのレコード・フォーマットがあっても、inputBuffer ポインターは1つしか存在しません。この場合、LIKEREC キーワードを使用して、その他のレコード・フォーマット (複数の場合もあり) を定義することで、ベース・データ構造を追加で定義するだけです。追加のデータ構造はすべて、同じ inputBuffer 基点ポインターに基づいています。ハンドラー内で、QrnOpenAccess_T データ構造の recordFormat サブフィールドを確認することで、どのレコード・フォーマットがクライアント・プログラム内で使用されているか判断できます。ハンドラーの例で、cusrec レコード・フォーマットのみ扱っていることはわかっていますので、複数のレコード・フォーマットを処理する特殊なコーディングは必要ありません。
私自信のテストのために、ハンドラーに多少データを返すようにしたいと思います。クライアント・プログラムは読み取りループにあるため、READ 操作は最初の読み取り後に再度呼び出されます。READ 操作のため次にハンドラーを呼び出したとき、end-of-file 条件を設定したいと考えています。このテストでは、ハンドラーからクライアントへ「レコード」を 1 つだけ返せば十分です。READ 処理の 2 番目の呼び出しを制御するため、標識変数 readCalled (図6 のコールアウトC) を定義します。コールアウトDで、その変数をテストします。
データを qcustcdt データ構造サブフィールドに移動する様子は、コールアウト E で示しています。この時点で、データ値の移動は期待外れです。データ構造サブフィールドそれぞれに有効な値を設定する簡単な練習です。値を設定したら、次の呼び出しでコールアウト D のコードを使用して、READ 操作を処理するよう readCalled 標識の値を設定します。
IFS03 プログラムを実行すると、図7 のようなメッセージが表示されます。ここで、OPEN 操作、次に2つの READ 操作、最後に CLOSE 操作があるのがわかると思います。クライアント・プログラムのスプール・ファイル出力である図8 は、データがハンドラーから呼び出し元に返された証拠を示しています。ハンドラーは現在有効で、意図した動作をしています。

次のステップ

これで、作業用クライアントとハンドラー・プログラムができました。データ取得作業は、無事クライアントに代行してもらうことができました。このシリーズの次号では、IFS のテキスト・ファイルから読み取るコードを追加して、ハンドラーを完成させましょう。

あわせて読みたい記事

PAGE TOP