連載 役に立つXMLツール集(11)
XULとJSFでリッチクライアント 〜ロジック編2〜 Page 2

www.netpotlet.com
原田洋子
2004/10/13

リクエストパラメータ取得/ナビゲーションの実装

 ナビゲーションは本連載第9回「XULとJSFでリッチクライアント 〜JSF編〜」の図2にあるNavigationHandlerが担当している処理です。リクエスト処理ライフサイクルの中には現れてきませんが、UIコンポーネントにアクションがセットされていると、ライフサイクルの最後にfaces-config.xmlで指定されているページに遷移してくれるのがナビゲーションです。

準備

 それではプログラムの詳細を見ていきましょう。第9回で説明したように、JSF仕様ではリクエスト処理ライフサイクルを回すのもイベントを受け取るのもUIコンポーネントですから、まずはUIコンポーネントを定義します。ここはコンポーネント作成者の作業です。どのようにするかは本連載第9回で詳しく説明したのでそちらを参照してください。

 第9回ではXULのスキーマを定義せずに、XMLインスタンスであるXULファイルからスキーマを作り出してオブジェクトモデルにマッピングしていました。しかし、これではタグの順番を変えるだけでもプログラムを修正しなければならなくなります。第10回で、JSFのロジックで必要になるタグも含めてXULのスキーマを定義しましたので、今度はスキーマからオブジェクトモデルへマッピングします。モデルのマッピングにはRelaxerを使います。RelaxerによりXMLモデルからオブジェクトモデルへマッピングする方法については本連載第4回「Relaxerでデータバインディングに挑戦しよう」 第5回「スキーマコンバータ/バリデータ/ビジターパターン」、第9回で取り上げていますので、詳細はそちらを参照してください。モデルマッピングのためにリスト6に示すAntのビルドファイルを用意しました。

 AntはUNIXのシェルやWindowsのコマンドプロンプトでantコマンドを実行する方法がありますが、その後の開発やデバッグもあるのでEclipse上で実行すると便利です。今回紹介するサンプルプログラムはEclipse 3.0.0Tomcat Plugin 3.0.0を追加して開発しました。Eclipse上にcoveという名前でプロジェクト(Servlet/JSPのWebアプリケーション相当)を作り、図4に示すようにリスト6のbuild.xmlをcoveフォルダの下に作ったものとしてbuild.xmlのディレクトリ名やファイル名を指定しています。Eclipse上でAntを実行するには

 パッケージ・エクスプローラー → build.xmlを右クリック → Run
 → Ant Build ...

とすると、図5のウィンドウが開くのでここでRunボタンをクリックします。これで、XULのスキーマ定義からリスト6の12行目で指定したパッケージcom.netpotlet.xul.elementのJavaクラスが生成されるので、

 パッケージ・エクスプローラー → 右クリック → Refresh(最新表示)

のようにしてコンパイルします。

図4 coveプロジェクトの構成





図5 Antの実行

リスト6 build.xml(別ウィンドウで表示します)

 以上で、XULをオブジェクトモデルにマッピングできるようになったので、次はJSFのUIコンポーネントを用意します。第9回で説明したように、サンプルではXULファイルを扱うので、各UIコンポーネントに対応するRendererのクラスも定義します。Rendererのクラスはcom.netpotlet.xul.renderkitパッケージに作りました。同時にUIコンポーネントツリーを作るためのビジタークラスも定義します。ビジタークラスについてはここでは説明しませんので第9回を参照してください。また、ビジターパターンそのものについては本連載第5回を参照してください。

リクエストパラメータの取得

 引き続き、コンポーネント作成者の作業です。ブラウザから送信されたリクエストパラメータを取得するのは、リクエスト処理ライフサイクルのうち「Apply Request Values」フェイズで行います。第10回で説明したように、このフェイズで実行されるのは通常、UIComponentのprocessDecodes()メソッドか、このメソッドから実行されるdecode()メソッドですが、Rendererを利用する場合、Rendererのdecode()メソッドが実行されるのでした。ですから、今回のサンプルではRendererのdecode()メソッドを実装します。このメソッド内でブラウザから送信されたパラメータを取得し、さらに、どのボタンが押されたかを判定します。

 リスト7がButtonRendererのdecode()メソッドです。このメソッドでは、32、33行目でリクエストに含まれるすべてのkey=valueペアを取得し、クリックされたボタンかどうかの判定を35、36行目で行っています。ここで紹介するサンプルは常にイベントを発したUIコンポーネントのIDがサーバに送られてくるようにしてあります。

 このように“判定”が必要な理由ですが、これは図6に示すように、リスト1のlanguage.xulをUIコンポーネントツリーにマップするとButtonComponentがツリー内に2つ存在するためです。JSF仕様には「UIViewRoot以下すべてのUIコンポーネントに対してdecode()メソッドを実行する」とありますので、“日本語”ボタンを押した場合でも“English”ボタン用のdecode()メソッドも実行されます。このため、判定が必要になってくるというものです。このように同じ型のUIコンポーネントがツリーの中に複数存在するのはよくあることなので、JSFのUIコンポーネントを作る場合は常にidをチェックするようにするといいでしょう。

図6 UIコンポーネントツリー

リスト7 ButtonRendererのdecode()メソッド(別ウィンドウで表示します)

ナビゲーションの実装

 あるページから別のページに遷移させたい場合、JSFではアプリケーション開発者がリスト8のようにfaces-config.xmlを定義します。from-view-idタグでは遷移元のページへのパスを指定し、to-view-idタグでは遷移先ページのパスを指定します。パスはコンテキスト内のパスで、facesというJSFの仮想パス名を含まないものを指定します。また、遷移の条件をfrom-outcomeタグで指定します。このfrom-outcomeはUIコンポーネントにセットされたアクションの戻り値です。戻り値はリスト1のlanguagu.xulの23、31行目にあるconfigタグのaction属性で指定しました。JSF仕様ではアクションはアクションメソッドをaction="#{actionBean.getAction}"のようにメソッドバインディングの書式で指定することになっていますが、戻り値を返す過程ではロジックを動かす必要がない場合がよくあるので、値で指定できるようにしてあります。

 コンポーネント作成者の作業は、UIコンポーネントにアクションをセットすることです。これはUIコンポーネントツリーを作るフェイズで実行されるので、リスト9のようにしています。リスト9の24行目ではbuttonタグの子要素を取得してsetup()メソッドに渡しています。setup()メソッドでは37行目で渡された子要素の中からconfigタグを見つけ出し、configタグにaction属性があったら(42、43行目)、メソッドバインディングの書式になっているかをチェック(45行目)します。メソッドバインディングではない場合、アクションの戻り値が指定されたものとしてリスト10のConstantMethodBinding型のオブジェクトを作ります(50、51行目)。このMethodBinding型のオブジェクトをUIコンポーネントのアクションにセット(53行目)しておけば、リクエスト処理ライフサイクルが進んでいく過程で、ナビゲーションが指定どおり行われます。

リスト8 faces-config.xml(別ウィンドウで表示します)

リスト9 ナビゲーションのためのアクション設定(別ウィンドウで表示します)

リスト10 値で指定されたアクションを扱うクラス ConstantMethodBinding(別ウィンドウで表示します)

サンプルの動作について
  UIコンポーネントにセットしたアクションのメソッドが実行され図1の“English”ボタンが押されたら図3の画面、“日本語”ボタンが押されたら図2の画面が表示されるのですが、ここでパラメータを入力してボタンを押してもサーバにリクエストを送れません。これは、表示は変わったもののブラウザが参照しているファイルがlanguage.xulのままなのでuserinfo_ja(en).xulのJavaScriptが参照されないためのようです。JavaScriptでレスポンスの内容をalertウィンドウに表示させると確かに図7、図8のように遷移先ページの内容が表示されるので、userinfo_ja(en).xulがJavaScriptも含めてレスポンスで返ってきていることを確認できます。window.open()するときに遷移先urlを指定する方法も考えらるので、JSFのナビゲーションをデフォルトのフォワードからリダイレクトに変更してLocationヘッダの取得を試みました。しかし、XULでLocationヘッダを取得できない(図9)ので、この方法は使えませんでした。

 この問題については解決後、このページに追加するなどどこかで報告したいと思います。

図7 JavaScriptでレスポンスの内容をalertウィンドウに表示(日本語)


図8 JavaScriptでレスポンスの内容をalertウィンドウに表示(英語)


図9 XULでLocationヘッダの取得に失敗

次ページへ続く)

2/3

 Index
連載 役に立つXMLツール集(11)
XULとJSFでリッチクライアント 〜ロジック編〜
  Page 1
・はじめに
・サンプルの概要
Page 2
・リクエストパラメータ取得/ナビゲーションの実装
  Page 3
・検証/変換/ロジック実行の実装
・まとめ&サンプルダウンロード
・ごあいさつ


「連載 役に立つXMLツール集」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間