ここでは、次の内容を中心としてサンプル・アプリケーションの詳細について説明します。
| 注意 |
ここでは、
「サンプル・アプリケーションのインストールと設定」の項の指示に従っていること、
|
MVCアーキテクチャでは、ユーザー入力の処理、ビジネス・サービスとの連携処理、およびユーザーに表示するページの決定を行うすべてのプロセスがコントローラ層内にあります。 Webアプリケーションのコントローラ層に対するベスト・プラクティスなアプローチは、 Front Controllerデザインパターンを実装することです。 受け取る要求のタイプに基づいて、フロント・コントローラは要求の処理をタスク固有のコントローラ・ロジックへ委譲します。 通常、フロント・コントローラはサーブレットとして実装され、 web.xml内で構成されており、アプリケーションに関連するURLのすべての要求を処理します。 ユーザーは、このコントローラ・サーブレットを手動で実装することも可能ですが、
Apache Strutsのフレームワークには、このジョブを処理するための実装があらかじめ用意されています。
Strutsを使用し、次の内容を実行することによって、Webアプリケーションの「ページ・フロー」を記述します。
たとえば、簡単なWebストアのアプリケーションでは、ユーザーは、
/yourcartという名前のアクションを使用して買い物かごの中身を調べるかもしれません。 一般に、Strutsを使用する場合、ブラウザから *.doという接尾辞と一致するURLをリクエストしたときにアクションが実行されるため、 /yourcartアクションのURLは、次のようになります。
http://yourcompany.com/ADFToyStore/yourcart.do
買い物かごを調べる一方で、ユーザーは、/reviewcheckoutアクションのリンクをクリックして、購入した内容を確認し、支払の処理を開始することもできます。
これらのアクション、およびアクションに関連付けられているリンクをビジュアルに表すと、図34のようになります。
/yourcartおよび
/reviewcheckoutのアクションはギア型のアイコンとして示されますが、ビュー層を表すJSPページは、ページ型のアイコンとして示されています。 Strutsの用語では「フォワード」と呼ばれるアクション間のリンクは、ここではアクションからターゲットのページまたはアクションへの実線として示されています。
点線は、Webページ内に、リンクまたは矢印の先のアクションをターゲットとするHTMLフォームがあることを表します。 これはほんの一例ですが、より複雑なアプリケーションに対するStrutsのアクションとページに対して、ビジュアル・ダイアグラムが、わかりやすい「ページ・フロー」を示してくれることは想像できるでしょう。このページ・フローでは、ユーザーが遷移できるページ、およびどのようにしてページ間を遷移するかが示されています。
ご想像のように、それぞれのアクションに関連付けられているコントローラ層の動作は、関連付けられているJavaクラス内に記述されます。 これらのアクション・クラスは、各要求に応じて、Web層での処理を調整します。 図35は、ユーザーが、Strutsアクションを表すリンクをクリックするたびに発生する一般的な手順を表しています。
| 注意 |
次の 「ADF/Strutsの統合について」の項では、ADFのビジネス・デリゲート実装がどのように機能するかのメカニズムについて調べます。 |
図34に戻ってみると、
/yourcartアクションと
yourcart.jspページは一種の共生関係にあることがわかります。
/yourcartアクションは、データが
yourcart.jspで表示されるよう準備し、
yourcart.jspページは、すべてのリンクとHTMLフォームのターゲットを /yourcartアクションへ戻して、コントローラ層での詳細を処理して次に何をするかを決定できるようにしています。 このように、1つのアクションとページが相互に依存していて、必ず同じアクションへ戻る(バック)ような送信(ポスト)を行う状況を、「ポストバック・パターン」であると言います。アクションは、ページで表示できるようにデータを準備し、ページはページでのイベントの処理のためにアクションへポストバックします。
図36は、JDeveloperのStrutsページフロー図で、StrutsおよびADFを使用した「ポストバック・パターン」実装の2つの方法を示しています。 一つ目のアプローチは、すでに説明済みです。 アクションとページの両方について、それぞれのアイコンがあり、アクションとページ間の両方向に対して関連線があることを確認しました。 もう1つの方法は、ADFの「DataPage」を使用するもので、これは「ポストバック・パターン」実装に含まれているアクションとページの組合せを、1つのアイコンで表しています。
ご想像どおり、「DataPage」のアプローチを使用した方が、大きいサイズのアプリケーションの場合に、ページ・フロー図の把握がより簡単になります。 後で確認しますが、ADF ToyStoreのコントローラ層は、このDataPageアプローチを使用して実装されています。
| 注意 |
DataPageは設計時のための抽象概念で、StrutsベースのMVCアーキテクチャにおいて、ポストバック・パターンに沿ったページの表現を簡潔するためのものに過ぎません。 DataPageの、実行時における実体は、単なる標準のStrutsアクションと、制御がフォワードされるページであり、Strutsの |
Strutsは、他の構成情報とともに、アクション名とアクション・ハンドラ・クラスとのマッピング情報を、 struts-config.xmlファイルに保持しています。
図37の
ToyStoreViewControllerプロジェクトに示されているように、この構成ファイルは、ユーザーの
Webコンテンツ
を構成する他の要素とともに、アプリケーションの標準の
WEB-INFディレクトリに格納されています。
struts-config.xmlファイルをダブルクリックすると、デフォルトで、ビジュアルなページ・フロー図が表示されます。 ADF ToyStoreサンプル・アプリケーションのページ・フローを調べて、これらの図の1つ1つがどのように表示されるのかを検討します。
図38は、ADF ToyStoreサンプル・アプリケーションのコントローラ層に対するページ・フロー図を表しています。 それぞれのDataPageは、JSPページとStrutsアクション(表示するモデル・データを準備し、ポストバック・イベントを処理するためのアクション)の組合せによって実装されています。 これらのページ/アクションのペアは1つのアイコンとして表示されるため、ページ・フロー図は簡潔でわかりやすくなります。
エディタの
「ソース」タブをクリックすると、ADF ToyStoreサンプル・アプリケーションに対するStrutsのアクション・マッピング
のXMLソース・コードが示されます。
/yourcartを検索すると、
例6のような XMLタグを見つけることができます。 「検索」メイン・メニューで、
「検索...」または
「次をインクリメンタル検索」(
Ctrl+
E)オプションのいずれかを使用して、場所を特定できます。
<struts-config>
|
struts-config.xmlファイルには、構成情報の他の部分とともに、アプリケーションのそれぞれのアクションに対応する <action>タグが含まれています。 Strutsのactionタグでネストされている
<forward>要素は、たとえば、 /yourcartページからたどることができる有効なページ・ナビゲーション「ルート」である、 reviewcheckoutのような、論理的な名前を提供します。この例では、支払いする商品を確認するためのルートとして、フォワード先が1つだけ用意されています。
parameter属性の値は、
/yourcartアクションが、
/WEB-INF/jspディレクトリにある
yourcart.jspページに関連していることを示しています。 また、このアクションが toystore.controller.strutsactionパッケージの
YourCartActionクラスにマップされていることもわかります。 このアクションには、
UpdateCartおよび
RemoveItemのイベントを処理するためのコントローラ・ロジックが含まれています。これらのイベントは、ユーザーがページで適切なボタンをクリックすると発生し、買い物かごの数量を調整するために、 ToyStoreServiceの
adjustQuantitiesInCartAsStringArrays()を呼び出します。 このアクションには、ビジネス・サービスから買い物かごの合計を取得してページに表示するためのコードも含まれています。
YourCartActionクラスは、
FwkExtensionsプロジェクトの
ToyStoreDataForwardActionクラスを拡張したものです。 このフレームワークの拡張クラスは、ADFの
DataForwardAction からの多数の組込み動作を継承し、加えて、ToyStoreサンプル・アプリケーションの多くのアクションで有用なヘルパー・メソッドがいくつか追加されています。
アクション定義タグの中に登場する modelReferenceプロパティの値は、データ・バインディング機能のための定義情報を指しています。
| 注意 |
|
表2は、それぞれのアクションが実行する内容について説明しています。
| アクションのパス名 | データページ |
|---|---|
/home
|
ToyStoreのトップページ |
/showcategory
|
あるカテゴリの製品を表示する「Category」ページ |
/showproduct
|
製品項目を表示する「Product」ページ |
/showproductdetails
|
製品の詳細ページ |
/yourcart
|
ショッピング・カート・ページ |
/search
|
検索ページ |
/signin
|
サインイン・ページ |
/register
|
新規ユーザー登録ページ |
/editaccount
|
ユーザー情報編集ページ |
/reviewcheckout
|
処理の確認ページ |
/confirmshippinginfo
|
出荷情報の確認ページ |
/thankyou
|
「Thank You」ページ:注文番号が表示されます |
/revieworder
|
注文の確認ページ |
/revieworderxml
|
注文の確認情報をXMLとして取得 |
struts-config.xmlファイルがアクティブな場合、JDeveloperの構造ウィンドウとプロパティ・インスペクタによって設計時の追加サポートが提供されます。 構造ウィンドウには、構成ファイルの内部構造を表すツリーが表示されます。 ツリーの要素をクリックすると、プロパティ・インスペクタでそのプロパティを参照または編集することができます。
図39では、ADF ToyStoreサンプル・アプリケーションのすべてのStrutsアクションが、ツリーの
「Action Mappings」フォルダに表示されており、プロパティ・インスペクタには、選択された
/yourcartのアクションのプロパティが表示されていることがわかります。
「Form Beans」や 「Action Mappings」など、適切なノードを選択し、マウスを右クリックしてメニューで 「New」を選択すると、その種類の新しいエントリを作成できます。 次にプロパティ・インスペクタを使用して、様々なプロパティを構成することができます。 また、マウスを右クリックしてメニューで 「編集」をクリックすると、 図40のようなStruts構成エディタを介して、ファイルの任意の項目を編集することもできます。 また、構造ウィンドウでアクションを選択し、マウスを右クリックしてメニューで 「コードに移動」を選択すると、該当するタグ・コードにジャンプできます。
ここでわかるように、Strutsの構成情報を操作するために、Struts ページフロー図、構造ウィンドウおよびプロパティ・インスペクタの組合せを使用して、処理に必要なすべてのことを実現することができます。 逆に、XMLソースを編集したい場合には簡単に実施可能で、ユーザーが手動で行った編集内容とページ・フロー図を自動的に同期化します。 この選択は自由です。
コントローラ層のアクション・クラスの管理に関連する補足機能関として、Strutsでは、アクションでのHTMLフォームの入力処理のサポート機能があります。 Strutsのコントローラ・サーブレットは、フォームBean と呼ばれるオブジェクトの属性に、送信されたHTMLフォームのデータを移入します。 そこで、アクション・クラスを記述する開発者は、Beanのgetterおよびsetterメソッドを使用して、HTMLフォームの値を操作することができます。 ユーザーがHTMLフォームで入力したデータは文字列として受け取られ、入力されたデータにおける検証エラーなどは、ユーザーの再入力(修正)の際に、そのまま再表示して値を示すことが期待されるため、通常、フォームBeanのすべてのプロパティはString型として処理します。
例7は、
/signinアクションが、
toystore.controller.strutsactionsパッケージの
SignInActionアクション・クラスにマップされていることを表しています。 また、 <action>タグの追加属性をみると、Strutsのコントローラが loginformというフォームBeanを使用して、送信されたHTMLフォームのデータをカプセル化し、なんらかの検証エラーがあった場合にそれを signin.jspページに返すことを示しています。
ここで、
loginformは
<form-beans>セクションで定義されている論理名で、
toystore.controller.strutsformbeans.LoginFormというJavaBeanクラスに対応していることに注意してください。
<struts-config>
|
これらの追加属性セットによって、Strutsのコントローラは、実行時にユーザーがフォームを送信したとき、サインイン・ページのHTMLフォームのusernameとpasswordの値を
LoginForm Beanへ移入します。 これらの値は、
SignInActionクラスの
onVerifySignin()イベントハンドラ・メソッドによってアクセスされます。このメソッドは、ユーザーがフォームを送信したときに、そのポストバック処理の中でコールされます。
例8は、フォームBeanの
onVerifySignin()メソッドを表しています。 このメソッドは、ユーザー名とパスワードのプロパティの両方が空白になっていないかを確認し、次のメソッドをコールします。
public boolean validSignon(String username, String password)
上記メソッドは、
ToyStoreServiceビジネス・サービス・インタフェースのメソッドで、ユーザー名とパスワードの組合せが正しいWebストア・ユーザーを表しているかどうかを検証するものです。 検証チェックに失敗した場合は、このメソッドによって、Strutsの
ActionErrorオブジェクトが
ActionErrorsのコレクションに追加され、ビュー層で適切なエラー・メッセージをユーザーに表示できるようになります。 エラーを示す文字列キー(定数 INVALIDLOGINなど)は、ユーザーが認識しやすい適切なメッセージに変換されますが、それについては、「多言語アプリケーションを作成するためのStrutsとADFの機能」の項で説明しています。
// Method in the SignInAction Struts Action class
|
前述のログイン・フォームの送信に対して、検証に失敗すると、再入力のためにsigninページがユーザーに提示されます。 検証で成功すると、前述のコードによってヘルパー・メソッドがコールされ、対象ユーザーがサインインしたことを示すフラグが設定され、適切なページが返されます。 また、このアクションでは、
targetパラメータを設定していますが、これは、このサンプル・アプリケーション内の他の任意のアクションで、次の処理の前にユーザーのログインを要求したい場合に使用します。このような場合、
targetパラメータで適切な「次の」ページを指定した上で、
signinページにフォワードすると、ログイン認証の後、指定した「次の」ページに転送されるようになります。
これまで見てきたように、Strutsは、Webアプリケーションのコントローラ層を実装するための便利なフレームワークです。 「JSPページとJSTLによるビュー層の構築」項で詳しく説明しますが、Strutsには、ビュー層の構築を簡単にするための、いくつかのJSPタグ・ライブラリも用意されています。 ただしStrutsでは、実際には、アプリケーションの開発においておそらく最も重要で時間がかかる作業である、モデル層の実装をサポートするための組込みの機能は提供しません。 モデル層には、ユーザーのビジネス・ロジック、およびデータ・アクセスのすべての機能が格納されています。 J2EE開発者が必要とする、強力なMVCアプリケーション・インフラストラクチャを完成させるためには、Strutsとのすぐれた統合を実現するモデル層を構築するための有効なアプローチが必要です。 この項では、Oracle ADFフレームワークが、この試みにおいてStrutsの最適なパートナーになっている状況について説明します。
ADFは、モデル層の開発を促進するためにビジネス・コンポーネントの仕組みを提供している以外にも、Strutsのコントローラおよびビュー層の機能と調和させるための特別な機能を提供しています。 たとえば、次の機能があります。
もちろん、JDeveloper では、これらのすべての機能をアプリケーションに組み込むことを容易にする、生産性の高い開発環境を提供します。
| 注意 |
『Oracle ADFによるデータ・バインディング』というドキュメントでは、ADFのデータ・バインディングとStrutsに関するすべての機能について、簡単な3つのADF/Strutsアプリケーションの実装を交えて説明しています。 ここでは、この情報についても取り上げますが、これらの機能をどのように使用してADF ToyStoreサンプル・アプリケーションを実装するかを中心に説明します。
|
Oracle ADFには、バックエンド・ビジネス・サービスに対する「データ・コントロール」という抽象化層、およびフロントエンドのユーザー・インタフェース・コントロールを、データ・コントロールで提供されるデータと宣言的に連結するバインディング層が含まれています。 JSR 227に基づいたこれらの要素は、J2EEアプリケーションに対して首尾一貫したプラッガブルなモデル層を実現するもので、J2EEの標準になりつつあります。
図41は、ADF全体像の中で、モデル、ビュー、コントローラおよびビジネス・サービス・アーキテクチャとADFデータ・コントロール/ADFバインディングがどのように組み込まれているかを示しています。
Oracle ADFの主なデータ・バインディング概念は次のとおりです。
データ・コントロール
データ・コントロールは、ビジネス・サービス実装を抽象化し、バインディング層が一貫した方法ですべてのサービスからデータをアクセスできるようにしています。
イテレータ・バインディングとコントロール・バインディング
バインディングは、バックエンド・データとフロントエンドUI表示を分離する軽量オブジェクトです。 イテレータ・バインディングは、データ・コントロールによって提供されたデータ・オブジェクトのコレクションを扱う一貫した方法を提供します。 コントロール・バインディングはUIコンポーネントに対する標準インタフェースを提供して、そのインターフェースを通して、イテレータ・データのやりとりや、モデル・データの設定/イベント処理を行う「アクション」メソッドの呼び出しなどを可能にします。 バインディングは、動的な、多言語対応のユーザー・インタフェースを容易に構築可能にするための主要メタデータも公開しています。
バインディング・コンテナ
バインディング・コンテナは、アプリケーションの特定のページ(またはパネル)用に使用する、イテレータ・バインディングおよびコントロール・バインディングの名前付きグループです。 バインディング・コンテナはUI用にモデル・データの適切なサブセットを提供するため、「UIモデル」とも呼ばれます。
バインディング・コンテキスト
バインディング・コンテキストはユーザー・アプリケーションに対してデータ環境を提供します。 ユーザー・アプリケーションからアクセスできるすべてのデータ・コントロールおよびバインディング・コンテナが含まれます。
JDeveloper は、設計時に様々な方法でこれらのデータ・バインディングの概念をサポートしています。 一度ビジネス・サービスを作成すると、それをデータ・コントロールとして公開し、データ・コントロール・パレットに表示することができます。
図42は、
ToyStoreServiceImplアプリケーション・モジュール・コンポーネントに基づいた
ToyStoreServiceデータ・コントロールが、データ・コントロール・パレットでどのように見えるかを表しています。 ここでは、モデル・データ・マップのすべてのデータ・コレクション、および 「Operations」フォルダのToyStoreServiceサービス・インタフェースのカスタム・メソッドを参照することができます。
| 注意 |
ADFアプリケーション・モジュールとして構築されたサービスは、JDeveloperによってデータ・コントロールとして自動的に公開されます。 デフォルトでは、
他のタイプ(シンプルなJavaBeanや、EJB、Webサービスなど)のビジネス・サービスでは、追加で 「データ・コントロールの作成...」という手順を実行してデータ・コントロール化します、また、プロパティ・インスペクタでデータ・コントロールの |
データ・バインドされたページを(データ・コントロール・パレットからドラッグ&ドロップで追加する、または明示的に作成することによって)作成すると、アプリケーションのバインディング・コンテキスト定義情報が保持されます。 この情報には、アプリケーションが使用しているデータ・コントロール、作成した様々なバインディング・コンテナ、およびそれらのコンテナに含まれているバインディング・オブジェクトが記述されています。 JDeveloperは、バインディング・コンテキスト定義情報を DataBindings.cpxファイルに格納します。 ナビゲータでこのファイルをクリックすると、
図43に示すように、構造ウィンドウにアプリケーションで使用中のデータ・コントロール、およびアプリケーションの様々なページを構成する各種のバインディング・コンテナ(「UIモデル」とも呼ばれる)が表示されます。 構造ウィンドウで ToyStoreServiceなどをクリックすると、プロパティ・インスペクタにそのプロパティが表示されます。
あるJSPページを参照しているときに、 図44に示すように、構造ウィンドウの「UIモデル」タブをクリックして、そのページに含まれているバインディング・オブジェクトの詳細を表示することができます。 特定のバインディング・オブジェクトをクリックすると、プロパティ・インスペクタでそのプロパティを表示し、編集できます。また、マウスを右クリックしてメニューで 「編集...」を選択すると、専用のエディタを使用して編集することも可能です。
Apache StrutsアプリケーションでADFバインディング層の利用を簡単にするために、Oracle ADFには、Strutsのためのコントローラ層コンポーネントが用意されており、StrutsとADFバインディング層をシームレスに統合することができます。 これらのコンポーネントには、次のものが含まれています。
ADFBindingFilter。このフィルタは、ビジネス・デリゲート、ビジネス・サービス・プーリング、およびステート管理の使用を調整します。
DynaActionFormを拡張した、汎用で利用可能な
BindingContainerActionForm。これを使用すると、作成するそれぞれのページに対して、StrutsのフォームBeanを作成する必要がなくなります。 Actionクラスを強化したDataAction。このクラスは、リクエスト処理の中で、ADFのデータ・バインディング機能を利用できるようにライフサイクルを実装しています。もちろん、このライフサイクルは、簡単にカスタマイズ可能です。
ActionMapping の拡張クラスであるDataActionMapping。このクラスは、データ・バインディングの自動処理のために使用される追加の宣言的な定義情報を、アクション・マッピングの拡張プロパティから取得します。
ADFとStrutsの連携利用を支援するものとして、Struts ページフロー図と、ADFの「DataPage」の概念があります。 DataPageは、ページとStrutsアクションを、ポストバック・パターンによって連携するように構成したい場合に、その作業を簡易化します。 JDeveloper のStrutsページフロー図では、Webページと DataForwardActionのセットを1つのアイコンで表すことができます。
DataForwardActionは、ADF
DataActionがもつ機能(ライフサイクル機能とDispatchActionに類似したイベント処理)と、Strutsの
ForwardActionが持つ機能(暗黙的なページ転送機能とイベント処理)を組み合わせたものです。
DataForwardActionは、
(DataActionから)イベント処理機能を継承しています。この機能は、Strutsの
DispatchActionに似ています。 HTMLフォームまたはリンクで、
eventという名前のリクエスト・パラメータを含めてDataActionに送信すると、その値は処理の対象となるイベントの名前として取り扱われます。たとえば、フォーム・フィールドを使用してリクエスト・パラメータとして
event=YourNameを送信すると、
YourNameというイベントが呼び出されることになります。または、HTMLのボタンで、name属性としてevent_YourNameという値を使う方法でも、同じ
YourNameというイベントが発行されます。
ADF DataActionは、イベントの処理に関して、主に3つの機能サポートしています。
YourEventという名前のイベントが起動された場合、
リクエストを処理するDataActionクラスに
public void onYourEvent (DataActionContext ctx)メソッドがある場合は、イベント処理時に、このメソッドが呼び出されます。これにより、カスタム・コードでイベントを処理できます。
YourEventという名前のアクション・バインディングがある場合は、このアクション・バインディングが呼び出されます。
1のようなカスタム・イベント・コードと組み合わせて使用したい場合は、イベント処理コードから、次のようなコードを使用して、デフォルト・アクションを明示的に呼び出します。
if (ctx.getEventActionBinding() != null) {
PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
}
YourEventという名前のStrutsフォワードがある場合は、それを使用して次の制御を転送します。
1のようなカスタム・イベント・コードと組み合わせて使用する場合に、イベント処理コード内でctx.setActionForward()を呼び出すと、そのコードで設定した転送先が優先されます。
これらの基本的な内容をふまえたうえで、次にADFとStrutsの統合について、および、その統合機能をADF ToyStoreサンプル・アプリケーションでどのように活用しているかについて説明します。
図45は、Webページ・リクエストのライフサイクルを表すために、その「遷移部分」の全貌をシーケンス図でまとめたものです。
http://yourserver/yourapp/some.doに対してWebリクエストされます。
ADFBindingFilterは、HTTPセッション内でADFバインディング・コンテキストを検索し、見つからない場合は、最初に初期化します。
バインディング・コンテキストを初期化する際に、
ADFBindingFilterは次の処理を行います。
CpxFileNameという名前のサーブレット・コンテキスト初期化パラメータを調べて、その値に拡張子として
.cpxを追加した名前で、バインディング・コンテキスト定義ファイルの名前を特定します。 デフォルトでは、パラメータの値は
DataBindingsになっているため、ファイル名は
DataBindings.cpxになります。
ADFBindingFilterが、バインディング・コンテキストのそれぞれのデータ・コントロールで
beginRequest()メソッドを呼び出します。 これによって、すべてのデータ・コントロールに対して、リクエストの開始点にきたことが通知されます。この通知により、各データ・コントロールで必要なセットアップ作業が実施されます。
ADFアプリケーション・モジュールをベースとしているデータ・コントロールは、この
beginRequest通知によって、アプリケーション・モジュール・プールからインスタンスを取得します。
Strutsの
RequestProcessorは、
struts-config.xmlに構成されている
/someアクションに対応したアクション・クラスに対して、制御を転送します。
|
注意 |
正確には、最初はStrutsのActionServletが
RequestProcessorへ制御を転送しますが、 実質、RequestProessorがその後のすべての処理を実施するため、図の中には ActionServletを省略しています。
|
Strutsのアクション・クラスがADFの
DataActionクラスである、またはそれを拡張している場合は、次の内容を含む一連の処理手順(まとめてリクエスト処理の「ライフサイクル」と呼ばれる)が実行されます。
*UIModel.xmlメタデータ・ファイルに対応するバインディング・コンテナに対して、バインディング・オブジェクトが作成されます。
prepareModel()フェーズでそれを実行します。
processUpdateModel()フェーズで、対応するバインディング・オブジェクトに対してそのデータで更新処理をします。
invokeCustomMethod()フェーズでコントローラ層のカスタム・ロジックが(あれば)実行されます。
findForward()フェーズで、次に表示するページを決定します。
ライフサイクルの各フェーズは、開発者が上書きすることができます。また、それぞれのメソッドは、
DataActionContextオブジェクトに渡されますが、このオブジェクトでは、バインディング・コンテキスト、バインディング・コンテナ、およびStrutsに関するすべての要素(サーブレット・リクエストやレスポンス、アクション・マッピング、アクション・フォームなど)にアクセスできます。 アクションは、Strutsの
ActionForwardを返し、これによって、次にナビゲートするアクションまたはページを表します。
| 注意 |
Strutsのアクション・クラスがDataActionを拡張していない場合は、アクション・クラスはバインディング・コンテナを手動で検索(および必要な場合は初期化)し、バインディングとの処理を自身で管理する必要があります。 『Oracle ADFによるデータ・バインディング』のホワイト・ペーパーには、この処理を行うためのコード例が記載されています。 |
Strutsの
RequestProcessorは、次に制御を渡すようアクションが指定したアクションまたはページに転送します。
転送先がJSPページであるとします。この場合、ページは、コントロール・バインディングからデータ転送オブジェクトのコレクションへアクセスして反復処理を行い、書式化されたWebページへと書き出されます。
Strutsの
RequestProcessorは、
ActionServletへ制御を返します。さらに、サーブレット・コンテナへ制御を戻します。
ADFBindingFilterは、バインディング・コンテキストのそれぞれのデータ・コントロールで
endRequest()メソッドを呼び出します。これによって、すべてのデータ・コントロールに対して、リクエストの終了点にきたことが通知されます。この通知により、各データ・コントロールで必要なリソースのクリーンアップ作業が実施されます。
ADFアプリケーション・モジュールをベースとしているデータ・コントロールは、この
endRequest通知によって、アプリケーション・モジュール・プールへインスタンスを解放します。
ユーザーは、結果ページをブラウザで参照できます。
このシーケンス図をみると、バインディング・コンテナが、コントローラ層によるモデル・データの操作の際も、ビュー層によるモデル・データの反復処理とレンダリングの際も、使用できるということがわかります。
JDeveloperを使用してADFベースのStrutsアプリケーションを作成する場合には、設計時のサポート機能によってWebアプリケーションが自動的に構成されます。 ただし、ツールが設定する内容を理解しておくと、個々の設定がどのように統合されるのかを理解するうえで有効です。 J2EE Webアプリケーションでは、いくつかの構成手順において標準の web.xmlファイルを使用します
。 この項では、設定の手順でいくつか興味深い点を中心に説明します。
まずStrutsを使用するためには、
ActionServletという名前のStrutsのフロントコントローラ・サーブレットを構成し、URLパターンにマップする必要があります。 ADF ToyStoreでは、一般的な
*.doというURLパターンを使用して、リクエストを ActionServletへマップしています。
ToyStoreViewControllerプロジェクトの
「Webコンテンツ」フォルダの
WEB-INFディレクトリにある
web.xmlファイルの該当個所は、次のようになっています。
<web-app>
:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
:
</web-app>
2番目の手順は、
ADFBindingFilterを設定して、 *.jspまたはStrutsの
ActionServletに関するURLが処理されたときに、これを連動させることです。
web.xmlファイルの該当個所は、次のようになります。
<web-app>
:
<filter>
<filter-name>ADFBindingFilter</filter-name>
<filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ADFBindingFilter</filter-name>
<servlet-name>action</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>ADFBindingFilter</filter-name>
<url-pattern>/*.jsp</url-pattern>
</filter-mapping>
:
</web-app>
ADFBindingFilterは、アプリケーションに対するバインディング・コンテキスト定義情報を記述したファイルの場所を認識している必要があります。 ここでは、 CpxFileNameというサーブレット・コンテキストの初期化パラメータを読み込んで、ファイル名を特定します。 ADF ToyStoreの
web.xmlファイルでは、このパラメータがデフォルトの
DataBindingsという値を使用していることがわかります。 これは、
ADFBindingFilterが
DataBindings.cpxファイルを使用して、この情報を読み込むことを意味しています。
<!-- web.xml file -->
<web-app>
<description>web.xml file for the ADF Toy Store demo application</description>
<context-param>
<param-name>CpxFileName</param-name>
<param-value>DataBindings</param-value>
</context-param>
:
</web-app>
ToyStoreViewControllerプロジェクトの
「アプリケーション・ソース」フォルダに、
DataBindings.cpxファイルがあります。 これは、ADFのデータ・コントロール、およびADF ToyStoreアプリケーションで使用するバインディング・コンテナの定義情報をすべて記述しているXMLファイルです。 このファイルを見てみると、ADF ToyStoreサンプル・アプリケーションには ToyStoreServiceという1つのデータ・コントロールがあることがわかります。 ナビゲータまたはコード・エディタで
DataBindings.cpxファイルが選択されている場合には、同じ情報が構造ウィンドウのツリーで表示されます。
<!-- DataBindings.cpx File -->
<JboProject id="DataBindings" ... >
<Contents>
<DataControl
id="ToyStoreService"
SubType="DCBC4J"
SupportsFindMode="true"
SupportsTransactions="true"
Package="toystore.model.services"
FactoryClass="oracle.adf.model.bc4j.
DataControlFactoryImpl"
Configuration="ToyStoreServiceLocal" >
<Parameters >
<Parameter
name="Sync"
value="Immediate" >
</Parameter>
</Parameters>
</
DataControl>
:
</Contents>
</JboProject>
ADF Business Componentsベースのこのデータ・コントロールには、
Packageおよび
Configurationという名前のプロパティがあります。 これらの属性の値は、ADFのBusiness Delegateパターン実装ロジックによって、適切なビジネス・サービスのインスタンスを検索するために使用されます。この例では、このビジネス・サービスの構成プロパティは、 ToyStoreServiceLocalという構成によって定義されています。 パッケージ名を指定すると、ADFは実行時に、パッケージ名に対応するディレクトリの
commonサブディレクトリで、 bc4j.xcfgファイルを最初に検索します。 前述の例では、指定されたパッケージ名が
toystore.model.servicesであるため、実行時のクラスパスから ./toystore/model/services/common/bc4j.xcfgというリソースを開いて読み込みます。 そのファイルには、 ToyStoreServiceLocalという構成定義の中に、ビジネス・サービスの実装として使用される、アプリケーション・モジュールの完全修飾名を表すプロパティが含まれています。
| 注意 |
構成とは、名前が付けられた構成プロパティのセットで、ADFはこれを使用してアプリケーションの配置を簡潔にします。 アプリケーション・モジュールの構成を表示または編集するには、アプリケーション・ナビゲータでアプリケーション・モジュールを選択した後で、マウスを右クリックしてメニューで 「構成...」オプションを選択します。 |
ビジネス・サービスへの参照を、このようなベスト・プラクティスな抽象的方法によって実施できることにより、アプリケーションの残りの部分に影響を与えずに、モデル層の配置の方針を簡単に変更することができます。 ADF Business Componentsを使用してモデル層を構築していれば、後でADF/Strutsのアプリケーションを、簡単なJavaBeanベースのビジネス・サービスの使用から、EJB Session Beanとして配置されているアプリケーション・モジュールの使用へ変更する場合も、データ・コントロールの定義情報で前述のConfiguration属性の値を変更するだけです。 このときに置き換える構成定義には、EJB Session Bean設定に関するさまざまな情報が含まれることになりますが、それはXMLの構成情報の中に明確に切り出されており、コード内の変更を必要としません。 もちろん、設計時には、これらの複数の構成の初期設定の自動化や、構成パラメータ編集用の専用ダイアログといった支援機能が提供されます。
ユーザーがアプリケーション・データを入力または編集できるようなHTMLフォームを作成する場合には、前述のように、Strutsによって、FormBeanのインスタンスへフォーム・データが抽出され、ユーザーのアクションで簡単に処理できるようになります。 HTMLフォームとのやり取りに関して、アクションでの、比較的頻度の高いタスクとして、次の3つがあります。
Oracle ADFには、連携するための重要な2つの要素が用意されており、これらのシナリオを簡単に実装することができます。
DynaActionFormを拡張した、汎用で利用可能な
BindingContainerActionForm。これを使用すると、作成するそれぞれのページに対して、StrutsのフォームBeanを作成する必要がなくなります。
Actionクラスを強化した
DataAction
。このクラスは、リクエスト処理の中で、ADFのデータ・バインディング機能を利用できるようにライフサイクルを実装しています。もちろん、このライフサイクルは、簡単にカスタマイズ可能です。
図9に示すような /register DataPageを使用してADF ToyStoreのサイトに新しいユーザーを登録すると、国のデフォルトが「USA」になっていることに気付きます。 これは、このデータ入力フォームで使用されている Accountsビュー・オブジェクトが、そのベースである Accountエンティティ・オブジェクトと関連しており、 Accountエンティティ・オブジェクトではCountry属性でUS を初期値として定義しているためです。
ユーザーが「Register as a New User」フォームの
「(Save Changes)」ボタンをクリックすると、フォームは
/register.doアクションに送信されます。 Strutsのフレームワークは、フォーム・パラメータの内容を DataForm(このアクション・マッピングに対して構成されているFormBean)へ移入します。
struts-config.xmlの form beanセクションを見てわかるように、DataFormフォームBeanは、 BindingContainerActionFormクラスを使用しています。
<form-beans>
<form-bean name="loginform"
type="toystore.controller.strutsformbeans.LoginForm"/>
<form-bean name="DataForm"
type="oracle.adf.controller.struts.forms.BindingContainerActionForm"/>
</form-beans>
このフォームBeanはStrutsのDynaActionFormの拡張で、現行のバインディング・コンテナで定義されているバインディング名に対応するプロパティを持つように構成されます。
/registerアクションは、
ToyStoreViewControllerプロジェクト内の
RegisterActionクラスに対応付けされています。 このアクション・クラスはADFの DataForwardActionを拡張しており、そのリクエスト処理のライフサイクルにおける「モデルの更新」フェーズで、フォームBeanのデータを適切なバインディング・オブジェクトの値として適用します。 次に、そのバインディング・オブジェクトは、 Accountsビュー・オブジェクトのデフォルト行セット内のターゲットの行にデータを移入する処理を行います。
図46はこの処理の流れを表しています。
| 注意 |
|
具体的にいうと、ブラウザからデータベースへ、変更データを送信すると、次の手順が実施されます。
RequestProcessorは、フォームBeanのプロパティに、送信されたフォーム・プロパティの値を移入し、 RegisterActionへ制御を転送します。
DataForwardAction のライフサイクルの処理機能を継承している RegisterActionは、バインディング・コンテナを検索し、
BindingContainerActionFormフォームBeanの対応する値を使用して、バインディング・コンテナ内のモデル・バインディングを更新します。
Accountsビュー・オブジェクトに基づくイテレータ・バインディング内のターゲットの行にバインドされています。そのため、バインディングに対して値を設定することによって、ベースとなるビュー行の属性にこれらの値が設定されることになります。
event_Saveという名前であるため、 RegisterActionは、
onSave()イベント処理メソッドを呼び出します。 この動作は、ADFの DataForwardAction が持つイベント処理機能を継承した結果です。この
onSave()メソッドでは invokeEventAction()ヘルパー・メソッドをコールし、バインディング・コンテナの
Saveという名前のアクションを呼び出しています。 このアクション・バインディングは、 ToyStoreServiceデータ・コントロールの組込みの
Commit操作にバインドされているため、トランザクションがコミットされます。
ビュー・オブジェクトはベースとなるエンティティ・オブジェクトと関連付けられているため、ビュー・オブジェクト行にデータが移入されると、フレームワークは属性の設定作業をエンティティ・オブジェクトに委譲します。 これらのエンティティ・オブジェクトには、ユーザーが入力した値の評価を行うビジネス・ルールが定義されています。 いずれかのビジネス・ルールに違反している場合は、フレームワークが、それらの例外を一つにまとめてラッピングして
DataActionContextに保存します。その後、DataActionのライフサイクル・メソッドである
reportErrors()がコールされたときに、Strutsの
ActionErrors に変換され、ユーザーにレポートされます。
| 注意 |
この後にある「コントローラ層に対するStrutsのフレームワークのカスタマイズ」の項では、ADFによってまとめられた例外をエラーメッセージに変換してユーザーに提示するカスタマイズ方法について説明しています。 |
| 注意 |
コードの内容を確認したい場合のために、ADF Strutsアクションのソースは、 |
アプリケーションで、複数のWebページにデータを入力して処理を完了するエンドユーザーのタスクをサポートする必要がある場合は、ページ間に保留されているデータを保持するための仕組みを検討する必要があります。 たとえば、従業員ファイルで経費をレポートするWebアプリケーションについて考えてみます。 このタスクでは、多くの経費明細を入力することが必要で、このタスクを完成させるには複数のページに明細を入力する必要があるでしょう。 ユーザーがこれらの詳細を入力している間に、徐々に増えていく保留中の経費レポートのデータは、どこに保存されているのでしょうか?
現在、最も一般的なアプローチは、保留データを次の場所へ保存しておくことです。
これらのすべてのアプローチには、デメリットがあります。ただし、
ADF Business Componentsフレームワークには、ビジネス・サービスに対する組込みのステート管理機能が用意されており、これらのすべての問題にすぐに対処することができます。 すべてのアプリケーション・モジュールのインスタンスは、保留中のステートをデータベースに一時保存でき、そのステートを後でデータベースから再有効化することができます。 保留中のステートは、汎用的なフレームワーク・コードによって1つのXMLドキュメントにシリアライズされます。このXMLドキュメントには、保留中の変更情報のみが含まれ、 PS_TXNという汎用データベース表に対して1回のラウンドトリップで保存されます。 このアプローチには、次のメリットがあります。
データベースによるセッション管理のため、アプリケーション内でのデータ・アクセスおよび永続性管理の目的で使用されるものとは別のJDBC接続が発生します。 一般的には、ステート管理表はアプリケーション表とは別のデータベース・ユーザーに定義したいものです。ユーザーは、 jbo.server.internal_connection構成プロパティの値に、JDBCデータソースの名前、または
JDBC URLの接続文字列を設定することによって、これを制御できます。 ADF ToyStoreサンプル・アプリケーションでは、 jdbc/toystore_statemgmtDS JDBC データソースの名前を使用するように、 jbo.server.internal_connectionの値を設定しているため、アプリケーション表が存在する TOYSTORE データベース・ユーザーではなく、 TOYSTORE_STATEMGMTデータベース・ユーザーの中に
PS_TXN表が作成され、管理されています。
ADF Business Componentsのアプリケーション・モジュール・プールでは、ブラウザからのユーザー・リクエストに対するアプリケーション・モジュール・コンポーネントの使用方法に関して、3つのパターンをサポートしています。 3つのモードは次のとおりです。
ステートレス・モード
ビジネス・サービス・コンポーネントのステートを保持せずに、現行のリクエスト内で変更内容を強制的にコミット/ロールバックします。
ステートフル・モード
組込みのステート管理メカニズムを使用して、保留中のステートを複数のページ・リクエストに渡って保持します。
リザーブ・モード
後でステートレス・モードで解放されるまで、またはブラウザ・セッションがタイムアウトになるまで、アプリケーション・モジュール・コンポーネントのインスタンスを、現在のブラウザ・セッションで占有します。
| 注意 |
リザーブ・モードは、JDeveloper 3.2との互換性目的で提供されています。 ステートフル・モードおよび ステートレス・モードに比べるとスケーラビリティが劣るため、新規開発では推奨されていません。 |
「ステートフル・モード」という名前は、実は適切な用語ではないのかもしれません。 名前をつけた後で、Web開発者にとって「ステートフル」という言葉が、スケーラビリティに関して限界がありそうなことを連想させるということに気付いたからです。 ただし、この「ステートフル」は名前と違い、実際の機能は非常にすぐれています。 「ステートフル・モード」とは、「ステートレスなパフォーマンスでステート管理できるモード」といった意味合いで理解してください。
「ステートフル・モード」を使用すると、完全にステートレスなアプリケーションとほとんど同程度のパフォーマンスを備えたうえで、ステートフル・アプリケーションのプログラミングの簡潔さを実現するWebアプリケーションを作成することができますが、 このことを実現するために、ADF Business Componentsのアプリケーション・モジュール・プールは、「Stateless with Session Affinity:セッション・アフィニティな(セッションに対する配慮のある)ステートレス」として知られているアルゴリズムを実装します。 「ステートフル・モード」のページ・フローでの複数ページにわたるリクエストでは、プールは、現行セッションのページ・フロー内で、最も新しいリクエストで使用されたものと同じインスタンスを返そうとします。 このインスタンスがプール内でまだ有効であり、現在、他のセッションで使用されていない場合は、このシナリオによって最高のパフォーマンスが得られます。
ただし、他のブラウザのユーザー・セッションへこのインスタンスを分配しなければならい場合、またはリクエストがサーバー・ファーム内の別のサーバーへ割り振られた場合は、プールは、他の有効なビジネス・コンポーネント・インスタンスを取得し、データベースに保存されているユーザーの保留中のステートを使って「再有効化」します。 たとえば、ユーザーが新しく5行分のデータを作成し、そのうちの2行を更新して、1行を削除した場合は、これらの同じ変更情報が、ステートを再有効化した後でも提示されることになります。 また、ビュー・オブジェクトのイテレータ・ポジション情報も、ステートの情報として含まれています。このため、最後のリクエストで、イテレータが、コレクションの20行のうち3番目の行にある場合は、ステートを再有効化した後でも現在行はまだ行番号3に位置することになります。
まとめると、アプリケーションで処理しなければならない負荷に対してアプリケーション・モジュール・プールを適切にサイジングすると、 最高のパフォーマンスとスケーラビリティが得られるということになります。
ステートフル・モード は、ADF Business Componentsの処理のデフォルト・モードです。 ユーザーの買い物かごを商品を入れて、商品を購入するタスクは、論理的な1つのトランザクションの部分であるため、このデフォルトのモードは、ToyStoreサンプル・アプリケーションで適切に機能します。
1つの例を見るために、ADF ToyStoreサンプル・アプリケーションの買い物かごを使用してみます。 買い物かごは、
ToyStoreModelプロジェクトの
toystore.model.dataaccess.ShoppingCartという名前のビュー・オブジェクトとして実装されており、このビュー・オブジェクトには、一時的なすべての属性
およびSQL問合せが定義されています。 Strutsのアクション・クラス
YourCartActionは、
ToyStoreServiceビジネス・インタフェース・メソッドの
adjustQuantitiesInCartAsStringArrays()をコールして、買い物かごに対して商品を追加、変更、または削除します。 「ステートフル・モード」を使用しているため、アプリケーション・コードでは買い物かごで保留中のデータをどのように保存しておくかは意識する必要はありません。 ここでは、単に、ShoppingCartオブジェクトに対して、ビュー・オブジェクト・エディタで 「チューニング」パネルから、すべての一時属性(transient属性)をフレームワークのステート管理メカニズムによって非活性化(受動化)できるように設定します。 この機能は、チェックボックスをオンにするだけで使用できます。
これ以降のそれぞれのリクエストにおけるアクションでは、なにも意識することなく ShoppingCartビュー・オブジェクトにアクセスして、データ転送オブジェクトのコレクションを処理したり、ビュー・オブジェクトのデフォルト行セットの内容を調整することができます。
サンプル・アプリケーションのページ・フローにおける最後のアクション、
/thankyouでは、「ステートレス」なリリース・モードを使用して、これ以上は保留ステートを管理する必要がないことを提示します。 ステートレス・モードで解放するために、このアクションは、 DataForwardActionのメインの
handleLifecycle()メソッドをオーバーライドし、ヘルパー・メソッド releaseToyStoreServiceStateless()のコールを追加します。これは、 ToyStoreServiceDataForwardAction から継承されたメソッドです。
/* From ToyStoreServiceDataForwardAction */
protected void releaseToyStoreServiceStateless(DataActionContext ctx) {
releaseDataControlStateless(DATACONTROLNAME, ctx);
}
このメソッドは、ステートレス・モードで解放するために、データ・コントロールの名前を引数として、 ToyStoreDataForwardActionのヘルパー・メソッドを呼び出します。次のメソッド・コードをみてわかるように、名前からデータ・コントロールを検出し、 releaseState()メソッドを呼び出します。これにより、保留中のすべてのステート管理情報は、リクエストの最後に解放されます。
/* From ToyStoreDataForwardAction */
protected void releaseDataControlStateless(String dcName,
DataActionContext ctx) {
DC
DataControl dc = ctx.getBindingContext().findDataControl(dcName);
if (dc != null) {
dc.resetState();
}
}
アプリケーション・モジュールをステートレス・モードでプールに解放すると、フレームワークによって管理されていたデータベース内の保留中のステートは、すべて自動的にクリーンアップされます。