OpenRPGUIの紹介
OpenRPGUIとExt JSを使用してアプリケーションのモダン化を容易に
IBM iとRPGが、モダンなインタフェースを効率的に提供することができないことで長年悩まされているということは言うまでもありません。RPGの開発チームは、IBMのモダン化への方向性がRPG Open Accessを除けば常にJava、PHP、EGLに向けられているので、RPGのモダン化についてIBMからの支援を一切受けられていません。Java、PHP、EGLではモダン化というタスクを達成できないと言っているのではなく、モダン化のプロセスに複数のコア・サーバー・サイド言語が深く関係する場合は常に、複数のアプリケーション・サーバー、さまざまなコンパイラやランタイムに対する複数のアップデート、各言語に対する情報の半減期に遅れをとらないようにしていく、など、複雑さに起因する問題に遭遇することになります。Microsoft .NETを全面的に使用しているチームがバックエンドをSolaris OS上で稼動するOracleデータベースに変更するように指示されている場合にも同じことが言えるでしょう。そのような提案が来ても困ってしまいますよね。私の考えでは、RPGでできているフロント・エンドをJava、PHP、EGL、.NETに変え、RPGはビジネス・ロジックのレイヤーだけで使用することについても同じことがあてはまります。サーバー側でできていることをこのように変更すると効率的で統合されたインフラストラクチャではなくなりますので、変更するとしても最後の手段として選択すべきです。
障害発生の潜在箇所を増やさない
私の経験では、多くのIT部門でUIのモダン化をRPGがブラウザと「話す」方法を探るのではなく、ASP.NET、JavaServer Faces、PHP等といったテクノロジに委ねています。たいていの場合、ASP.NETはフロント・エンドのグラフィック・インタフェースに使用されており、SQL、XML、ウェブ・サービス、その他のプレーン・テキスト・ソケット等のテクノロジを組み合わせて使用して、RPGやDB2をバックエンド側で呼び出しています。このようなアプローチを取ることは障害発生の潜在箇所を増大させることになり、ビジネスを効率的にしかも効果的に維持し続けるという観点で、私としては受け入れることはできません。
サーバー側ではRPGだけを使用してUIをモダン化できるとしたらどうでしょうか。数十年に渡って蓄積してきた大変効率的でビジネス向けの文法を持つモジュール言語であるRPGプログラミングの知識を利用できるとしたらどうでしょうか。表示ファイルを記述して更新することで画面を遷移するなどといった、モダンなインタフェースと「話す」と似たような概念を利用できるとしたらどうでしょうか。
我々に欠けているもの
ここ数年の間、私はRPGのプログラマに欠けていることは何かを考えていました。我々に本当に欠けているものの中で唯一重要なものは、モダン・ユーザー・インタフェースと通信をする簡単な方法です。モダン・インタフェースにはブラウザだけでなくAndroid、iPhone、iPad等といった携帯端末も含まれます。この結論に達するのは難しいことではありませんが、RPGにあって私たちが当たり前と思っているのに他のプラットフォームにないことをも評価しているときにこの結論にたどりついたのです。データベース、OS、ジョブ・ログ、コール・スタックの統合においてRPGの右に出るものはありません。一から構築したとても安定したOSで大量のトランザクションとユーザーを一度に処理し、ウィルスの進入も容易ではありません。ジョブ管理機能も優れており、コマンド・ラインもメニュー・システムも最高です。LinuxとMacをここ3年使用してみて学んだことは、システム管理用には大好きなコマンド・ラインやメニュー・システムがいかにありがたいものであるかということでした。実運用環境にあるサーバー側ジョブをデバッグすることもできますし、ライブラリ・リストやジョブ記述を備えた強力でしかもシンプルな環境制御機能を有しています。いいことをあげればきりがないのですが、ここではおいておきましょう。
プレゼンテーション層だけを扱うテクノロジをずっと探し続けていたのはこういう理由だったのです。ここ数年でSenchaのExt JS (www.sencha.com) などといったJavaScriptベースのフレームワークが出てきて、これが目に見えて現実のものとなってきたのは嬉しいことです。私がExt JSを好きなのは、毎回たくさんのHTMLをブラウザ側に押し返すのではなく、UIパネルの構成やコンポーネントに基づいて画面設計をするというアプローチだからです。たとえば、Ext JSを使用すると図―1に示すような構文でパネルを定義することができます。このコードは、ユーザーがこのページを訪れたりこのページから移動したりするたびにダウンロードされるのではなく、ウェブ・サイトを訪れるたびにブラウザに一度ダウンロードされます。(このコード・ファイル全体をSystem iNetworkのコードサイトsysteminetwork.com/codeからダウンロードすることができます。)
図―1に示すJavaScriptの構文は図―2に示すパネルを定義しています。このような構文を見たことがなくても心配しないでください。おそらく見たことがないはずなのです。これをしばらく見ていると、Ext JSのオブジェクトを使用してJavaScriptで構成するのに覚えなければならない構文が、それほどないことにお気づきになるでしょう。JavaScriptの構文全体のチュートリアルは本稿のスコープを超えていますので、詳細はen.wikipedia.org/wiki/JavaScript_syntaxを参照してください。
私はパネルの定義をシンプルにすることで、構文にとらわれすぎずに概念に集中できるようにしました。図―1では実際にはJavaScript自体とExt JS APIの2つの構文が使われていて、Ext JS APIはJavaScriptを使用していますが、特定の構成に対してどのようなタイプのものが指定できるのかについて制約があります。Ext JS APIのドキュメントについてはhttp://docs.sencha.com/ext-js/4-0/を参照してください。Ext Panelのドキュメントについてはhttp://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Panelを参照してください。
見覚えのある設計パターン
Ext JSでいろいろな構成をすることの興味深い点は、*DSPFオブジェクトに対するDDSと同じパターンや概念の一部に従っているという点です。JavaScriptのconfigオプションの構文では、pnl1のid、最初は非表示であること、タイトルや幅が400などといったことが宣言されます。これらはヘッダー・レベルの構成なのです。
Ext Panelのドキュメントをご覧になるとわかると思いますが、指定できるconfigオプションは多数あります。設定されていないconfigオプションについてはすべてデフォルト値が使用され、実行時に任意のconfigオプションを変更することができます。その変更はパネルを表示したり非表示にしたりするときに反映されます。
次にitems構成ですが、ここには詳細なエリアが表示されます。この場合、fieldsetコンポーネント・タイプとそれをサポートするconfigオプションとしてhtmlを指定し、そこにプレーン・テキストを入れてユーザーに対して表示することができます。itemsの構文はJavaScriptの配列を使用しており、1つまたは複数のコンポーネントを指定することができます。角括弧は配列の始まりと終わりを示し、波括弧は配列中の要素を宣言します。
最後のオプションはパネル用のボタンを定義するところで、ボタンはitemsエリアで特に構成しない限りパネルの一番下に現れます。btnNxtPnl1ボタンにはハンドラと呼ばれるものがあり、このハンドラはhtmlファイル中のbtnHandlerという名前のJavaScript関数を指しています。(btnHandlerについては後で少し触れます。) configオプションsubmitFormsは実際には私が作ったオプションで、Ext JSのドキュメントには存在していません。Ext JSのコンポーネントをこのように容易に拡張できることは、後で使用するコンポーネントと追加のメタデータを使用できるのでとても有用です。configオプションsubmitFormsを使用すると、このボタンがクリックされた時にどのフォームをサブミットするのかを宣言することができます。サブミットする必要のあるフォームがページに複数ある場合があるからです。しかしsubmitFormsという名前のカスタムconfigオプションを追加しただけでは、指定したフォームのデータがサーバー側に自動的に送られるわけではありません。この送信タスクを完了させるには汎用的なJavaScriptコードを書く必要があり、JavaScriptのbtnHandlerメソッドの話に戻って来るのです。
JavaScriptのbtnHandlerメソッドの定義は図―3に示す通りです。このメソッドはRPGプログラムでいうサブ・プロシージャに相当するもので、多くの異なるパネルのボタンから汎用的に再利用するコードの塊です。btnHandlerメソッドはbuttonとeventという2つのパラメータを入力として受け取っていることがお分かりいただけると思います。私が書いたbtnHandlerメソッドも、図―4に示す通り、Ext JSのButtonオブジェクトのハンドラのconfigオプションのドキュメントによれば、2つのパラメータを受け取ることになります。受け取るbuttonパラメータはステロイド上のRPGデータ構造に少し似ていて、ボタンに関するデータを保持しているだけでなくenable()やdisable()のようなメソッドも起動します。
btnHandlerメソッドの1行目はExt.Ajax.requestの呼び出しです。この特別なExt JSオブジェクトは、IBM iマシン上のRPGプログラムと通信するのに使用するオブジェクトです。サーバー上のRPGと送受信するために、私は他の数人のプログラマと一緒にOpenRPGUI (www.OpenRPGUI.com)という名前の、フリーのオープン・ソースのプロジェクトを起こしました。この方が関連するさまざまなテクノロジを使用しながらも軽量に保つことが簡単にできるからです。OpenRPGUIのコードについては本稿でもう少し詳しく述べます。
Ext.Ajax.requestの最初のconfigオプションはurlという名前で、OpenRPGUIのRPGプログラムの場所を指定します。OpenRPGUIをインストールしたときは、Apacheのインスタンスが設定され、URLリクエストをマシン上のインストール・ライブラリに適切にマップしてくれる点に注目してください。
次はparamsというconfigオプションです。このオプションには、IBM iマシンに対して送信する名前-値のペアを指定します。buttonパラメータがsubmitFormsのconfigオプション用にどのように呼び出され、getFormVarsに渡されているのかに注目してください。この部分が、Ext.Panel上で私が前述した通りに指定したカスタマイズした部分で、この特定のサーバー通信でどのフォームを送信するのかを決定するのに使用しています。また、ボタンの識別子(idともいいます)を取得してアクション値としても使用しています。アクション値は、ユーザーがたった今取ったアクションが何であるかをRPGプログラムに知らせるものです。back2meメソッドは、従来のウェブ・プログラミングにおけるクッキーの仕組みと似たような動きをします。基本的にはback2meは私が記述したJavaScriptのメソッドで、IBM iマシンのRPGプログラムから送信されてきた値を保存し、それ以後のサーバーとの通信の度に同じ値を送り返します。これは、Apacheがサーバー側でジョブを再利用するので、ウェブ・アプリケーションの状態をトラッキングする1つの方法です。
最後の2つのconfigオプションはsuccessエリアとfailureエリアで、サーバーとの通信中にどちらか(つまり成功または失敗のどちらか)の条件が成立したときにすべきことを記述したJavaScriptのコードです。通信が成功した場合は2つのことを実行します。1つ目はJavaScript Object Notation (JSON)応答を取得してデコードします。次に、デコードした応答を汎用的なuiactnメソッドに渡して処理します。uiactnがどのように動作するのか詳しく見る前に、JSONとはどういうものでどのように動作するのかについて述べます。
JSONはタグ付けされたデータをテキスト表現したものに過ぎず、XMLの代わりと考えてください。JSONがどのようなものであるかの例を図―5に示します。JSONの詳細についてはjson.orgをご覧ください。図―5に示したJSONの例は、btnNxtPnl1のアクションがIBM iマシンに送信された時に、RPGプログラムがクライアント側に送り返してくるものを示しています。JSONはクライアントとサーバー間の通信で頻繁に使用されます。ここでは「クライアント」という汎用的な用語を使用しました。それはJSONがJavaScriptから派生したものであるにもかかわらず、Android携帯端末との通信などJavaScriptがまったく関係しない場合を含む他のさまざまなシナリオで、データを送信するのに使用できる(そして使用されている)からです。
JSONの良いところはかなり軽量で(つまりXMLほど多弁ではない)、RPGとクライアントのプレゼンテーション層との間のバッファの役割を果たす点です。これがなぜ重要なのでしょうか。それは、5年後10年後にはプレゼンテーション層としてのExt JSとは通信していないかもしれないからです。プレゼンテーション層は、現在さまざまなベンダーが固有のテクノロジでトップの座を目指して競争していて大変不安定な状態にあります。その例がAdobe社のFlexやFlash、Microsoft社のSilverlight、Oracle社のJavaFXなどです。これらのテクノロジはいずれもプレゼンテーション層としてはさまざまな成功を収めていますが、非固有でオープンなHTML5の仕様が徐々にリリースされブラウザに採用され、最終的にはそれにとって代わられざるを得ないという点で部分的な成功に終わるのです。Google社やApple社もHTMLを頼りにしていると表明しています。HTML5の詳細についてはen.wikipedia.org/wiki/HTML5をご覧ください。
図―5に示すJSONの応答には複数の情報が指定されています。msg名は毎回の応答の際に送り返す一般的なもので、テキスト・メッセージをユーザーに渡すことができます。次の項目はuiactn配列で、この配列にはshowとhideが含まれています。この2つのアクションはそれぞれ名前-値ペアの値部分に指定されている対応するコンポーネントpnl1およびpln2に対して実行されます。図―3に戻って、successというconfigオプションが指定されている箇所で、JSONの文字列を取得してuiactn()という名前のJavaScriptメソッドに渡しています。
uiactn()メソッドの一部を図―6に示します。基本的な概念は、このコードがJSONに対して繰り返し実行され、その中の特定の文字列を処理するというものです。たとえば、図―6中の2つ目と3つ目の赤い矢印の部分では、1つ目の赤い矢印で示したExt.getCmp()の呼び出しで動的に取得したコンポーネントの、hide()メソッドとshow()メソッドを呼び出しています。
図―7は、パネルのフィールドを更新するコードというuiactn()のもう一つの重要な部分を示しています。ここでの考え方は、Ext.getCmp()を使用してコンポーネントを取得し、取得したコンポーネントがどんなタイプであるかを判断して新しい値やテキストを設定するのに適切なAPIを呼び出す、というものです。
uiactn()メソッド内のこの箇所で起きたことを宣言することが重要です。つまり、サーバー(すなわちRPG)が何を表示して何を非表示にするのか、コンポーネントにはどの値を持たせるのかを決めたということです。コントローラー・ロジックはクライアント側のJavaScriptコード内ではなく、サーバー側のあるべきところに存在します。これがJavaScriptとExt JSのプレ・ビルド・コンポーネントを組み合わせて使用することの主な利点です。すなわち、コードの大部分をサーバー側においておくことを容易にするパターンのフレームワークを簡単に作成できるのです。
リクエストと応答の概要
ここで、リクエストと応答がサーバー側で詳細にはどのように処理されているのかを見てみるのが良いでしょう。通信におけるレイヤーの例を図―8に示します。Ext.Ajax.requestのconfigオプションで指定したURLに基づいて、Apacheにリクエストが送信されます。Apacheはこのリクエストを受け取って、プログラム名の直前に指定されているパスに基づいて、このリクエストをどのライブラリにルーティングするかを決めます。次にApacheはコマンド・ラインから起動された時と同様の方法で、ORUINTROというRPGプログラムを起動します。ORUINTROプログラムは制御を受け取ってブラウザからの入力を読み、入力を処理して、最終的にApacheがクライアントに渡し返す応答を生成します。
ORUINTROプログラムのメインラインを図―9に示します。ご覧になっておわかりの通り、最初にinitpgmサブ・ルーチンを起動し、大域変数をいくつか準備してアクションを読み込みます。次にselect文を使用してどのアクションが実行されているのかを確認してからローカルで定義された同じ名前のサブ・プロシージャ、今回の場合はbtnNxtPnl1になりますが、これを起動します。最後に、endpgmサブ・ルーチンを起動します。このサブ・ルーチンは、終了処理をしてbtnNxtPnl1サブ・プロシージャで作成された応答をApacheに送り返します。これがリクエストの大まかなフローです。次に各部分を詳しく見ていきましょう。
図―10はinitpgmサブ・ルーチンです。ここではjson_create()とjsona_create()の呼び出しに注目してください。json_create()の呼び出しはメインとなる応答メッセージへのハンドルを作成し、jsona_create()の呼び出しは、コントローラとビジネス・ロジックの部分に追加するuiactn配列へのハンドルを生成します。「ハンドル」としたのは、このハンドル変数が整数ではなくポインタとして宣言されていることを除いては、IFSファイルのハンドルの動作と似ているからです。ポインタが関わってくるからといってあまり心配しないでください。しばらくすれば慣れます。次に、http_inStr()を呼び出して、Apacheに送られてきたリクエストを大域的に定義されたgIn.stdInという名前の文字列に読み込みます。これは「標準入力からの読み込み」と呼ばれており、実際にはIBMが提供しているAPIを使用してリクエスト全体を取り出していることを、基本的には意味します。これで、変数gIn.stdInには値&action=btnNxtPnl1が含まれていることになります。Ext JSパネルは入力フィールドが多いほど複雑さが増しますから、gIn.stdIn中の名前-値ペアが増えることになります。最後の行では、http_getCgiVal()を使用して変数gIn.stdInからの「アクション」を取得しています。この他にもさまざまなデータ・タイプにアクセスするための、http_で始まるサブ・プロシージャがたくさんあります。たとえば、http_getCgiNbr()は文字列ではなく数値データ・タイプを返し、変換をする手間を省いています。
ここで、変数gIn.actionにはbtnNxtPnl1という値が入っています。メインラインのselect文に戻ると、それに対応するbtnNxtPnl1()というローカルのサブ・プロシージャが起動されます。サブ・ルーチンを使用すべきなのですが、私はサブ・プロシージャがローカル変数を保証してくれるのであればそれを使用したいので、サブ・プロシージャを使用しています。
図―11は、図―12で示したローカル・サブ・プロシージャuiactn()を2度呼び出しているのを含む、btnNxtPnl1()サブ・プロシージャの全体です。基本的には、JSONのuiactn配列に2つの要素を追加したかったのです。1つはpnl1を非表示にし、もう1つはpnl2を表示するための要素です。initpgmサブ・ルーチンでgJuiactnArrayを初期化したのを覚えていますでしょうか。HIDEとSHOWは指定しやすいように大域定数として定義されています。uiactn()の目的は、Ext JSに対してアクションを伝えるたびに3行のコードを節約することです。ただそれだけ。1つのパネルから別のパネルに遷移するのに必要なコードはこれだけなのです。
リクエストと応答の最初から最後までを完了させるプログラムの最後の部分はendpgmの呼び出しで、これは図―13に示されています。まず、json_putString()を呼び出してグローバル変数gMsgTxtをgJRspに追加すること、JSON応答の一番上のレベルにgMsgTxtを置きます。次に、gJRspにgJuiActnArryを追加してuiactnという名前を付けます。(結果としてできるJSON文字列がどのようなものかについては図―5に戻ってください。)
最後にしておかなければならないのは、メモリ中に保存されているクライアントに対して応答を書き出すことです。HTTPで通信しているわけですからHTTPの決まりに従わなければなりません。つまり、応答の「Content-Type」を指定し、その後に改行を2回、そしてその後にExt JSに伝達したいコンテンツを指定しなければなりません。コンテンツはjson_toString()を呼び出してトップ・レベルのgJRspに渡すことで取得します。渡されたコンテンツはすぐにhttp_outPtr()に渡されます。最後に、json_dispose()を呼び出して終了処理をし、メモリ・リークを起こしていないことを確実にします。
この時点でユーザーはpnl2に新しいボタンが付いているのを目にします。ボタンをクリックするとRPGプログラムに別のリクエストが送信され、その特定のボタンidが処理されます。pnl2には特に目新しい点はありませんのでpnl3を見てみましょう。pnl3ではもっと多くのフォーム・フィールドが定義されています(図―14)。pnl3は、それが表示された時点ですでにデータが入っていることにお気づきでしょう。これはユーザーがbtnNxtPnl2ボタンを選択した時に、図―15に示す通り、それに対応するサーバー側のRPGサブ・プロシージャbtnNxtPnl2が起動され、json_putString()とjson_putInt()を使用してJSONの応答を使用し、それがその後クライアント側のJavaScriptのuiactn()メソッドで処理されているからです。使用されているNAM、CRDCD、DOB、AGE、TIMESTAMP等の名前はpnl3中のExt JSの名前と直接対応しています。また図―15にはbtnStayPnl3サブ・プロシージャもあり、これはユーザーとの相互作用後にそのまま残り、パネルのコンポーネントのコンテンツを変更する例を示すために作成されたものです。この場合、クレジット・コードを確認して「Horrible」と分類されればクライアントにメッセージを伝え返します。
氷山の一角
ご覧になっておわかりの通り、Ext JSとOpenRPGUIを組み合わせて使用すると、RPGプログラマが既存のアプリケーションをモダン化する際に多くの希望が持てることになります。コミュニティ主導のOpenRPGUIチームは、このツールを適用すべきところに関して氷山の一角を見ているに過ぎません。私たちは物事をより効率的にするためのアイデアをたくさん持っています。あらゆるモダン化のための努力と同じく、学習曲線というものがありますが、今日現在でこの組み合わせが最短の学習曲線であると思います。それは、すぐに使えるものが多数あり、事前に定義されたパネルと通信するという概念がアプリケーションについての私の考えと一致しているからです。詳細についてはOpenRPGUIのサポート・ページ(openrpgui.com/support)を参照ください。OpenRPGUIのための公式SourceForgeフォーラムへのリンクがあります。