投稿日:2021年4月8日
プログラムのILE化において、汎用性の高い複数のモジュールを集めて1つのサービスプログラムとして提供することは、ILE化の利点を享受する基本テクニックの1つと言えます。しかし、現実には1つのモジュールだけからなるサービスプログラムという奇妙な構造のものが多く見受けられます。これは、サービスプログラムにモジュール追加があった場合、このサービスプログラムを使用しているプログラムの再コンパイルが必須だという思い込みによるものと推測されます。本記事は、こうした誤解を払拭してサービスプログラムへの理解を深め、その活用に役立つものになっています。 (以上編集部注)
当記事ではシグネチャの説明およびシグネチャ・エラーを避けるためのオプションに関する概要説明をします。
ジョン・パリス
多くのRPGプログラマーが、オブジェクト管理を簡素化するために複数のモジュールを一緒にまとめるよう設計されたサービスプログラムの利点について学びました。しかし、多くの会社が、明らかにすべてのサービスプログラムをただ1つのモジュールで作成していることに私たちは気付きました。このアプローチに技術的な誤りは何も無いとは言え、コレクションの中にただ1つのモジュールしかないのは、「コレクション」オブジェクトの無駄遣いのように思えます。
加えて、これら単一モジュールから成るサービスプログラムを多く使用するプログラムがあると、パフォーマンスに影響を与える可能性があります。その理由は、プログラム開始時にプログラムとそれが使用する各サービスプログラムとの間で接続が確立されなければならないからです。プロシージャの数が同じであったとしても、10個のサービスプログラムを使用するプログラムは、2~3個のサービスプログラムを使用するプログラムより開始に時間が掛ります。
この単一モジュールから成るサービスプログラムというアプローチの背後にある論理的根拠が理解できませんが、これらの会社が過去に経験してきたサービスプログラムのシグネチャ・エラーという問題がその理由かもしれません。
Q: シグネチャって何?
A: シグネチャは、ファイルに対してレベルチェックが行うのと同様のチェックをサービスプログラムに対して提供する値です。シグネチャは、サービスプログラムの変更が、それを使っているプログラムが正しく機能し続けられる形でなされていることを保証できるよう支援します。シグネチャは、一般的にCRTSRVPGM(サービスプログラム作成)コマンドでExport(*ALL)を指定した場合に、システムによって生成されます。シグネチャの値はすべてのサービスプログラムの「エクスポート」の名前とその順序を入力とするアルゴリズムで生成されます。「エクスポート」は呼び出し可能なプロシージャ及びすべてのエクスポートされたデータ項目の名前です。サービスプログラムを参照するプログラムを作成する場合、サービスプログラムの現在のシグネチャ値がプログラム内にコピーされます。プログラムが検知することなくエクスポートリストが変更されると、プログラムは潜在的に間違ったプロシージャを呼び出す可能性があります。
Q: 何でシグネチャが変わるのか?
A: シグネチャはサービスプログラムに対するエクスポートリストが変わった場合に変わります。シグネチャ変更の最も一般的な原因は、サービスプログラムへの新たなプロシージャの追加です。通俗的に信じられている「知恵」とは違い、プロシージャ内のパラメータを変更してもサービスプログラムのシグネチャは変わりません。
Q: シグネチャが変わると何が起こる?
A: プログラムが呼び出されたとき、プログラムはそれが使用するすべてのサービスプログラムのシグネチャ値を直ちに検査し、シグネチャが一致しない場合にはシグネチャ違反メッセージを出します。これは、サービスプログラムを実際に呼び出した時点ではなく、プログラムの開始時に起こります。
Q: シグネチャ違反が起きました。さて、どうしましょう?
A: この状況を修正するには2つの選択肢があります。変更されたサービスプログラムを参照しているすべてのプログラムを再構築するか、サービスプログラムのシグネチャを管理するバインダー言語を作成することができます。
Q: サービスプログラムを参照するプログラムをどうやって再バインドするのか?
A: UPDPGM(プログラム更新)コマンドを使用します。どのモジュールも置き換えていないので、Module(*None)を指定します。UPDPGMコマンドは自動的にバインドされたサービスプログラムすべてのシグネチャを再検査するので、サービスプログラムを指定する必要はありません。シグネチャのどれかが変更されていた場合、コマンドはプログラムのシグネチャ値も更新します。
Q: CRTPGMコマンドを使ってプログラムを再コンパイルまたは再作成しない理由は?
A: 問題解決を行っている間、望まないあるいは不要な変更が紛れ込む可能性を排除するために、一般的に1時点では1つの変更を行うのがより良いことです。再コンパイルまたは再構築は潜在的に変更を紛れ込ませる可能性があります。例えば、ソースを変更すると、プログラムの再コンパイルは間違いなく変更を引き起こします。たとえ事前にコンパイルされた幾つかのプログラムを使って再構築したとしても、そのプログラムが現在のプログラム中にあるのと厳密に同一のコードになっている保証は必ずしもありません。加えて、プログラムを再作成したときに、他の(権限、活動化グループ等に関連した)プログラム属性が変わる可能性があります。しかし、おそらくUPDPGMを使う最大の理由は、他の選択肢のどれよりもずっと簡単で早いということです。
Q: どのプログラムを更新する必要があるかどうやって知るのか?
A: それは難しいかもしれません。サービスプログラムを理解する相互参照ツールをお持ちなら、必要な情報をそのツールが提供するはずです。ツールがない場合は、プログラム表示(DSPPGM)コマンドを使って各プログラムを表示し、(4番目の表示画面上で)プログラムが使用しているサービスプログラムの一覧と探しているシグネチャ値を見ることができます。もちろん、シグネチャが変わっていることを知っている場合は、プログラムはそのサービスプログラムを参照しているという事実から、プログラムを更新しなければならないと分かります。この方法は手動で行うのは面倒です。適当なAPIを使って自動的に更新を行うプログラムを作成することもできます(DSPPGMはファイル出力をサポートしていません)。これは当記事の範囲を超えますが、将来これを取り上げるかもしれません。
Q: バインダー言語はどうですか?
A: サービスプログラムに新しいプロシージャを追加するたびに幾つかのプログラムを更新したくないなら、シグネチャ値を管理するためにバインダー言語を作成できます。 例えば、DateStuffという名前のサービスプログラムがあり、元々はDayOfWeekとDayNameという名前の2つのプロシージャを含んでいるとします。バインダー言語を書くという目的からすると、両方のプロシージャが同じモジュール内にあるか、モジュールの組み合わせなのかは関係ありません。バインダー言語は単にプロシージャ名にしか関心がありません。元々DateStuffを作成したときにはバインダー言語を使用していなかったのでExport(*ALL)を指定しています。
最近、そのサービスプログラムに、再度Export(*ALL)を指定してGetDurationとLastDayOfMonthという名前のプロシージャを含む新しいモジュールを追加しました。シグネチャ値を変更したプロシージャ(エクスポート)を追加したので、シグネチャはこの時点で異なったものになっています。昔のバージョンのサービスプログラムで正常に動作していたプログラムは、シグネチャ違反でもう動作しなくなります。
これを修正するために幾つかのバインダー言語を作成しましょう。バインダー言語をQSRVSRCと言う名前のファイルのDateStuffというサービスプログラムと同じ名前のメンバーに入力します。メンバータイプはBNDです。バインダー言語は以下のようになります。
STRPGMEXP PGMLVL(*CURRENT) EXPORT SYMBOL(DayName) EXPORT SYMBOL(DayOfWeek) EXPORT SYMBOL(GetDuration) EXPORT SYMBOL(LastDayOfMonth) ENDPGMEXP STRPGMEXP PGMLVL(*PRV) EXPORT SYMBOL(DayName) EXPORT SYMBOL(DayOfWeek) ENDPGMEXP
(バインダー言語中のエクスポートシンボルと呼ばれる)プロシージャ名の順序が重要であることに注意してください。以前のエキスポートリスト(*PRV)は、最初にサービスプログラムがExport(*ALL)で作成されたときシステムが入力した順序と同じでなければなりません。システムはプロシージャ名のエクスポートをアルファベット順に置くので、ここでそれと同じことをしました。同様に、*CURRENTエクスポートリストがすべての*PRVリスト内の全エクスポートの順序を維持することも重要です。すなわち、DayNameは1番目に、DayOfWeekは2番目にといった具合に順序を変えずにいなければなりません。
バインダー言語が作成されたので、サービスプログラムDataStuffを更新するためにサービスプログラム更新(UPDSRVPGM)コマンドを実行できます。今回はExport(*SRCFILE)を指定します。ソースメンバー名とファイル名が正しく指定されていることを確認してください。
これでサービスプログラムは、バインダー言語内の各エクスポートリストに1つ、計2つの異なるシグネチャを持つことになります。*PRVリストによって生成されたシグネチャは、サービスプログラムの元々のシグネチャと一致していなければなりませんので、古いプログラムは動作し続けます。新しいまたは更新されたプログラムは自動的に*CURRENTリストによって生成されたシグネチャを取り出します。
将来別のモジュールを追加する必要がある場合、2つ目の*PRVリストが作成できます。とにかく、プロシージャ名の順序が重要であることを覚えておいてください。*CURRENTリストのブロックコピーを作成し、その中の1つのPGMLVLを*PRVに変更します。新しい*CURRENTリストの後部に新しいすべての新しいプロシージャ名を追加します。エクスポートリスト内のどのプロシージャ名も削除したり、順序を変えたりしないでください。作成し終わったら、バインダー言語をCRTSRVPGMまたはUPDSRVPGMに適用できます。
このテクニックは、システムが生成したシグネチャ値を使用します。自分自身のシグネチャ値をハードコーディングすることも可能です。将来の記事でサービスプログラムのシグネチャの管理について別な観点から取り上げる予定です。