◎本記事は、i Magazine 2014年2月号に掲載されたものです。RPG Ⅳの基本情報に大きな変更はないため掲載します。
IBM i でのアプリケーション開発に用いられるプログラミング言語の筆頭として挙げられるのは、やはりRPGである。その中でもRPG Ⅳ (ILE RPG)は、OS/400 V3R1での発表以来、OSリリースを経るに従って機能拡張が図られている。
図表1に主な機能の変遷をまとめたが、たとえば6桁を超えるパラメーター名称のサポートや日付処理の組み込み関数といった、ユーザーにとってより使いやすさを追求した機能拡張が加えられてきた(「ILE RPG解説書」に各OSリリースの機能拡張点がまとめられているので参考にされたい)。Part1で解説するフリーフォームも、その一環と捉えられるだろう。
このように進化を続けるRPG Ⅳであるが、ここであらためてその特徴と強みを考えてみたい。RPG Ⅲと比較したRPG Ⅳの特長は、大きく分けて2つある。1つはILEとしての特長であり、もう1つはプログラミング言語としての特長である。ここでは、それぞれについて解説してみよう。
進化するRPG Ⅳ ~ILEとしての特長
ILE(Integrated Language Environment:統合化言語環境)とは、IBM i におけるプログラミング・モデルの1つであり、ILEより前のプログラミング・モデル (OPM:Original Programming Model)では実現できなかったさまざまな機能を実装している。
RPG ⅢはOPMであり、RPG ⅣはILEであることから、RPG ⅣはILEの特色をそのままメリットとして享受できる。その中で最も大きいのが、モジュール化である。
OPMではプログラム・ソースをコンパイルすると、1つのプログラム・オブジェクト(*PGM) が生成されるが、ILEではプログラム・ソースからコンパイルして生成するのはモジュール・オブジェクト(*MODULE) となる。生成した1つ以上のモジュールを組み合わせる (この操作をバインドと呼ぶ) ことで、プログラム・オブジェクトを生成する。この仕組みの違いを示したのが図表2である。
なお、ジョブで実行するのはあくまでプログラム・オブジェクトであり、モジュール・オブジェクトを直接実行はできず、あくまでプログラム・オブジェクトを生成するための中間的なオブジェクトという位置づけである。プログラム・オブジェクトはモジュール・オブジェクトが組み込まれた形で生成される。
さらにILEでは、サービス・プログラム・オブジェクト (*SRVPGM) と呼ばれるオブジェクトがサポートされている。サービス・プログラム・オブジェクトは、1つ以上のモジュール・オブジェクトを組み合わせて作成するオブジェクトという点ではプログラム・オブジェクトと同じである。
両者の違いは、直接実行可能であるか否かにある。サービス・プログラムは、他のプログラムもしくはサービス・プログラムから呼び出す形式での実行のみサポートされている(図表3)。
モジュールとサービス・プログラムはいずれも、意味あるビジネス・ロジックや機能単位でソースを切り出して構成していくことで、ロジックの重複を防ぐことができ、重複の防止はそのままアプリケーション開発の品質向上に寄与する。
では単純にサービス・プログラムを作らずに、すべてモジュールを作成し、目的に応じて、その都度モジュールを組み合わせながらプログラムを作成すればよいのではないか、と思われる読者もいるだろう。
モジュールを直接組み込んでプログラムを生成する場合と、いったんサービス・プログラム化して、サービス・プログラムをプログラムから呼び出す形で構成する場合では、プログラム・コードの保守容易性の確保に大きな違いが出てくる。
前者の場合、モジュール内のロジックに変更を加える必要が生じた場合には、モジュールの再コンパイルと、当該モジュールを組み込んだすべてのプログラム・オブジェクトの再バインドが必要となる。これに対して後者では、モジュールの変更に伴ってサービス・プログラムの再バインドは必要だが、当該サービス・プログラムを呼び出しているプログラム自体の再バインドは不要である。
つまり頻繁に使われる機能単位、たとえば消費税計算ロジックや業務日付計算ロジックなどでサービス・プログラム化しておき、プログラムから呼び出す形で構成すれば、よりコードの高い保守容易性を実現できる。
消費税計算サービス・プログラムであれば、今後の税率変更や将来日本でも導入されるかもしれない品目別消費税率などに対しても、より効率的に対応できると期待される。もちろんコードの重複を防ぐために、OPM RPGでも/COPY句を使用して、同一ロジックを複数のプログラム・ソースに展開している読者も多いと思うが、その場合、当該ロジックの変更時に各プログラム・オブジェクトでの確認と再コンパイルが必要となる。
また、プログラムから別のプログラムの機能を呼び出せばよいのでは、と思われる読者も多いだろう。プログラム・オブジェクトの生成時に呼び出すサービス・プログラムを指定するのだが、そのサービス・プログラム・オブジェクトは呼び出し元のプログラム・オブジェクトが利用されるタイミングで同じくメモリ上にロードされてくる。
つまり、呼び出し元のプログラム・オブジェクトの中に組み込まれた機能であるかのようなイメージで、高速呼び出しがアーキテクチャとして実現している。一方、プログラムから他のプログラムを呼び出す (これを「動的プログラム呼び出し」と呼ぶ) 場合には、呼び出される時に初めて、そのプログラム・オブジェクトはメモリ上にロードされる(図表4)。
Javaなどのオブジェクト指向言語を利用する目的として、アプリケーション開発時の品質向上やコード保守容易性の確保が挙げられるが、RPGによるアプリケーション開発においても、RPG Ⅳでのモジュール化ならびにサービス・プログラム化によって、同様のメリットが得られる。今後のアプリケーション開発で、ぜひ採用を検討したい点である。
RPG Ⅳ プログラミング言語としての特長
■ 組み込み関数や命令セット
ここまでRPG Ⅳのメリットについて、ILEの側面から解説してきたが、次にプログラム言語としてRPGⅢと比較した場合のRPGⅣの特長や優位性について考えてみたい。
第1のポイントとして、数多くの組み込み関数 (BIF:Built-In Function)や命令コード、キーワードが用意・追加されるなど、継続的な機能強化がなされている点を挙げたい。IBM i 6.1ならびに7.1で新たにサポートが開始、もしくは機能強化されたBIFや命令セットについて少し紹介しよう。
%PARMNUM (新規/IBM i 7.1以降)
パラメーター・リストから指定した名称のパラメーターの序数 (順番) を取得できるBIFである。
これは、OPTIONS(*NOPASS) が指定されたオプション・パラメーターが存在するプロシージャーで、実際に値が渡されているかを検査するといった活用ができる。これまでは、%PARMSと比較するパラメーター数はハード・コードとなっていたが、組み込み関数に置き換えることで、よりハード・コード部分を減らせるため、プログラム・コードの柔軟性を向上できる。
たとえば、ある企業で社員一覧があると考えてみよう。以前は、事業所は1カ所のみであったため、一覧には氏名・社員番号だけが登録されていた。しかし業務拡大によって複数の事業所を擁するようになり、新たに入社する社員に関しては、一覧に所属事業所も登録されるようになった。
その際、一覧への登録プログラムをRPG Ⅳで実装すると、氏名・社員番号は必須パラメーターとして渡すことになるが、所属事業所はオプション・パラメーターとして渡す形になるはずだ。このような場合に、事業所名も渡されたかを判別するロジックとして、%PARMSと%PARMNUMを使用すれば便利である(図表5)。
1 2 3 4 5 6 7 8 |
D pi D name 20a D id 6a D office 10a options(*nopass) C /free //事業所パラメーターが渡されたかの検査 if %parms >= %parmnum(office); |
%SCANRPL (新規/IBM i 7.1以降)
指定した文字列を検索して、一度に置き換えるBIFである。
汎用のメッセージを用意しておいて実際に出力させる時、特定の値で置き換えて表示させたいといった場合に有効である。社員番号の既存有無を確認し、存在しない場合には利用可能である旨を通知するロジックを例にすると、図表6のようなコードになる。
汎用メッセージである “ ID is not used. You can use ID for a new employee.” のIDが実際の社員番号に置き換えられて 、“010101 is not used. You can use 01010 for a new employee” と出力される。
1 2 3 4 5 |
/free //汎用メッセージ genmsg = 'ID is not used. You can use ID for a new employee'; //IDを検査社員番号に置換 actmsg = %SCANRPL('ID':'010101':genmsg); |
%LEN (機能拡張/IBM i 7.1以降)
%LEN自体は以前より提供されているBIFであるが、IBM i 7.1ではパラメーター値*MAXを使用することで、可変長文字フィールドの最大長を取得できるように機能拡張されている(図表7)。
1 2 3 4 5 |
D parm1 s a varying(1000) C /free //最大長の取得 maxLength = %len(parm1:*max); |
%ADDR (機能拡張/IBM i 6.1以降)
IBM i 7.1の%LENと同様、%ADDRも以前から提供されているBIFであるが、IBM i 6.1で新規のパラメーター値*DATAがサポートされるようになった。
*DATAの指定によって、可変長フィールドのデータ部分のアドレスを取得できるように機能拡張されている。これまでは、フィールド長が含まれる最初の2バイトを明示的に除去する必要があった。
%LENと %ADDRはともに可変長フィールドに対するサポートの拡大であり、他のプログラムとのより柔軟な連携をRPG Ⅳで実現できるようになっている(図表8)。
1 2 3 4 5 |
D parm1 s a varying(1000) C /free //データ部のアドレス取得 dataAddr = %addr(parm1:*data); |
%LOOKUP (機能拡張,/IBM i 6.1以降)
SORTA (新規/ IBM i 6.1以降)
BIFの %LOOKUPおよび命令コードのSORTAを使用し、配列データ構造のサブフィールドをキーとして検索やソートを実行できる。売上情報の配列データ構造があり、品目・型番・売上明細というサブフィールドが存在し、売上明細には計上店舗コード・個数・金額という配列サブフィールドがあったとする。そこで品目が “TV” の配列要素を検索する時のコード例が、図表9である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Ddetail ds qualified D store 3 D quantity 10P 0 D amount 10P 0 D DsalesInfo ds qualified dim(100) D item 2 D pin 6 D salesDetail likeds(detail) dim(10) D DnumItem s 3P 0 Delem s 3P 0 C /free elem = %LOOKUP('TV':salesInfo(*).item:1:numItems); |
また各品目の売上詳細を店舗コード順に並べた上で、品目順に並べるといった処理のSORTAによる実装例は図表10のようになる。なおSORTAは(D)(A) 拡張子を付けることで降順・昇順の設定も可能である(図表10)。
1 2 3 4 5 6 7 8 9 |
/free for i = 1 to numItem; sorta %subarr(salesInfo(i).salesDetail(*).store :1 :numStore); endfor; sorta %subarr(salesInfo(*).item :1 :numItem); |
EXTDESCキーワード (新規/IBM i 6.1以降)
EXTFILE (*EXTDESC)(機能拡張/IBM i 6.1以降)
プログラムのコンパイル時に、コンパイラーが外部記述を取得するために参照するファイルと実行時に参照するファイルを明示的に指定できるようになっている。
図表11のコード例では、file01はコンパイル時にはファイルMYLIB/MYFILE01から外部記述が取得されるが、実行時にはライブラリー・リストを利用してMYFILE01が参照・利用される形となっている。
1 2 3 4 |
Ffile01 if e k disk extdesc('MYLIB/MYFILE01') F Ffile02 if e k disk extdesc('MYLIB/MYFILE02') F extfile(*extdesc) |
一方、EXTFILE(*DESC)を指定したfile02では、コンパイル時ならびに実行時ともにMYLIB/MYFILE02が参照・利用される。
このようにBIFはOSリリースを経るごとに追加されており、BIFに限らず命令コードやキーワードに関しても同様だ。RPG Ⅳはその時代の要件に応じた適切な関数群や命令コードを常に追加できる能力を備えている。一方でRPG Ⅲの開発は既に終了しており、今後の機能強化は特に予定されていない。今後のOSリリースで、どのようなBIF/命令セット、キーワードがRPG Ⅳに追加されていくのか、注目していきたい。
■ 各種制限の緩和
RPG Ⅳにおける第2のポイントとしては、各種制限の緩和がある。
代表的例としては、取り扱い可能な変数長の拡大がある。特にIBM i 6.1以降、RPG Ⅳでは最大約16MB (16,773,104バイト) のデータ構造や文字型変数がサポートされるようになっている (i5/OS V5R3までは約64KB、OS/400 V4R5までは約32KB)。
RPG Ⅲでは、一貫して約32KBまでの変数長である。5250画面やDB2 for i の物理ファイルへの入出力といった処理では、32KBでも特に問題ないかもしれない。しかし、Webアプリケーションや他システムとのデータ連携などを考えた場合、より大きなデータの受け渡しが必要になることは明らかである。
Webアプリケーション側で生成したXMLベースの情報をIBM i 側のRPGでさらに処理し、結果をDB2 for iに格納するといったケースでは、より大きなデータ・サイズが要求される場合もある。
■ ポインターのサポート
第3のポイントとしては、ポインターのサポートが挙げられる。
ポインターとはその名のとおり、メモリ上のデータ開始位置をポイントするものだ。RPG Ⅳでもポインターを利用できる。RPG Ⅲでは単純に値の受け渡ししかサポートされていなかったため、メモリ上では単にデータがコピーされていた。しかしRPG Ⅳポインターの受け渡しによって、メモリ上の同一データを参照・操作することができるようになった。
RPG Ⅳ内で大きなサイズの変数を受け渡す場合などは、ポインターを活用するのもよいだろう。また、一時的なストア領域としてワークファイルを作成し、単一フィールドに種々のデータを一時格納して他のプログラムに受け渡すといったアプリケーションを利用している場合でも、ポインターは効果的である(こうした例は他社ホストからコンバージョンしてIBM i 上で稼働させているケースなどでよく見かける)。
たとえば受注情報について、顧客名・受注品目番号・数量・金額といった情報を1つのデータとしてワークファイルにつなげて入れるケースでも、図表12のように、RPG Ⅳ内ではデータ構造にデータを格納し、当該データ構造のポインターを基底とする単一変数を定義することで、データ構造からデータ自体をコピーすることなく、各要素に分けて取り扱うことができる。
1 2 3 4 5 6 7 8 9 10 |
DorderInfoDs ds qualified D customerName 20 D orderItem 10 D quantity 10P 0 D amount 10P 0 DorderInfoPtr s * DorderInfoRaw s 42 based(orderInfoPtr) C /free orderInfoPtr = %addr(orderInfoDs); |
またポインターを使えば、より多くのシステムAPIを効果的に利用することも可能になる。IBM i で提供されているシステムAPIの多くは、パラメーターとしてポインターを利用しているため、RPG ⅣはシステムAPIとの親和性がより高いと言える。
特にユーザー・スペースに結果を出力した後に、そのデータを利用する形式のAPIでは効果を発揮する。ユーザー・スペースに格納されたデータのポインターを取得するAPIとして、QUSPTRUS APIがある。このAPIを利用してRPG Ⅳでポインターを取得し、データを取り扱う。APIには多くの種類があるので多彩な場面で活用できるが、身近な例としてはMSGWになったジョブがないかを定期的にチェックするようなプログラムの実装などがある。
WRKACTJOBコマンドのOUTPUTパラメーターは、*PRINTのみであるため、いったんスプール・ファイルに出力し、CPYSPLFコマンドでファイルにデータをコピーした後に、状況を示す部分を抜き出して判別するといった処理を実装しているケースもあるだろう。
その場合、OSリリースの変更時に仮にスプール・ファイルのレイアウトが変更になると、レイアウト変更に合わせて運用プログラムを書き換えねばならない。一方でシステム API QUSLJOB を使用してユーザー・スペースにジョブの情報を取得し、状況を判別するようにすれば、OSリリースが変更されても問題なく稼働させられる。
■ XMLファイル
ここに挙げた以外にも、RPG Ⅳでは多くの機能拡張がなされており、特に各種業界標準への対応という面では、RPG ⅣはRPG Ⅲと比較して、より高い親和性を保つように拡張され続けている。たとえば最近ではユーザーアプリケーションにおいても、XMLベースのデータハンドリングを求める場面が増えているが、RPG ⅣではXMLデータに対してより楽にアクセスできる仕組みを提供している。
たとえばIBM i 5.4から、XML関連の命令コードが新たにサポートされた。その1つであるXML-INTOは図表13のように、BIF の%XMLを組み合わせ、XMLファイル内の要素とデータ構造のサブフィールド名を同一にしておくことで、XMLファイルのデータをデータ構造の変数に直接取り込むことができる。
このように、RPG ⅣではこれまでのIBM i のファイル・データのハンドリングのみならず、種々のインターフェースを強化していることがおわかりだろう。
XML-INTOおよび%XMLによるXMLデータの取り込み
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
D DCustomerInfo ds qualified dim(100) D CoreFields likeds(CustomerDs) D DCustomerDs ds qualified D LastName 25a D FirstName 25a D Telephone 13a C /free xmlfile = '/tmp/xmlfiles/customer1.xml'; options ='doc=file + path=CustomersGroup/CustomerInfo + case=any + allowextra=yes + allowmissing=yes'; xml-into customerInfo %xml(xmlfile:options);<CustomersGroup> <CustomerInfo> <CoreFields> <LastName>山田</LastName> <FirstName>太郎</FirstName> <Gender>M</Gender> <Telephone>050-1234-5678</Telephone> <UIC>9999999999</UIC> </CoreFields> </CustomerInfo> </CustomersGroup> |
XMLファイル
1 2 3 4 5 6 7 8 9 10 11 |
<CustomersGroup> <CustomerInfo> <CoreFields> <LastName>山田</LastName> <FirstName>太郎</FirstName> <Gender>M</Gender> <Telephone>050-1234-5678</Telephone> <UIC>9999999999</UIC> </CoreFields> </CustomerInfo> </CustomersGroup> |
■ ファイル・データのハンドリング
ファイル・データのハンドリングに関しても、従来のレコード・レベル・アクセスだけでなくSQL、いわゆる組み込みSQLを利用したデータの入出力に関する使い勝手が向上している。
IBM i 5.4において、フリー・フォーマット (/free ~ /end-free) におけるフリー・フォームSQLステートメントがサポートされ、組み込みSQLを利用したプログラム・ソースの可読性も大幅に向上した。
i5/OS V5R3までは、/exec sql ~ /end execで囲まれた範囲での記述が必須であったが、/free ~ /end-freeの中でexec sqlを利用することにより、プログラム・ソースで直接的に組み込めるようになっている(図表14)。
i5/OS V5R3までの記述例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Hbnddir('ACCRCV') dftactgrp(*no) Fcustfile uf e disk Freport o e printer D custDs ds extname(custfile) D sendOverdueNotice... D pr /free read custfile custDs; dow not %eof; if dueDate > %date(); // overdue? sendOverdueNotice (); write reportFmt; /end-free C/exec sql insert :name, :duedate into C+ mylib/myfile C/end-exec /free endif; read custfile custDs; enddo; *inlr = '1'; /end-free |
IBM i 5.4からの記述例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Hbnddir('ACCRCV') dftactgrp(*no) Fcustfile uf e disk Freport o e printer D custDs ds extname(custfile) D sendOverdueNotice... D pr /free read custfile custDs; dow not %eof; if dueDate > %date(); // overdue? sendOverdueNotice (); write reportFmt; exec sql insert :name, :duedate into mylib/myfile; endif; read custfile custDs; enddo; *inlr = '1'; /end-free |
■ PCMLの自動生成
プログラム間連携の面では、RPG ⅣのCRTBNDRPGコマンドなどではプログラム・オブジェクト生成時に、PCML (Program Call Markup Language) を自動生成させる仕組みを提供している。
PCMLは、IBM i 上のプログラム・オブジェクトをホストサーバー経由で呼び出すToolbox for Javaで実装された仕組みの1つであり、PCMLで呼び出し可能なプログラム・オブジェクト自体はILEに限るものではない。
しかしPCMLを自動生成するなど、RPG Ⅳではより簡単に実装できるようになってきている。また、IBM i にはOS標準機能として組み込まれた軽量Webアプリケーション・サーバーのインスタンスとして、Webサービス・ゲートウェイと呼ばれる機能を提供している。RPGで作成したビジネス・ロジックの入出力定義をPCMLで記述しておけば、そのPCMLを当インスタンスに読み込み、登録することで、Webサービスとしてデプロイすることが可能になっている。その意味でも、PCMLの自動生成が可能なRPG ⅣはWebとの親和性がより高いと言えるだろう。
またWebに限らず、RPGの入出力を既存のファイルに限定しない新しい仕組みとして、IBM i 6.1以降では「Rational Open Access:RPG Edition」 (OAR) も用意されている。
これはファイルへの入出力の代わりに、ハンドラーと呼ばれるユーザー作成のILEモジュールにデータを受け渡すことで、ファイルに限定しないリソースへの入出力を可能にする仕組みであり、このOARを用いてユーザー・インターフェースのWeb化を実現するベンダー製品なども登場している。
RPGは利用用途として5250ベースのアプリケーションだけだと考えられているケースも多くあると思うが、このようにRPGは多様なインターフェースを持つ言語として進化している。これまで自社で培ったRPGベースのビジネス・ロジックを活かしつつ、ユーザー・インターフェースをWebサービスやOARといった新しい仕組みで刷新する機能も提供されている。
では、RPG Ⅲを現在使用しているユーザーがRPG Ⅳに移行するのは難しいのだろうか。
決してそうではない。RPG Ⅲのプログラム・ソースをRPG Ⅳのプログラム・ソースに変換するCVTRPGSRCコマンドが提供されている。まずはRPG Ⅳで動かしたいという場合には、単純にCVTRPGSRCコマンドによるソースの変換とCRTBNDRPGコマンドによるプログラム・オブジェクトの生成で対応できる。
それにより、既存のアプリケーションでもRPG Ⅳで機能拡張されているBIFや命令コードを利用可能である。もちろん、RPG Ⅳの優位点であるモジュール化の実現には、既存ソースからのロジック切り出しの検討が必要になるが、そこはIBM i の最大の特長である既存資産の継承性を活かして、漸次移行すればよいだろう。
RPG ⅣはRPG Ⅲとは異なり、今後も機能拡張が続く言語である。ぜひ読者の方々のシステム開発でも、RPG Ⅳを積極的に活用していただきたいと願っている。
著者|中村 陽一氏
日本アイ・ビー・エム システムズ・エンジニアリング株式会社
クラウド・イノベーション
[i Magazine 2014年2月号掲載]