ADF ToyStore
- 3: サンプル・アプリケーションの分析 「コントローラ層」 -

前に戻る | 目次に戻る | 次へ進む

ここでは、次の内容を中心としてサンプル・アプリケーションの詳細について説明します。

注意

ここでは、 「サンプル・アプリケーションのインストールと設定」の項の指示に従っていること、 ADFToyStore.jwsのワークスペースがJDeveloper で開かれていること を前提にしています。


Apache Strutsフレームワークでのコントローラ層の実装

MVCアーキテクチャでは、ユーザー入力の処理、ビジネス・サービスとの連携処理、およびユーザーに表示するページの決定を行うすべてのプロセスがコントローラ層内にあります。 Webアプリケーションのコントローラ層に対するベスト・プラクティスなアプローチは、 Front Controllerデザインパターンを実装することです。 受け取る要求のタイプに基づいて、フロント・コントローラは要求の処理をタスク固有のコントローラ・ロジックへ委譲します。 通常、フロント・コントローラはサーブレットとして実装され、 web.xml内で構成されており、アプリケーションに関連するURLのすべての要求を処理します。 ユーザーは、このコントローラ・サーブレットを手動で実装することも可能ですが、 Apache Strutsのフレームワークには、このジョブを処理するための実装があらかじめ用意されています。

Strutsのアクションによって、モデルの相互作用とページ・フローを処理する

Strutsを使用し、次の内容を実行することによって、Webアプリケーションの「ページ・フロー」を記述します。

簡単なページ・フローの例

たとえば、簡単なWebストアのアプリケーションでは、ユーザーは、 /yourcartという名前のアクションを使用して買い物かごの中身を調べるかもしれません。 一般に、Strutsを使用する場合、ブラウザから *.doという接尾辞と一致するURLをリクエストしたときにアクションが実行されるため、 /yourcartアクションのURLは、次のようになります。

http://yourcompany.com/ADFToyStore/yourcart.do

買い物かごを調べる一方で、ユーザーは、/reviewcheckoutアクションのリンクをクリックして、購入した内容を確認し、支払の処理を開始することもできます。

これらのアクション、およびアクションに関連付けられているリンクをビジュアルに表すと、図34のようになります。 /yourcartおよび /reviewcheckoutのアクションはギア型のアイコンとして示されますが、ビュー層を表すJSPページは、ページ型のアイコンとして示されています。 Strutsの用語では「フォワード」と呼ばれるアクション間のリンクは、ここではアクションからターゲットのページまたはアクションへの実線として示されています。

Strutsのアクションとフォワード
図34: Strutsのアクションとフォワード

点線は、Webページ内に、リンクまたは矢印の先のアクションをターゲットとするHTMLフォームがあることを表します。 これはほんの一例ですが、より複雑なアプリケーションに対するStrutsのアクションとページに対して、ビジュアル・ダイアグラムが、わかりやすい「ページ・フロー」を示してくれることは想像できるでしょう。このページ・フローでは、ユーザーが遷移できるページ、およびどのようにしてページ間を遷移するかが示されています。

ご想像のように、それぞれのアクションに関連付けられているコントローラ層の動作は、関連付けられているJavaクラス内に記述されます。 これらのアクション・クラスは、各要求に応じて、Web層での処理を調整します。 図35は、ユーザーが、Strutsアクションを表すリンクをクリックするたびに発生する一般的な手順を表しています。

  1. コントローラがページ・リクエストを受け取り、それを適切なアクション・クラスへルーティングします。
  2. アクションは、ビジネス・デリゲートを介してビジネス・サービスのインスタンスを取得し、そこでメソッドを呼び出します。
  3. ビジネス・デリゲート実装コードによって、メソッドのコールがビジネス・サービスへ委譲されます。
  4. ビジネス・サービスは、1つ以上の問合せコンポーネントを使用して、SQL問合せからビジネス・データを取得し、結果を「モデル・データ・マップ」内にデータ転送オブジェクトのコレクションとして公開します。
  5. ビジネス・デリゲートは、「モデル・データ・マップ」をビュー層へ公開します。
  6. コントローラは適切なビューを選択し、レンダリングするために要求をビュー層へフォワードします。
  7. ビュー層は、「モデル・データ・マップ」からデータ転送オブジェクトのコレクションを検出し、そのコレクションを反復処理してデータをレンダリングします。
StrutsとJSPにおける一般的なMVCの処理手順
図35: StrutsとJSPにおける一般的なMVCの処理手順

注意

次の 「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つのアイコンで表しています。

ポストバック・パターンを実装したページとアクション
図36: ポストバック・パターンを実装したページとアクション

ご想像どおり、「DataPage」のアプローチを使用した方が、大きいサイズのアプリケーションの場合に、ページ・フロー図の把握がより簡単になります。 後で確認しますが、ADF ToyStoreのコントローラ層は、このDataPageアプローチを使用して実装されています。

注意

DataPageは設計時のための抽象概念で、StrutsベースのMVCアーキテクチャにおいて、ポストバック・パターンに沿ったページの表現を簡潔するためのものに過ぎません。 DataPageの、実行時における実体は、単なる標準のStrutsアクションと、制御がフォワードされるページであり、Strutsの DispatchActionおよび ForwardActionの機能を合わせたものと同等になります。


Strutsの構成ファイル

Strutsは、他の構成情報とともに、アクション名とアクション・ハンドラ・クラスとのマッピング情報を、 struts-config.xmlファイルに保持しています。 図37ToyStoreViewControllerプロジェクトに示されているように、この構成ファイルは、ユーザーの Webコンテンツ を構成する他の要素とともに、アプリケーションの標準の WEB-INFディレクトリに格納されています。

Strutsの構成ファイルの場所
図37: Strutsの構成ファイルの場所

struts-config.xmlファイルをダブルクリックすると、デフォルトで、ビジュアルなページ・フロー図が表示されます。 ADF ToyStoreサンプル・アプリケーションのページ・フローを調べて、これらの図の1つ1つがどのように表示されるのかを検討します。

ADF ToyStoreのStrutsページ・フロー

図38は、ADF ToyStoreサンプル・アプリケーションのコントローラ層に対するページ・フロー図を表しています。 それぞれのDataPageは、JSPページとStrutsアクション(表示するモデル・データを準備し、ポストバック・イベントを処理するためのアクション)の組合せによって実装されています。 これらのページ/アクションのペアは1つのアイコンとして表示されるため、ページ・フロー図は簡潔でわかりやすくなります。

ADF ToyStoreサンプル・アプリケーションのフロー
図38: ADF ToyStoreサンプル・アプリケーションのフロー

エディタの 「ソース」タブをクリックすると、ADF ToyStoreサンプル・アプリケーションに対するStrutsのアクション・マッピング のXMLソース・コードが示されます。 /yourcartを検索すると、 例6のような XMLタグを見つけることができます。 「検索」メイン・メニューで、 「検索...」または 「次をインクリメンタル検索」( Ctrl+ E)オプションのいずれかを使用して、場所を特定できます。

例6: struts-config.xml構成ファイルにおけるアクション・マッピング定義
<struts-config>
     :
  <action-mappings>
       :
     <!-- Show your shopping cart -->
    <action path="/yourcart"
            className="oracle.adf.controller.struts.actions.DataActionMapping"
            type="toystore.controller.strutsactions.YourCartAction"
            name="DataForm"
            parameter="/WEB-INF/jsp/yourcart.jsp"
            unknown="false">
      <set-property property="modelReference" value="WEB_INF_jsp_yourcartUIModel"/>
      <forward name="reviewcheckout" path="/reviewcheckout.do"/>
    </action>
       :
  </action-mappings>
     :
</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のイベントを処理するためのコントローラ・ロジックが含まれています。これらのイベントは、ユーザーがページで適切なボタンをクリックすると発生し、買い物かごの数量を調整するために、 ToyStoreServiceadjustQuantitiesInCartAsStringArrays()を呼び出します。 このアクションには、ビジネス・サービスから買い物かごの合計を取得してページに表示するためのコードも含まれています。

YourCartActionクラスは、 FwkExtensionsプロジェクトの ToyStoreDataForwardActionクラスを拡張したものです。 このフレームワークの拡張クラスは、ADFの DataForwardAction からの多数の組込み動作を継承し、加えて、ToyStoreサンプル・アプリケーションの多くのアクションで有用なヘルパー・メソッドがいくつか追加されています。
アクション定義タグの中に登場する modelReferenceプロパティの値は、データ・バインディング機能のための定義情報を指しています。

注意

DataForwardActionの機能や、ここで紹介したアクションへの追加プロパティの効果、および、その他のサンプル・アプリケーション内のフレームワーク拡張クラスについては、次の 「ADF/Strutsの統合について」の項で詳しく説明します。


表2は、それぞれのアクションが実行する内容について説明しています。

表2: ADF ToyStoreサンプル・アプリケーションにおける主なStrutsアクションのマッピング
アクションのパス名 データページ
/home ToyStoreのトップページ
/showcategory あるカテゴリの製品を表示する「Category」ページ
/showproduct 製品項目を表示する「Product」ページ
/showproductdetails 製品の詳細ページ
/yourcart ショッピング・カート・ページ
/search 検索ページ
/signin サインイン・ページ
/register 新規ユーザー登録ページ
/editaccount ユーザー情報編集ページ
/reviewcheckout 処理の確認ページ
/confirmshippinginfo 出荷情報の確認ページ
/thankyou 「Thank You」ページ:注文番号が表示されます
/revieworder 注文の確認ページ
/revieworderxml 注文の確認情報をXMLとして取得

その他のStrust設計時サポート

struts-config.xmlファイルがアクティブな場合、JDeveloperの構造ウィンドウとプロパティ・インスペクタによって設計時の追加サポートが提供されます。 構造ウィンドウには、構成ファイルの内部構造を表すツリーが表示されます。 ツリーの要素をクリックすると、プロパティ・インスペクタでそのプロパティを参照または編集することができます。 図39では、ADF ToyStoreサンプル・アプリケーションのすべてのStrutsアクションが、ツリーの 「Action Mappings」フォルダに表示されており、プロパティ・インスペクタには、選択された /yourcartのアクションのプロパティが表示されていることがわかります。

Strutsの構造ウィンドウとプロパティ・インスペクタのサポート
図39: Strutsの構造ウィンドウとプロパティ・インスペクタのサポート

Form Beans」や 「Action Mappings」など、適切なノードを選択し、マウスを右クリックしてメニューで 「New」を選択すると、その種類の新しいエントリを作成できます。 次にプロパティ・インスペクタを使用して、様々なプロパティを構成することができます。 また、マウスを右クリックしてメニューで 「編集」をクリックすると、 図40のようなStruts構成エディタを介して、ファイルの任意の項目を編集することもできます。 また、構造ウィンドウでアクションを選択し、マウスを右クリックしてメニューで 「コードに移動」を選択すると、該当するタグ・コードにジャンプできます。

Oracle JDeveloperのStruts構成エディタ
図40: JDeveloperのStruts構成エディタ

ここでわかるように、Strutsの構成情報を操作するために、Struts ページフロー図、構造ウィンドウおよびプロパティ・インスペクタの組合せを使用して、処理に必要なすべてのことを実現することができます。 逆に、XMLソースを編集したい場合には簡単に実施可能で、ユーザーが手動で行った編集内容とページ・フロー図を自動的に同期化します。 この選択は自由です。

Struts Form Beansを使用してHTMLフォームの入力を処理する

コントローラ層のアクション・クラスの管理に関連する補足機能関として、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クラスに対応していることに注意してください。

例7: StrutsのフォームBeanを使用するアクション・マッピング
<struts-config>
  <form-beans>
    <form-bean name="loginform"
               type="toystore.controller.strutsformbeans.LoginForm"/>
      :
  </form-beans>
  <action-mappings>
       :
    <action path="/signin"
            className="oracle.adf.controller.struts.actions.DataActionMapping"
            type="toystore.controller.strutsactions.SignInAction"
            name="loginform"
            parameter="/WEB-INF/jsp/signin.jsp"
            input="/WEB-INF/jsp/signin.jsp"
            scope="request">
      <set-property property="modelReference" value="none"/>
    </action>
       :
  </action-mappings>
</struts-config>

これらの追加属性セットによって、Strutsのコントローラは、実行時にユーザーがフォームを送信したとき、サインイン・ページのHTMLフォームのusernamepasswordの値を LoginForm Beanへ移入します。 これらの値は、 SignInActionクラスの onVerifySignin()イベントハンドラ・メソッドによってアクセスされます。このメソッドは、ユーザーがフォームを送信したときに、そのポストバック処理の中でコールされます。

例8は、フォームBeanの onVerifySignin()メソッドを表しています。 このメソッドは、ユーザー名とパスワードのプロパティの両方が空白になっていないかを確認し、次のメソッドをコールします。

public boolean validSignon(String username, String password)

上記メソッドは、 ToyStoreServiceビジネス・サービス・インタフェースのメソッドで、ユーザー名とパスワードの組合せが正しいWebストア・ユーザーを表しているかどうかを検証するものです。 検証チェックに失敗した場合は、このメソッドによって、Strutsの ActionErrorオブジェクトが ActionErrorsのコレクションに追加され、ビュー層で適切なエラー・メッセージをユーザーに表示できるようになります。 エラーを示す文字列キー(定数 INVALIDLOGINなど)は、ユーザーが認識しやすい適切なメッセージに変換されますが、それについては、「多言語アプリケーションを作成するためのStrutsとADFの機能」の項で説明しています。

例8: イベント・ハンドラ・メソッドにおけるユーザー名/パスワードの検証
// Method in the SignInAction Struts Action class
  public void onVerifySignin(DataActionContext ctx) {
    HttpServletRequest request = ctx.getHttpServletRequest();
    String target = request.getParameter("target");
    if (isNullOrEmpty(target)) {
      target = "welcome";
    }
    LoginForm loginForm = (LoginForm) ctx.getActionForm();
    final String username = loginForm.getUsername();
    final String password = loginForm.getPassword();
    if (isNullOrEmpty(username)) {
      ctx.getActionErrors().add("username", new ActionError(BLANKUSERNAME));
    }
    else if (isNullOrEmpty(password)) {
      ctx.getActionErrors().add("password", new ActionError(BLANKPASSWORD));
    }
    else {
      ToyStoreService ts = getToyStoreService(ctx);
      if (ts.validSignon(username, password)) {
        AppUserInfo.signIn(request, loginForm.getUsername());
        ctx.setActionForward(target);
        return;
      }
      else {
        ctx.getActionErrors().add(ActionErrors.GLOBAL_ERROR,
          new ActionError(INVALIDLOGIN));
      }
    }
    saveErrors(ctx);
  }

前述のログイン・フォームの送信に対して、検証に失敗すると、再入力のためにsigninページがユーザーに提示されます。 検証で成功すると、前述のコードによってヘルパー・メソッドがコールされ、対象ユーザーがサインインしたことを示すフラグが設定され、適切なページが返されます。 また、このアクションでは、 targetパラメータを設定していますが、これは、このサンプル・アプリケーション内の他の任意のアクションで、次の処理の前にユーザーのログインを要求したい場合に使用します。このような場合、 targetパラメータで適切な「次の」ページを指定した上で、 signinページにフォワードすると、ログイン認証の後、指定した「次の」ページに転送されるようになります。

ADF/Strutsの統合について

これまで見てきたように、Strutsは、Webアプリケーションのコントローラ層を実装するための便利なフレームワークです。 「JSPページとJSTLによるビュー層の構築」項で詳しく説明しますが、Strutsには、ビュー層の構築を簡単にするための、いくつかのJSPタグ・ライブラリも用意されています。 ただしStrutsでは、実際には、アプリケーションの開発においておそらく最も重要で時間がかかる作業である、モデル層の実装をサポートするための組込みの機能は提供しません。 モデル層には、ユーザーのビジネス・ロジック、およびデータ・アクセスのすべての機能が格納されています。 J2EE開発者が必要とする、強力なMVCアプリケーション・インフラストラクチャを完成させるためには、Strutsとのすぐれた統合を実現するモデル層を構築するための有効なアプローチが必要です。 この項では、Oracle ADFフレームワークが、この試みにおいてStrutsの最適なパートナーになっている状況について説明します。

ADFは、モデル層の開発を促進するためにビジネス・コンポーネントの仕組みを提供している以外にも、Strutsのコントローラおよびビュー層の機能と調和させるための特別な機能を提供しています。 たとえば、次の機能があります。

もちろん、JDeveloper では、これらのすべての機能をアプリケーションに組み込むことを容易にする、生産性の高い開発環境を提供します。

ADFデータ・バインディングとコントローラのコンセプトの概要

注意

Oracle ADFによるデータ・バインディング』というドキュメントでは、ADFのデータ・バインディングとStrutsに関するすべての機能について、簡単な3つのADF/Strutsアプリケーションの実装を交えて説明しています。 ここでは、この情報についても取り上げますが、これらの機能をどのように使用してADF ToyStoreサンプル・アプリケーションを実装するかを中心に説明します。
この先の内容を理解するためにも、 『Oracle ADFによるデータ・バインディング Part1』および 『Oracle ADFによるデータ・バインディング Part2』を先に読むことをお勧めします。


Oracle ADFには、バックエンド・ビジネス・サービスに対する「データ・コントロール」という抽象化層、およびフロントエンドのユーザー・インタフェース・コントロールを、データ・コントロールで提供されるデータと宣言的に連結するバインディング層が含まれています。 JSR 227に基づいたこれらの要素は、J2EEアプリケーションに対して首尾一貫したプラッガブルなモデル層を実現するもので、J2EEの標準になりつつあります。

図41は、ADF全体像の中で、モデル、ビュー、コントローラおよびビジネス・サービス・アーキテクチャとADFデータ・コントロール/ADFバインディングがどのように組み込まれているかを示しています。

J2EE アプリケーションのためのOracle ADFアーキテクチャ
図41: J2EE アプリケーションのためのOracle ADFアーキテクチャ

ADFデータ・バインディング・コンセプトの概要

Oracle ADFの主なデータ・バインディング概念は次のとおりです。

JDeveloper は、設計時に様々な方法でこれらのデータ・バインディングの概念をサポートしています。 一度ビジネス・サービスを作成すると、それをデータ・コントロールとして公開し、データ・コントロール・パレットに表示することができます。 図42は、 ToyStoreServiceImplアプリケーション・モジュール・コンポーネントに基づいた ToyStoreServiceデータ・コントロールが、データ・コントロール・パレットでどのように見えるかを表しています。 ここでは、モデル・データ・マップのすべてのデータ・コレクション、および 「Operations」フォルダのToyStoreServiceサービス・インタフェースのカスタム・メソッドを参照することができます。

データ・コントロール・パレット上のToyStoreServiceデータ・コントロール
図42: データ・コントロール・パレット上のToyStoreServiceデータ・コントロール

注意

ADFアプリケーション・モジュールとして構築されたサービスは、JDeveloperによってデータ・コントロールとして自動的に公開されます。 デフォルトでは、 YourModule というアプリケーション・モジュールに基づくデータ・コントロールは、 YourModuleDataControlという名前になります。 この名前を変更するには、 DATA_CONTROL_NAMEというアプリケーション・モジュールのカスタム・プロパティを追加します。このプロパティの値が、デフォルトの名前のかわりに使用するデータ・コントロール名になります。 サンプル・アプリケーションでは、 ToyStoreServiceはこのプロパティの値を ToyStoreServiceに設定して、 ToyStoreServiceDataControlという長い名前にならないようにしています。

他のタイプ(シンプルなJavaBeanや、EJB、Webサービスなど)のビジネス・サービスでは、追加で 「データ・コントロールの作成...」という手順を実行してデータ・コントロール化します、また、プロパティ・インスペクタでデータ・コントロールのIDプロパティを編集することによって、名前を変更することができます。


データ・バインドされたページを(データ・コントロール・パレットからドラッグ&ドロップで追加する、または明示的に作成することによって)作成すると、アプリケーションのバインディング・コンテキスト定義情報が保持されます。 この情報には、アプリケーションが使用しているデータ・コントロール、作成した様々なバインディング・コンテナ、およびそれらのコンテナに含まれているバインディング・オブジェクトが記述されています。 JDeveloperは、バインディング・コンテキスト定義情報を DataBindings.cpxファイルに格納します。 ナビゲータでこのファイルをクリックすると、 図43に示すように、構造ウィンドウにアプリケーションで使用中のデータ・コントロール、およびアプリケーションの様々なページを構成する各種のバインディング・コンテナ(「UIモデル」とも呼ばれる)が表示されます。 構造ウィンドウで ToyStoreServiceなどをクリックすると、プロパティ・インスペクタにそのプロパティが表示されます。

ナビゲータ、構造ウィンドウ、およびプロパティ・インスペクタのバインディング・コンテキスト情報
図43: ナビゲータ、構造ウィンドウ、およびプロパティ・インスペクタのバインディング・コンテキスト情報

あるJSPページを参照しているときに、 図44に示すように、構造ウィンドウの「UIモデル」タブをクリックして、そのページに含まれているバインディング・オブジェクトの詳細を表示することができます。 特定のバインディング・オブジェクトをクリックすると、プロパティ・インスペクタでそのプロパティを表示し、編集できます。また、マウスを右クリックしてメニューで 「編集...」を選択すると、専用のエディタを使用して編集することも可能です。

検索ページのバインディング・コンテナを表示する「UIモデル」タブ
図44: 検索ページのバインディング・コンテナを表示する「UIモデル」タブ

Strutsに対するADFのコントローラ層機能

Apache StrutsアプリケーションでADFバインディング層の利用を簡単にするために、Oracle ADFには、Strutsのためのコントローラ層コンポーネントが用意されており、StrutsとADFバインディング層をシームレスに統合することができます。 これらのコンポーネントには、次のものが含まれています。

ADFとStrutsの連携利用を支援するものとして、Struts ページフロー図と、ADFの「DataPage」の概念があります。 DataPageは、ページとStrutsアクションを、ポストバック・パターンによって連携するように構成したい場合に、その作業を簡易化します。 JDeveloper のStrutsページフロー図では、Webページと DataForwardActionのセットを1つのアイコンで表すことができます。 DataForwardActionは、ADF DataActionがもつ機能(ライフサイクル機能とDispatchActionに類似したイベント処理)と、Strutsの ForwardActionが持つ機能(暗黙的なページ転送機能とイベント処理)を組み合わせたものです。

DataForwardActionによる宣言的機能の概要

DataForwardActionは、 (DataActionから)イベント処理機能を継承しています。この機能は、Strutsの DispatchActionに似ています。 HTMLフォームまたはリンクで、 eventという名前のリクエスト・パラメータを含めてDataActionに送信すると、その値は処理の対象となるイベントの名前として取り扱われます。たとえば、フォーム・フィールドを使用してリクエスト・パラメータとして event=YourNameを送信すると、 YourNameというイベントが呼び出されることになります。または、HTMLのボタンで、name属性としてevent_YourNameという値を使う方法でも、同じ YourNameというイベントが発行されます。

ADF DataActionは、イベントの処理に関して、主に3つの機能サポートしています。
YourEventという名前のイベントが起動された場合、

  1. リクエストを処理するDataActionクラスに public void onYourEvent (DataActionContext ctx)メソッドがある場合は、イベント処理時に、このメソッドが呼び出されます。これにより、カスタム・コードでイベントを処理できます。

  2. YourEventという名前のアクション・バインディングがある場合は、このアクション・バインディングが呼び出されます。
    1のようなカスタム・イベント・コードと組み合わせて使用したい場合は、イベント処理コードから、次のようなコードを使用して、デフォルト・アクションを明示的に呼び出します。

    if (ctx.getEventActionBinding() != null) {
      PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
      p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
    }
  3. YourEventという名前のStrutsフォワードがある場合は、それを使用して次の制御を転送します。
    1のようなカスタム・イベント・コードと組み合わせて使用する場合に、イベント処理コード内でctx.setActionForward()を呼び出すと、そのコードで設定した転送先が優先されます。

これらの基本的な内容をふまえたうえで、次にADFとStrutsの統合について、および、その統合機能をADF ToyStoreサンプル・アプリケーションでどのように活用しているかについて説明します。

StrutsとADFを使用したWebページ・リクエストのライフサイクル

図45は、Webページ・リクエストのライフサイクルを表すために、その「遷移部分」の全貌をシーケンス図でまとめたものです。

StrutsとADFを使用したWebページ・リクエストのライフサイクル
図45: StrutsとADFを使用したWebページ・リクエストのライフサイクル
  1. http://yourserver/yourapp/some.doに対してWebリクエストされます。

  2. ADFBindingFilterは、HTTPセッション内でADFバインディング・コンテキストを検索し、見つからない場合は、最初に初期化します。
    バインディング・コンテキストを初期化する際に、 ADFBindingFilterは次の処理を行います。

    • CpxFileNameという名前のサーブレット・コンテキスト初期化パラメータを調べて、その値に拡張子として .cpxを追加した名前で、バインディング・コンテキスト定義ファイルの名前を特定します。 デフォルトでは、パラメータの値は DataBindingsになっているため、ファイル名は DataBindings.cpxになります。
    • バインディング・コンテキスト定義ファイルを読み込んで、データ・コントロール定義およびバインディング・コンテナの名前を検出します。
    • 各データ・コントロールのインスタンス、および各バインディング・コンテナに対する参照を作成します。 このように、バインディング・コンテナに対して作成されるのは、この時点では参照だけで、各バインディング・コンテナの中身は、それらが最初に(どこかのページから)使用されるタイミングでロードされます。
       
  3. ADFBindingFilterが、バインディング・コンテキストのそれぞれのデータ・コントロールで beginRequest()メソッドを呼び出します。 これによって、すべてのデータ・コントロールに対して、リクエストの開始点にきたことが通知されます。この通知により、各データ・コントロールで必要なセットアップ作業が実施されます。

  4. ADFアプリケーション・モジュールをベースとしているデータ・コントロールは、この beginRequest通知によって、アプリケーション・モジュール・プールからインスタンスを取得します。

  5. Strutsの RequestProcessorは、 struts-config.xmlに構成されている /someアクションに対応したアクション・クラスに対して、制御を転送します。

    注意

    正確には、最初はStrutsのActionServletRequestProcessorへ制御を転送しますが、 実質、RequestProessorがその後のすべての処理を実施するため、図の中には ActionServletを省略しています。

  6. Strutsのアクション・クラスがADFの DataActionクラスである、またはそれを拡張している場合は、次の内容を含む一連の処理手順(まとめてリクエスト処理の「ライフサイクル」と呼ばれる)が実行されます。

    • バインディング・コンテナを検索します。
      または、使用したのが初めての場合は、それを初期化します。 初期化の際に、 *UIModel.xmlメタデータ・ファイルに対応するバインディング・コンテナに対して、バインディング・オブジェクトが作成されます。
    • イテレータ・バインディングがまだ実行されていない場合は、 prepareModel()フェーズでそれを実行します。
    • 送信されてきたデータがある場合は、 processUpdateModel()フェーズで、対応するバインディング・オブジェクトに対してそのデータで更新処理をします。
    • invokeCustomMethod()フェーズでコントローラ層のカスタム・ロジックが(あれば)実行されます。
    • findForward()フェーズで、次に表示するページを決定します。

    ライフサイクルの各フェーズは、開発者が上書きすることができます。また、それぞれのメソッドは、 DataActionContextオブジェクトに渡されますが、このオブジェクトでは、バインディング・コンテキスト、バインディング・コンテナ、およびStrutsに関するすべての要素(サーブレット・リクエストやレスポンス、アクション・マッピング、アクション・フォームなど)にアクセスできます。 アクションは、Strutsの ActionForwardを返し、これによって、次にナビゲートするアクションまたはページを表します。

    注意

    Strutsのアクション・クラスがDataActionを拡張していない場合は、アクション・クラスはバインディング・コンテナを手動で検索(および必要な場合は初期化)し、バインディングとの処理を自身で管理する必要があります。 『Oracle ADFによるデータ・バインディング』のホワイト・ペーパーには、この処理を行うためのコード例が記載されています。


  7. Strutsの RequestProcessorは、次に制御を渡すようアクションが指定したアクションまたはページに転送します。

  8. 転送先がJSPページであるとします。この場合、ページは、コントロール・バインディングからデータ転送オブジェクトのコレクションへアクセスして反復処理を行い、書式化されたWebページへと書き出されます。

  9. Strutsの RequestProcessorは、 ActionServletへ制御を返します。さらに、サーブレット・コンテナへ制御を戻します。

  10. ADFBindingFilterは、バインディング・コンテキストのそれぞれのデータ・コントロールで endRequest()メソッドを呼び出します。これによって、すべてのデータ・コントロールに対して、リクエストの終了点にきたことが通知されます。この通知により、各データ・コントロールで必要なリソースのクリーンアップ作業が実施されます。

  11. ADFアプリケーション・モジュールをベースとしているデータ・コントロールは、この endRequest通知によって、アプリケーション・モジュール・プールへインスタンスを解放します。

  12. ユーザーは、結果ページをブラウザで参照できます。

このシーケンス図をみると、バインディング・コンテナが、コントローラ層によるモデル・データの操作の際も、ビュー層によるモデル・データの反復処理とレンダリングの際も、使用できるということがわかります。

StrutsとADFを使用できるようにJ2EE Webアプリケーションを構成する

JDeveloperを使用してADFベースのStrutsアプリケーションを作成する場合には、設計時のサポート機能によってWebアプリケーションが自動的に構成されます。 ただし、ツールが設定する内容を理解しておくと、個々の設定がどのように統合されるのかを理解するうえで有効です。 J2EE Webアプリケーションでは、いくつかの構成手順において標準の web.xmlファイルを使用します 。 この項では、設定の手順でいくつか興味深い点を中心に説明します。

StrutsのサーブレットとADFのバインディング・フィルタ

まず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という値を使用していることがわかります。 これは、 ADFBindingFilterDataBindings.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の構成情報の中に明確に切り出されており、コード内の変更を必要としません。 もちろん、設計時には、これらの複数の構成の初期設定の自動化や、構成パラメータ編集用の専用ダイアログといった支援機能が提供されます。

ADFのDataActionとBindingContainerActionFormの概要

ユーザーがアプリケーション・データを入力または編集できるようなHTMLフォームを作成する場合には、前述のように、Strutsによって、FormBeanのインスタンスへフォーム・データが抽出され、ユーザーのアクションで簡単に処理できるようになります。 HTMLフォームとのやり取りに関して、アクションでの、比較的頻度の高いタスクとして、次の3つがあります。

  1. データ入力フォームを表示する場合に、FormBeanに適切な初期値を移入する
  2. データ編集フォームを表示する場合に、FormBeanに現行の行データを移入する
  3. ユーザーが送信した変更データを含むFormBeanを使って、モデル層を更新する

Oracle ADFには、連携するための重要な2つの要素が用意されており、これらのシナリオを簡単に実装することができます。

図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はこの処理の流れを表しています。

新しいアカウント情報をデータベースに登録する仕組み
図46: 新しいアカウント情報をデータベースに登録する仕組み

注意

Accountおよび Signonのエンティティはそれぞれモデル化されており、1対1のアソシエーションで関連付けされています。これは、Java Pet Storeサンプル・アプリケーションで、これらの2つのエンティティがそれぞれにモデル化されたスキーマを使用しており、その構成をそのまま使えるようにしたためです。


具体的にいうと、ブラウザからデータベースへ、変更データを送信すると、次の手順が実施されます。

  1. Strutsの RequestProcessorは、フォームBeanのプロパティに、送信されたフォーム・プロパティの値を移入し、 RegisterActionへ制御を転送します。
  2. ADFの DataForwardAction のライフサイクルの処理機能を継承している RegisterActionは、バインディング・コンテナを検索し、 BindingContainerActionFormフォームBeanの対応する値を使用して、バインディング・コンテナ内のモデル・バインディングを更新します。
  3. コントロール値バインディングは、 Accountsビュー・オブジェクトに基づくイテレータ・バインディング内のターゲットの行にバインドされています。そのため、バインディングに対して値を設定することによって、ベースとなるビュー行の属性にこれらの値が設定されることになります。
  4. ビュー行は、ベースとなるエンティティ・オブジェクトに、属性のsetterコールを委譲します。これがコールされると、エンティティ・オブジェクトに定義されているビジネス・ルールが評価されることになります。
  5. (Save Changes)」ボタンが event_Saveという名前であるため、 RegisterActionは、 onSave()イベント処理メソッドを呼び出します。 この動作は、ADFの DataForwardAction が持つイベント処理機能を継承した結果です。この onSave()メソッドでは invokeEventAction()ヘルパー・メソッドをコールし、バインディング・コンテナの Saveという名前のアクションを呼び出しています。 このアクション・バインディングは、 ToyStoreServiceデータ・コントロールの組込みの Commit操作にバインドされているため、トランザクションがコミットされます。
  6. トランザクションをコミットしたときに検証エラーがない場合は、エンティティ・オブジェクトは、ベースとなる表に変更を書き込みます。

ビュー・オブジェクトはベースとなるエンティティ・オブジェクトと関連付けられているため、ビュー・オブジェクト行にデータが移入されると、フレームワークは属性の設定作業をエンティティ・オブジェクトに委譲します。 これらのエンティティ・オブジェクトには、ユーザーが入力した値の評価を行うビジネス・ルールが定義されています。 いずれかのビジネス・ルールに違反している場合は、フレームワークが、それらの例外を一つにまとめてラッピングして DataActionContextに保存します。その後、DataActionのライフサイクル・メソッドである reportErrors()がコールされたときに、Strutsの ActionErrors に変換され、ユーザーにレポートされます。

注意

この後にある「コントローラ層に対するStrutsのフレームワークのカスタマイズ」の項では、ADFによってまとめられた例外をエラーメッセージに変換してユーザーに提示するカスタマイズ方法について説明しています。


注意

コードの内容を確認したい場合のために、ADF Strutsアクションのソースは、 adf-controllersrc.zipファイル内の ./adfc/srcディレクトリに用意されています。 Ctrl+ のショートカット・キーを使用して表示されるダイアログで、名前によってクラスを検索すると、JDeveloperは、このzipファイルからソース・コードを適切に抽出して表示します。



自動ステート管理を使用して複数ページの作業単位を作成する

アプリケーションで、複数のWebページにデータを入力して処理を完了するエンドユーザーのタスクをサポートする必要がある場合は、ページ間に保留されているデータを保持するための仕組みを検討する必要があります。 たとえば、従業員ファイルで経費をレポートするWebアプリケーションについて考えてみます。 このタスクでは、多くの経費明細を入力することが必要で、このタスクを完成させるには複数のページに明細を入力する必要があるでしょう。 ユーザーがこれらの詳細を入力している間に、徐々に増えていく保留中の経費レポートのデータは、どこに保存されているのでしょうか?

現在、最も一般的なアプローチは、保留データを次の場所へ保存しておくことです。

  1. HTTPセッション・オブジェクト
  2. HTMLフォームの非表示フィールド
  3. データベース表への一時的な格納

これらのすべてのアプローチには、デメリットがあります。ただし、

  1. いくつかのJ2EEアプリケーション・サーバーでは、HTTPセッションに格納されているデータに対して、サーバー・ファーム(アプリケーション・サーバーのクラスタリング構成における一単位)内での確実なアクセスができません。 クラスタリングおよびセッション・ステートのレプリケーションをサポートしている Oracle Application Serverのようなサーバーを使用しても、HTTPセッションに追加するオブジェクトが多くなったり、規模が大きくなったりすると、他のHTTPセッション・オブジェクトと相互に同期をとるために、サーバー間で生成されるトラフィックが増えます。
  2. HTMLフォームの非表示フィールドに保留データを格納するということは、非表示のフィールド名に対応するオブジェクトのフィールド構造を準備する必要が出てくるということです。また、データ内容がHTML内に記述されることとなり、結果としてダウンロードするページのサイズが増えます。
  3. 保留データは完全に入力完了な状態になっていない場合があるため、それをそのままベースとなるデータベース表に直接格納できないことがありえます(格納すると、データベースの制約違反が発生する場合があるためです)。 そこで、保留データは、一時格納用の表に保存し、いつでも再ロードできるようにしておく手法があります。格納する保留データのそれぞれの種類に対して個別に表セットを作成するか、または、保留データを汎用的に共用のスキーマで格納する方法がありますが、どちらにしても、これらの作業は簡単ではありません。

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();
    }
  }

アプリケーション・モジュールをステートレス・モードでプールに解放すると、フレームワークによって管理されていたデータベース内の保留中のステートは、すべて自動的にクリーンアップされます。


前に戻る | 目次に戻る | 次へ進む