Oracle JDeveloperのヒント
ADF Business Componentsの状態管理機能について
2004年11月
| 注意: | この記事は高度なトピックなため、ADF Business Components (ADF BC)の使用経験があり、基礎知識を持つ読者を対象としています。 |
内容
はじめにはじめに
状態管理はOracle Application Development Framework(ADF)の一連の機能で、使用中のアプリケーションでペンディングされている変更状態を一定期間にわたり提示するものです。ステートフルWebアプリケーションと呼ばれる大きなアプリケーション・グループのための、ごく一般的なタスクです。 ステートフルWebアプリケーションは、論理アプリケーション状態をいくつかのHTTPリクエストにまたがって保持します。この種の情報を保持しないWebアプリケーションは、ステートレス・アプリケーションと呼ばれます。データベース・トランザクションが関係するステートレス・アプリケーションでは、1つのリクエストの範囲内でトランザクションが、コミットまたはロールバックによって開始および終了します。これは、HTTPプロトコル自体がステートレスな性質を備えているためです。Webアプリケーションで複数のHTTPリクエストにまたがるトランザクションを必要とする場合、ペンディングされた変更をトランザクションの一部として保持し、関連するHTTPリクエスト間で一貫性を維持することは、アプリケーションの責任です。 本書では、「ペンディングされた変更」または「ペンディングされたセッション・ステート」とは、現在のトランザクションで作成、更新または削除されたが、ポスト操作またはコミット操作によってデータベースに永続的保存されていないADFエンティティ・オブジェクトを意味します。 ADFが提供するJ2EEデータ・アクセス・フレームワークADF Business Componentsにより、純粋なステートレス・アプリケーションとほぼ同等のスケーラビリティを備えたステートフル・アプリケーションが容易に作成できます。 非常に生産性の高いこの機能を最大限活用するためには、背後で行われている処理を理解することが重要です。
ADF BC中間層アプリケーションは、1つ以上のルート・アプリケーション・モジュール(AM)で構成されています。 それぞれのルートAMは、単一のデータベース・トランザクションに紐付いています。各AMは、ビュー・オブジェクト(VO)、ネストされたAM(常に親AMのトランザクションを共有する)の両方または一方を含む場合があります。VOの属性は、エンティティ・オブジェクト(EO)属性に基づく場合、SQL計算による式から移入される場合、またはJavaで計算され、データベースにはまったく相関しない場合があります。 慣例として、VOのすべての属性が一時的な場合は、VO自体も一時VOと呼ばれます。
HTTPクライアントと ADF BC中間層との相互作用の再確認
Oracle ADFは、MVC(Model View Controller) アーキテクチャを 実装しています。 このアーキテクチャのモデルは、クライアントをビジネス・サービスから分離する抽象レベルです。 クライアント(「ビュー」と呼ばれる)はコントローラを介してモデル・レイヤーと通信し、モデル・レイヤーはクライアントから中間層のビジネス・サービスへリクエストを中継します。 コントローラも、応答を中間層のビジネス・サービスからモデル・レイヤーへ中継して戻し、ビュー・レイヤーがクライアントに対して表示できるようにします。 このように、クライアント・レイヤーは、ビジネス・サービスでの実装のディテールには依存していません。 前述の説明を図1に図式的に示します。
HTTPクライアント(HttpSession)とモデル・レイヤーの相互作用を理解することが重要です。 oracle.adf.model.servlet パッケージの ADFBindingFilter は、Webベース・クライアントから送信されたすべてのリクエストに割り込むサーブレット・フィルタです。 最初のリクエストを受け取ると、ADFデータ・バインディングに必要なクラスがフィルタにより初期化されます。 このブートストラップ手順の実行中に、 BindingContext オブジェクトが作成されて、 HttpSession に関連付けられます。 BindingContext オブジェクトは HttpSession ごとに1つずつあります。
BindingContext には、1つ以上のデータ・コントロール・オブジェクトとバインディング・コンテナ・オブジェクトがあります。その名前が示すように、バインディング・コンテナにはバインディングがあります。これは、UIコントロールのディテールをビジネス・サービス・データから抽出するヘルパー・オブジェクトです。これらのバインディングは、提供されたデータやモデル・レイヤーと連携するデータ・コントロールに実装されたメソッドと相互に作用します。各データ・コントロールは、ADF BCの SessionCookie を1つ持ち、それによりビジネス・サービスの機能が実装されたAMインスタンスをチェックアウトおよびリリースします。すなわち SessionCookie は、AM状態識別子として使用されます。 HttpSession には、複数のデータ・コントロールへの参照を格納できますが、与えられたデータ・コントロール・インスタンスは常にある1つの HttpSession に結び付けられています。 すなわち、HttpSession とデータ・コントロールの関係は1対多となります。 要約すると、 BindingContext は HttpSessionに関係し、 BindingContext 内のデータ・コントロールは、タスクの実行が必要なとき、 SessionCookie を使用してAMインスタンスを取得およびリリースします。この SessionCookie は、 oracle.jbo.http.HttpSessionCookieImpl によって実装されます。
これで構造が明確になりました。次にその機能を簡単に説明します。 ADFBindingFilter は、HTTPリクエストを受信すると、そのリクエストの HttpSession に関連するすべてのデータ・コントロールに対して(BindingContext を介して)beginRequest 通知を発行します。次に、処理チェーンの次のフィルタまたはサーブレットにリクエストを渡します。 処理チェーンが完了すると、コントロールはADFBindingFilterに戻されます。次に ADFBindingFilter が、リクエストの HttpSession に関連したすべてのデータ・コントロールについて endRequest 通知を発行します。
ADF BCデータ・コントロールは、 SessionCookie を使用してそのリクエストに対するAMを取得し、beginRequest 通知に対応します。したがって、AMは、リクエストの処理が完了するまでデータ・コントロールに「結合」されています。 ADF BCデータ・コントロールは、 SessionCookie を使用してそのリクエスト用のAMをリリースすることで、endRequest通知に対応します。本書では、データ・コントロールが状態管理を継続しながら、AMリリースを可能にするテクノロジについてさらに詳しく説明します。図2に実行するステップを示しますが、これは、モデル・レイヤーの仕組みを完全に説明していません。詳細な説明はこのドキュメントの範疇を越えています。ここで必要なのは、このドキュメントでこれから発生するいくつかの論議の目的をおおまかに理解することです。

次のステップは、ADFが状態管理を使用して、複数のHTTPリクエストにわたるカレントの HttpSession でペンティングされている変更を保存する方法の概要です。 最も簡単なケースを説明します。
HttpSession に関連付けられたデータ・コントロールはAMプールから、参照されていないAMインスタンスを取得します。参照されていないAMインスタンスとは、他のユーザー・セッションで状態の管理を実行していないAMインスタンスです。データ・コントロールがAMインスタンスを求める第2のリクエストを発行すると、AMプールは最初のリクエストと同じAMインスタンスを供給します。これは、データ・コントロールが SessionCookie により識別されるためです。数多くのユーザーが同一のアプリケーションに同時にアクセスしている場合は、AMインスタンスを異なる HttpSession 間で共有する必要があります。 その場合、他のセッションで使用できるよう、AMプールでは次に示すような現在参照されているAMインスタンスをリサイクルするための処理を行います。
HttpSession 1 に関連付けられている)がAMインスタンス(インスタンス1)をチェックアウトしてアプリケーション・プールに戻します。アプリケーションの状態はメモリに保存されます。HttpSession 2 に関連付けられているデータ・コントロールが、AMインスタンスを要求します。このとき、プールには参照のないインスタンスがありません。 AMインスタンスは、データ・コントロール1が参照するペンディング・トランザクション・ステートを永続ストレージに保存することで非アクティブ化し、トランザクション・ステートをリセットして、別のデータ・コントロールが使用できるようにします。これで、インスタンス1は参照されなくなり、AMプールはインスタンス1をデータ・コントロール2に提供します。非アクティブ化、アクティブ化およびリサイクルのプロセスにより、各データ・コントロール用の専用AMインスタンスなしでも複数のリクエストにわたり、データ・コントロールが参照する状態を保存できます。 前述ではシナリオのブラウザ・ユーザーが、両方とも複数のHTTPリクエストにわたるアプリケーション・トランザクションを実行していますが、バックグランドでの非アクティブ化とアクティブ化はエンド・ユーザーには認識されません。 エンド・ユーザーの目には、まだコミットされていないペンディングの変更のみが見えます。 非アクティブ化の状態を永続化するオプションについては後述します。
| 注意: | ADFアーキテクチャでは、状態管理のライフサイクルに任意のデータ・コントロールが参加できますが、現在、ADF Business Componentsデータ・コントロールは、状態管理サポートの独自実装を提供しています。 |
データ・コントロールは、HTTPリクエストが処理され終了されたことを意味するendRequest通知を受信する、AMインスタンスをリリースしてチェックアウトし、AMプールに戻します(またはAMプールが使用されていない場合はガベージ・コレクションにリリースします)。 各HTTPリクエストの後、データ・コントロールは、次の3つのうちいずれかの正しいリリース・レベルでリリースする責任があります。
このモードは、このデータ・コントロールに関連付けられた状態で、現在のHTTPリクエストを超えて存続すべき状態ではないことを示します。 これは、状態管理に関連するオーバーヘッドがなく、最もパフォーマンスの高いレベルです。ただし、その用途は、状態管理を必要としないアプリケーション、またはその時点で保持の必要がない場合(例えば、ログアウト・ページからHTTPリクエストの処理後にAMをリリースする場合など)に制限されます。
これはデフォルトのレベルです。 このレベルは、AMの状態が妥当で、複数のHTTPリクエストにわたりこのデータ・コントロールの保持が必要なことを意味します。Managedレベルでは、次のリクエストでこのデータ・コントロールが物理的に同じAMインスタンスを受け取る保証はされませんが、同じ状態でのAMの提供が保証されます。したがって論理的には、毎回同一のAMインスタンスが提供されます。重要な点は、フレームワークは、その時点で利用可能であれば、同じデータ・コントロールに対する同一のAMを最善を尽くして提供することです。これは、パフォーマンスの向上のために実行されます。同一のAMインスタンスなら、前回のリクエストを処理した後に状態が保持しているのでアクティブ化の必要がないからです。ただし、データ・コントロールがすべてのリクエストに対して同一のAMインスタンスを受け取れる保証はありません。前のリクエスト時にデータ・コントロールを処理したAMがビジーまたは使用できない場合は、別のAMがこのデータ・コントロールの状態をアクティブ化します。 そのため、AM、VOまたはビュー行への参照を、複数のHTTPリクエストにわたりコントローラ・レイヤーのコードにキャッシュできません。
このレベルは、各データ・コントロールに関連付けられたHttpSessionからの最初のリクエスト中および後続のすべてのリクエストを通して、各データ・コントロールに専用のAMを割り当てることを保証します。 このレベルでは、データ・コントロールは、常に同じ物理インスタンスを受け取ります。これは、旧リリースとの互換のためのモードで、非常に稀で特殊なケースでのみ使用されるべきです。通常、このレベルの使用はお薦めしません。その理由は、このレベルではデータ・コントロールとアプリケーション・モジュールの関係は常に1対1となり、アプリケーションのスケーラビリティおよび信頼性が大幅に低下するからです。 信頼性が損なわれるのは、Unmanagedレベルとは異なり、AMが何らかの理由で消失した場合、データ・コントロールは、その代わりとなるAMをプールからまったく受け取れず、HttpSessionも同様に消失するからです。
| 注意: | 予約済モードが使用される1つのケースとしては、PL/SQLストアド・プロシージャを使用してDBトランザクション・ステートを作成する場合です(または、 postChanges メソッドを使用することでもDBトランザクション・ステートの作成が可能です。 詳細は、このドキュメント内の postChanges の項を参照してください)。 この場合、何らかの状態がDBトランザクションに格納されているため、非アクティブ化・アクティブ化サイクルによって、別のAMインスタンスを前のAMスタンスと同一状態にはできません。 したがって、データ・コントロールには同じAMの使用が必要です。ただし、これは特異なケースであり、通常、予約済レベルの使用はお薦めしません。 |
リリース・レベルは、現在のリクエストに関連するStrutアクション・コードで設定できます。 oracle.adf.controller.struts.actionsパッケージにある DataForwardActionを拡張するアクションの場合は、次のメソッドをオーバーライドします。
protected void handleLifecycle(DataActionContext actionContext) throws Exception
前述のとおり、デフォルトのリリース・レベルはManagedレベルなので、リクエストがリリース・レベルの指定をオーバーライドしなければ、Managedレベルが使用されます。 これが一般的なケースと考えられます。 ただし、AMのリリース・レベルがReservedレベルに変更されている場合は、その指定が明示的に変更されるまで後続のすべてのリクエストについては、Reservedレベルとなります。 同様に、Managedレベルを明示的に指定することも可能です。そのためには、次のメソッドをコールします。
setReleaseLeevel(int releaseLevel)
このメソッドは、 oracle.adf.model.bc4j.DCJboDataControl クラスおよび oracle.jbo.ApplicationModule インタフェースで定義されています。リリース・レベルをManagedレベルに設定するには、引数に ApplicationModule.RELEASE_LEVEL_MANAGED を使用します(getReleaseLevel() メソッドをオーバーライドすることでも実現可能です)。
Unmanagedレベルでアプリケーションを実行している場合は、リクエストの処理中に DCDataControlクラス(oracle.adf.model.bindingパッケージ内)の resetState() メソッドがコールされます。これによりAMはリリース後に状態を非アクティブ化しません(この後AMは、参照されることなくリセットされるためです。次回の使用時には、そのリリース・レベルは、明示的に変更しないかぎり、デフォルトでManagedレベルに設定されます)。Unmanagedレベルに設定には、DCJboDataControlクラス(oracle.adf.model.bc4jパッケージ内)または oracle.jbo.ApplicationModule インタフェースのsetReleaseLevel()メソッドに引数として ApplicationModule.RELEASE_LEVEL_RESERVED を渡します。
前述のとおり、全体的な状態管理メカニズムは、あるAMインスタンスの状態を別のAMインスタンスにレプリケートする手段としてアクティブ化と非アクティブ化に依存しており、この2つのインスタンスは、データ・コントロールの側からは区別されません。 これは、すべてのペンディングされている変更が中間層のAMインスタンスによって管理されている場合にのみ可能となります。それにより、ユーザーが行った、まだコミットされていないすべての変更は、AMインスタンスが非アクティブ化される際にスナップショットされることが保証されます。別の方法として、適切なINSERT、UPDATEおよびDELETE文を現在のデータベース・トランザクションにポストすることによって、コミットしないままデータベースに保存することもできます。ただし、このアプローチには、パフォーマンスと機能の2つの面で大きな問題点があります。
これらの問題点について詳しく説明する前に、重要な点を指摘します。デフォルトでは、AMはJDBCの接続プーリングではなくAMプーリングを使用する構成をとります。 つまり、AMはインスタンスが生成された時点でJDBC接続を受け取り、この接続をプールにおけるライフスパンを通じて保持します。 したがって、デフォルトではAM は必ず同じJDBC 接続が使用されます。 パフォーマンス上は、これが最高の選択であるため、この構成の維持をお薦めします。 なぜなら、使用するJDBC接続が維持されるので、使用したJDBC PreparedStatementを再利用できるからです。
以上の点に注意した上で、次にペンディングの変更がデータベースにポストされながらコミットされない場合のパフォーマンスおよび機能における問題点を説明します。
パフォーマンス上の問題点とは、AMのリリース・レベルをReservedレベルに設定する必要があり、アプリケーションのスケーラビリティが低下します。機能上の問題点とは、変更が早い段階でにデータベースにポストされるため、情報が完全に妥当となる前に、不完全な情報をデータベースがポストされる場合があることです。そのため、整合性違反が発生する可能性があります。 最もクリーンでスケーラブルな戦略は、ペンディングされた変更の情報を中間層に保持し、データ・コントロールは特定なAMインスタンスおよびペンディングのデータベース・レベルのトランザクション・ステートを含んでいるJDBC接続に結合しないことです。 このことで、アプリケーション・モジュール・プールが提供するパフォーマンス最適化機能を最大限に活用できます。
したがって、上記に基づく複数のHTTPリクエストにわたる状態永続化機能の使用には、次の制限事項が適用されます。
postChanges メソッドは使用しないでください。AMによって管理されている状態に悪影響を与え不測の、場合によっては回復不可能なエラーが発生する可能性があります。jbo.locking.mode パラメータの値を「optimistic」に変更します。
あるリクエストで、postChanges()メソッドを実行して、またはPL/SQLストアド・プロシージャをコールしたいが、リクエストの最後にコミットやロールバックを発行したくない場合は、解決策として、そのリクエスト以降にコミットまたはロールバックが発行されるまで、対象のデータ・コントロールのリリース・レベルをReservedレベルに設定する方法があります。これにより、 HttpSession が同じAMインスタンスを使用することが保証されます。 ただし、Reservedレベルは長時間使用するには向かないため、新規トランザクションの作成からコミットまたはロールバックまでの期間を短くすることをお薦めします。 既述のとおり、Reservedレベルはアプリケーションのスケーラビリティと信頼性にマイナスの影響をもたらします。
| 注意: | アプリケーション・モジュールがReservedレベルでリリースされると、リリース・レベルが明示的に変更されるまで、後続するすべてのリクエストにそのレベルでリリースされる構成となります。 したがって、コミットまたはロールバックの発行後は、リリース・レベルをManagedレベル(ステートフル・モード)に戻すことが、開発者の責任となります。 |
前述から、もう1つのベスト・プラクティスが導かれます。
データベース接続プーリングが使用されている場合(jbo.doconnectionpoolingパラメータがtrueに設定されている場合)は、AMにより管理されている現在のトランザクション内でコミットなしにDML処理を実行することと、postChanges()のコールおよび/またはPL/SQLストアド・プロシージャの実行することは、同時に行なわない。
前述のとおり、
postChanges()メソッドをコールするのいずれかを実行する場合は、現在のデータ・コントロールが引き続きそのAMインスタンスを占有できるように、リリース・レベルをReservedレベルに設定する必要があります。
ただし、AMの構成では、「解放時にアプリケーション・モジュールを切断」オプションを使用し、複数のAMのがデータベース接続プールを共有する場合は、AMをAMプールにリリースする際に、そのJDBC接続は切断、リリースされて、データベース接続プールに戻されます。その後、その接続に対して ROLLBACK が発行されます。 つまり、ポストされたがコミットされていないすべての変更は失われます。 次にそのAMが使用されるリクエストの際に、プールからJDBC接続を取得しますが前に使用した接続オブジェクトとまったく同一ではないので、前のリクエスト中にDBにポストされたがまだコミットされていない変更は、消失します。
ADF Business ComponentsのAMプールは、AMが、自分が管理しているペンディング状態を含む現在のデータ・コントロールからできる限り離れないようにします。 これは、ユーザー・セッションのアフィニティの維持と呼ばれます。 データ・コントロールが、各リクエストに対して常に同一のAMインスタンスが継続して使用できるとき、最高のパフォーマンスが達成されます。永続スナップショットからのペンディング・ステートの再アクティブ化に必要なオーバーヘッドが回避されるからです。
jbo.dofailover と呼ばれる、AMの構成プロパティがあります。 このプロパティが非アクティブ化されるタイミングと頻度を制御します。 フェイルオーバー機能がオフになっていると、AMのペンディング・ステートは、プールが現在参照されているAMインスタンスを別のデータ・コントロールに渡す直前にのみ非アクティブ化されます。 すなわち、AMの状態は必要性が生じるまで保存されません。 フェイルオーバー機能がオンになっていると、アプリケーション・モジュールのペンディング・ステートは、AMプールにチェックインして戻されるたびに非アクティブ化されるので、その状態はプールに戻った時点でただちに保存されます。
フェイルオーバーのメリットは、何らかの理由でAMインスタンスが消失した場合(たとえば、このアプリケーションがデプロイされたOC4Jインスタンスの1つに障害が発生した場合)であっても、その状態が常に保存されているために任意のAMインスタンスによって随時アクティブ化できることです。 この機能は、AMがManagedレベルでリリースされるとチェックインごとにアクティブ化するための追加のオーバーヘッドを伴います。 フェイルオーバー・オプションは、パフォーマンスと信頼性がトレードオフされる典型的なケースです。 このパラメータの値には明確な推奨値はありません。使用可能なリソースおよびアプリケーションの性質に基づく各ユーザーの決定が必要です。 フェイルオーバー・モードのデフォルトの設定は true です。オラクルの独自ADF BCは、この分野での弱点はパフォーマンスより信頼性にあります。
| 注意: | フェイルオーバー・オプションは、ReservedレベルでリリースされたAMの場合は無視されます。Reservedレベルは、データ・コントロールが同じAMを要求して、別のAMでの代替は受け入れないことを意味し、その場合、フェイルオーバー・オプションは意味がなくなります。 ただし、リリース・レベルがReservedからManagedに戻ると、すぐフェイルオーバー・パラメータの値が適用されます。 |
非アクティブ化された情報を格納できる場所がいくつかあります。 その場所は、AM構成にオプションを構成することで、またはプログラムにより制御できます。 格納場所は、データベース、ローカル・ファイル・システム上のファイル、メモリーから選択できます。
データベースよりファイルのほうがアクセス速度は速いため、この選択肢は、利用可能な最速な方法となります。 この選択肢は、中間層全体(1つ以上のOracle Application Serverインストール)およびそのすべてのOC4Jインスタンスが同一マシン上にインストールされている場合、または共有ファイル・システムへのアクセスが可能な場合に適しています。 通常、この選択肢は、単一のOracleASを使用する小さな中間層に適しています。 つまり、単一のOracleASとそのすべてのコンポーネントが単一の物理マシンにインストールされた中間層です。 永続スナップショットの場所と名前は、jbo.tmpdirプロパティが指定された場合にその値によって決定されます。 このプロパティは、構成プロパティのBC4Jプロパティの優先順位のルールに従います。 他に指定されたプロパティがなければ、場所は、システム・プロパティ user.dir の値によって決定します。s
デフォルトで選択されます。 パフォーマンスの観点から見て、ファイルに格納する場合よりやや遅くなりますが、最も信頼できる方法です。 ファイルへの非アクティブ化で最も一般的な問題は、リモートでインストールされたOracleASインスタンスにはアクセスできないため、クラスタ環境で一方のノードに障害が発生した場合、他方のノードからは非アクティブ化した情報にアクセスできないことです。その場合はフェイルオーバーが機能しません。 もう1つの問題は、リモート・ノードからファイルにアクセス可能でも、アクセス時刻がローカル・ノードとリモート・ノードで大きく違う場合、パフォーマンスに一貫性がなくなることです。 データベースに関しては、アクセス時刻はすべてのノードで同一であることが必要です。 したがって、ファイルより遅くても、データベースが最も信頼性の高い堅実な選択肢となります。 非アクティブ化された情報は、いずれの記憶域についても同様にXMLスナップショットとして格納された後、データベース記憶域の場合は、 jbo.server.internal_connection プロパティで指定されたスキーマ内の一時表のBLOB列に書き込まれます。
これは、テスト目的のみで使用される選択肢で、本番環境での使用はお薦めしません。
設定値を変更するには、jbo.passivationstore プロパティを database または file に設定します。 null値の場合は、デフォルト、すなわちデータベースが使用されます。 database と file 以外の値はすべて、デフォルトの動作になります。 具体的には、データベースがOracleまたはDB2の場合、デフォルトで DBSerilizazer が使用されます。 その他の場合は、デフォルトで FileSerializer が使用されます。
記憶域のプログラムによる設定には、 oracle.jbo.ApplicationModuleインタフェースの setStoreForPassiveState() メソッドを使用します。 引数として渡すことのできる値は次のとおりです。
PASSIVATE_TO_DATABASEPASSIVATE_TO_FILEPASSIVATE_TO_MEMORY次に、AMインスタンスが非アクティブ化処理中に生成されるスナップショットを含む情報の一部を示します。
| 注意: | 前述の情報は、非アクティブ化できる情報をすべて網羅したものではなく、その主な一部です。 |
次に示すのは、AMからの非アクティブ化される際に生成されるスナップショットの簡単な例で、HRスキーマからの部課表に基づくVOを保持しています。 このスナップショットは、部課番号が271で部課名値が「TestDept」の新規の行を1つ保持しています。
<AM MomVer="0"> <cd/> <TXN Def="1" New="0" Lok="1"> <EO Name="model.Departments"> <![CDATA[000100000003C20348]]> <DepartmentsRow PS="0" PK="Y"> <DepartmentId>271</DepartmentId> <DepartmentName>TestDept</DepartmentName> </DepartmentsRow> </EO> </TXN> <VO> <VO It="1" Sz="10" St="17" Ex="1" Def="model.DepartmentsView" Name="DepartmentsView1" cli="1"> <Key> <![CDATA[000100000003C20348]]> </Key> <cd> <QC> <Ke>ACED000577020000</Ke> <NR Hdl="76" Idx="26"> <Ke>000100000003C20348</Ke> </NR> </QC> </cd> </VO> <VO It="1" Sz="10" Ex="1" Def="model.EmployeesView" Name="EmployeesView3" cli="1"/> </VO> </AM>
アプリケーションに指定できるタイムアウト・パラメータはいくつかあります。 ここでは、そのうちの2つを説明します。 はじめはHTTPタイムアウトです。 このパラメータはAM構成では指定されていません。 web.xmlファイルのWebアプリケーション用クライアント・プロジェクトで指定されています。デフォルト値は35分です。この値はweb.xmlファイルのテキストで、手動により次の部分を検索し編集できます。
<session-config> <session-timeout>35</session-timeout> </session-config>
または、web.xmlファイル上を右クリックしてプロパティを選択して編集します。HttpSessionタイムアウトは、一般セクション(デフォルトで表示される最初のページ)のセッション・タイムアウトを指定(分)フィールドで指定します。
ただし、このパラメータに関して最も興味深い点はその設定方法ではなく、中間層のJ2EEによるその解釈にあります。 HttpSessionは、物理リソースとして、または論理ユーザー・セッションとして扱われます。 論理ユーザー・セッションとは、アプリケーションとユーザーの相互作用です。 物理リソースとしての扱いとは、HttpSessionが、システム・ニーズに応じて削除または再インスタンス化され、その終了および再インスタンス化がユーザーに対して完全に透過的であるメモリー消費型のオブジェクトとして扱われることを意味します。
ADF BCは、データ・コントロールに格納されたSessionCookieを使用して、HttpSession に関連づけられた BindingContext に関連したAM状態の状態識別子を格納します。 前述のとおり、 HttpSession には BindingContext が1つあり、 BindingContext にあるすべてのデータ・コントロールは、BindingContextに格納されたコントロールとして、間接的に同一の HttpSession に関連付けられています。 HttpSessionがタイムアウトすると、BindingContext は有効範囲から外れてしまうため、データ・コントロールに格納されて状態識別子として使用される SessionCookie も有効範囲外となり、このデータ・コントロールが参照するAM状態にはアクセスできない場合があります。 同時に現在の状態を保持する AM は非参照となり、リセットされて(状態がクリーンアップされて)任意の HttpSession の処理で使用可能となります。 フェイルオーバーが無効化されている場合、HttpSessionタイムアウトは、セッションの物理的および論理的終端を意味します。物理的な HttpSession インスタンスとそのセッションに関連付けられた状態のいずれにもアクセスできないためです(したがって、この場合、新たなHttpSession開始までは、作業が継続できません)。古い HttpSession と、それに関連付けられた状態は破棄されます。 フェイルオーバーが有効化されている場合は、HttpSession タイムアウトは、セッションの物理的終端のみを意味します。そのセッションの HttpSession インスタンスにはアクセスできませんが、そのセッションに関連するデータ・コントロールに関連付けられた状態にはアクセス可能です (BindingContextとそこに格納されたすべてのデータ・コントロールおよびその HttpSession が有効範囲外となっても可能です)。 この時点で、AMもリセットされて非参照となりますが、フェイルオーバー機能が有効化なため、その状態はその前に非アクティブ化されています。この状態にまだアクセスが可能なのは、HttpSession フェイルオーバーが有効化されている場合、クッキーはブラウザ・クッキーとしてブラウザ層にも格納されるためです。 フレームワークのこのような動作により、異なる中間層サーバーにルーティングされた、新しい HttpSession とペンディング・セッション・ステート間でのリンクが再確立される可能性が保証されます。
たとえば、あるユーザーがブラウザを開いて作業後に変更をコミットしないまま昼食のため席を立つ、というシナリオを考えてみます。 フェイルオーバーは有効化されているものとします。 HttpSessionはタイムアウトし、メモリーから削除されます。 BindingContext も同様です。対応するAMは、非参照となりリセットされます。ただし、ユーザーの観点から、これはユーザー・セッションの終わりを意味しません。 ユーザーが戻り作業を再開すると、ユーザーのローカル・ファイル・システムに格納されたブラウザ・クッキーを使用して、HttpSession が再作成されます。 ADFBindingFilter がブートストラップを再度実行して、同一の状態識別子を保持した SessionCookie を持つデータ・コントロールとともにその BindingContext を再作成します。また、この HttpSession からの最初のリクエスト中にAMをチェックアウトしたデータ・コントロールは、前の HttpSession によって、前のデータ・コントロールを通じて参照されたAM状態を識別してアクティブ化できます。 したがって、ユーザーは、 HttpSession がタイムアウトしなかったかのように作業が継続できます。 つまり、ユーザーの観点からセッションは一度も終了していないことになります。セッションは物理リソースとしてのみ扱われ効率を理由に削除される場合があるが、論理アプリケーションのセッションは存続する、というわけです。
このテストの手順は、次のとおりです。 簡単なADF Business ComponentsアプリケーションとそのWebクライアントを作成します。 HttpSession タイムアウトを1分に設定して、フェイルオーバーが有効化されていることを確認します。クライアント・アプリケーションを実行して新しい行を作成、またはいずれかの値を変更します。ただし、コミット(またはロールバック)はしません。ここで、1分以上待機して、HttpSession をタイムアウトさせます。 ViewControllerプロジェクトのプロパティ・ダイアログの「実行」パネルで -Djbo.debugoutput=console などのJava VMプロパティを追加した場合は、 -Djbo.debugoutput=console が実際にタイムアウトした時刻がデバッグ・トレース情報として表示されます。 次に作業を続行します。 HttpSession がタイムアウトしなかったかのように、タイムアウト前の状態がそのまま保存されていることがわかります。 まだコミットされていないすべての変更を表示でき、また、それらのコミットも可能です。 同じシナリオを、今度はフェイルオーバーをオフにして試してください。 タイムアウト後、ペンディング・ステートが消失します。 この場合、前回の変更コミット時にさかのぼって作業を開始する必要があります。
ユーザーの観点から、HTTPタイムアウトが透過的で認識されない点が、実際にはどのように役立つか考えます。 この特長はリソース管理に使用されます。 Internet Explorerを使用している場合は、「スタート」メニューからブラウザを数回起動できます。また、それが別々のブラウザ・ユーザーであるかのように、起動ごとに別々のプロセスで実行されます。
| 注意: | その他のブラウザ(別のブラウザ・プロセスを起動するのではなく新規ブラウザ・ウィンドウのみを作成する場合、IE自体を含む)は、すべてのウィンドウを同じブラウザ・ユーザーのように扱います。 |
ユーザーが多くのブラウザ・ウィンドウを開くと仮定すると、一度に1つのウィンドウでのみアクティブな作業を実行できることが明白です。したがって、HttpSession タイムアウトを指定して非アクティブなセッションを削除させ、リソースの節約のためにそれらが参照するAMを非参照としリセットされるようにします。こうして、特定のウィンドウでの作業を続行する場合のみそれらをリストアさせます。 ただし、HttpSessionの削除と再作成が過度になり、AMの余計な非アクティブ化とアクティブ化の発生を避けようとして、設定する値が小さくなり過ぎないように注意してください。
HttpSession タイムアウトに関する詳細な説明をもう1点追加すると、このタイムアウトはプログラムによっても制御することが可能です。 この場合ユーザーは、 javax.servlet.http.HttpSession クラスのインスタンスにアクセスして、 invalidate() というメソッドのを実行する必要があります。 この手順は、HTTPタイムアウトと同じ効果を持ち、前述したように必ずしも論理セッションの終了を意味しません。
多くの場合、権限のないユーザーには各自のセッションで作業終了後に既存の HttpSession にアクセスを許さないことが大切です。 これを防ぐためには、論理ユーザーのセッションの終了が必要ですが、これまでに説明したように、フェイルオーバーが有効化されている場合は、HttpSession ではそうはなりません。 その1つの方法として、ブラウザ・ウィンドウ(より正確に言えば、同じブラウザ・プロセスに属するすべてのウィンドウ)を閉じます。 これで、そのプロセスは終了し、結果的にエンド・ユーザーの HttpSession も終了しますが、 それは再構成することができます。 後述する別のタイムアウト・パラメータを使用して、ブラウザ・プロセスの終了後も一定時間論理セッションを存続できます。 ユーザー・セッションを正しく終了するもう1つの方法は、アプリケーションに「Log off」と呼ばれるボタンまたはリンクを作成します。ユーザーがこのボタンをクリックすると、AMはステートレス・レベルでリリースされます。この方法ではAMはまったく非アクティブ化されないため、このセッションの状態は保存されません(この場合、トランザクションについてペンディング状態のすべての変更のコミットまたはロールバックをお薦めします。 開発者としては、そのことを実際に確認し、ログオフ前の適切な処理が実行されることを望んでいます)。
フェイルオーバーが有効な場合は、HttpSession が物理リソースとしてのみ扱われ、ユーザー・セッションの論理終了としては扱われないことについて重要な例外があります。AMがReservedレベルでリリースされた場合、HttpSession タイムアウトは論理セッションの終了となり、ユーザーはその後の作業を続行できなくなります。 ユーザーは認証プロセスの実行が必要となり未保存の変更はすべて失われます。前述のとおり予約済レベルでは、jbo.dofailover の値が無視されるからです。もちろん、フェイルオーバーの無効化の場合は、リリース・レベルに関係なく、HttpSession がユーザーのセッションの論理終了として扱われます。
HttpSession オブジェクトについては、理解すべき重要な事項があります。 ADF BCのセッション・ステートは、ADF BC SessionCookieとして識別されます。 ADF BC SessionCookieは、HttpSession に登録されるため、 HttpSession のリクエスト間で管理できます。 HttpSession は、サーブレット・コンテナによって生成されたIDで識別されます。 アプリケーション開発者は、ブラウザCookie、URLリライティングなど様々な HttpSession 追跡メカニズムを使用してHttpSession のIDとブラウザを関連付け、あるブラウザからの一連のHTTPリクエストを特定な HttpSession に相互に関連づけます。
一方、SessionCookie に対しては、対象のブラウザが配置されたローカル・ファイル・システム上にブラウザCookieが格納されています。 Webアプリケーションが分散としてマーキングされていて、1つのクラスタ内の複数のノードにインストールされている場合、実行時に HttpSession とその SessionCookie はアプリケーションがインストールしたOC4Jのすべてのインスタンスにレプリケートされます(これはアプリケーションが、OracleASでレプリケート・モードで構成された場合のみに当てはまります)。したがって、ノードの1つに障害が発生しても、HttpSession は依然として、他のOC4Jインスタンス上でシームレスにフェールオーバーされ、使用可能になります。 レプリケーションが失敗しても、ADF BC SessionCookieは再作成できます。ただし、汎用の HttpSessionは再作成できません。 このことは、論理ユーザー・セッションとそのペンディングの作業を、ブラウザCookieでリカバリおよび再作成できることを意味します。
次に説明する第2の重要なタイムアウト・パラメータは、AM構成で設定されるもので、jbo.maxpoolcookieage と呼ばれます。 このパラメータは、秒単位で指定され、ブラウザ・プロセスの終了後、または対象のブラウザでの最後のアクティビティの終了後、ブラウザのCookieが存続する時間を定義します。 デフォルト値は-1で、ほとんどの場合、この値にする必要があります。 デフォルト値は、ブラウザ・プロセスがアクティブな場合にかぎりブラウザCookieも存在することを意味します。 正の値を設定した場合は、ブラウザ・プロセスの終了後も、ブラウザでの最後のアクティビティの終了から指定した時間が経過するまで、ブラウザCookieが保持されることを意味します。 ここで理解する必要がある最も重要な結論は、ユーザーがブラウザを開いて自分のアプリケーション内で何らかのAMの状態を作成後、すべてのブラウザ・ウィンドウを閉じるとタイムアウト(jbo.maxpoolcookieageパラメータで指定した時間)内で同じブラウザを再オープンした場合に、そのユーザーのセッションがリストアされ、まだコミットされていないすべての変更も一緒にリストアされる点です。 つまり、このユーザーは、ブラウザのプロセスが終了しなかったかのように作業を続行できます。
| 注意: | JDeveloperバージョン9.0.5.Xの既知のバグにより、AM構成で設定された jbo.maxpoolcookieage パラメータは無視され、常にデフォルト値が使用されます。 この問題への対処には、このパラメータをコマンドライン・パラメータとしてクライアントのプロジェクト・プロパティに渡します(このパラメータは実行されるクライアント・プロジェクトでそのプロパティがピックアップされるため、サーバー/プロジェクトとしてではなく、クライアント・プロジェクトとして設定が必要になります)。プロジェクト・プロパティ・ダイアログの「実行」ページでは、Javaオプションで追加のパラメータを指定できます。 オプションを渡すには、 -D フラグを使用します。 たとえばこのパラメータの場合、 -Djbo.maxpoolcookieage=300 といった追加が必要です。 これにより、このパラメータの値は5分に設定されます。 (AM構成で無視されるパラメータがもう1つあります。それは jbo.ampool.monitorsleepinterval です。 これも既知の不具合です。ただし、このパラメータについては、本書では説明しません)。ここで言及した不具合は両方とも、JDeveloper 10.1.2で修正されているため、以降のバージョンでは、この方法によるパラメータの設定は必要ありません。 |
ここでは、単純なユースケースを使用して、前述の論点を例証します。 jbo.maxpoolcookieage プロパティの値を5分に設定します。Webアプリケーションを実行して、何らかのレコードを更新または挿入して状態を作成します。 ここでブラウザを閉じます(タスク・マネージャからブラウザ・プロセスが消えていることを確認して、ブラウザ・プロセスの終了を確認してください)。次に、指定したタイムアウト内に再びブラザウを開きます。 変更がピックアップされて、ブラウザを閉じていないかのように作業を続行できます。
このように、フェイルオーバーを有効にすると、HttpSession タイムアウトがManagedレベルの論理セッションの終了ではなく物理リソースとして解釈されます。 開発者がこの動作を変更して、HttpSessionタイムアウトを論理セッションの終了として解釈させる場合は、 HttpSession タイムアウトと jbo.maxpoolcookieage を同じ値に設定します。 この場合、HttpSessionCookieとSessionCookieに関連するブラウザCookieの両方が有効範囲から外れて同時に削除されるため、セッションのリストアに使用できるものはありません。 問題は、そのブラウザをjbo.maxpoolcookieage の経過前に再起動すると、ADF BCのセッション・ステートを、ブラウザ・プロセスの終了後でもリストアできる点です。 たとえば、あるユーザーが作業後にブラウザを閉じた場合、第2のユーザーは同じマシン上で新規のブラウザ・インスタンスを再起動して、第1のユーザーが作成した任意のAM/VO/Transactionの状態(WHERE句、RowSetsなど)にアクセスできます。
| 注意: | 一般に、このプロパティの値を変更する必要はありません。 必要な場合を除き、この値は変更しないでください。 デフォルト値を別の正の値に変更すると、セキュリティ上のマイナスの影響がある場合があります。 この値を変更する場合は、すべての結果が予測の範囲内にあることを確認してください。 |
カスタムのユーザー定義情報をAMに追加することは、広く行われている手法です。そのような情報には、ユーザーがAMに追加したメンバー変数や、セッション・オブジェクトに格納されたカスタムの情報などがあります。 ADF BCフレームワークでは、この情報を非アクティブ化するメカニズムも提供します。 これには、フックと呼ばれる方法を使用します。このAPIは、非アクティブ化およびアクティブ化の実行中にフレームワークが実行する空のメソッドをいくつか提供します。 これらのメソッドをオーバーライドすることにより、ユーザーは、非アクティブ化、アクティブ化のそれぞれの発生時にカスタム・ロジックを追加することができます。 この機能を活用するために、ユーザ−がオーバーライドできるメソッドは、 oracle.jbo.server.ApplicationModuleImpl に定義されています。その一部のメソッドを次に示します。
protected void passivateState(Document doc, Element parent) public void activateState(Element elem)
また、この目的に使用できる他のフックもあります。詳細は、 oracle.jbo.ApplicationModuleとoracle.jbo.server.ApplicationModuleImpl のAPIドキュメントを参照してください。VOとEOにもフック・メソッドがあります。次に、前述のメソッドをオーバーライドして、AMに格納されたカスタムの情報を非カウティブ化・アクティブ化のサイクルで確実に取り込む単純な例を示します。
保持したい値に関連するセッションが含まれる、jbo.counter というAMに定義されたカスタムのパラメータを保持する場合を考えます。 AMごとに関連付けられたセッション・オブジェクトがあり、そこにセッション(HttpSessionとは混同しないでください。詳細は oracle.jbo.Session インタフェースのAPIドキュメントを参照してください)の状態が格納されます。そこに、ユーザーのカスタム情報を格納する指定されたオブジェクトがあります。 これは、「ユーザーのデータ」と呼ばれます。 通常、カスタム情報はここに格納されます。 この場合、oracle.jbo.server.ApplicationModuleImplの拡張クラスで、前述の2つのメソッドをオーバーライドする必要があります。 次のようにします。
public void passivateState(Document doc, Element parent) { int counterValue = getCounterValue(); Node node = doc.createElement(COUNTER); Node cNode = doc.createTextNode(Integer.toString(counterValue)); node.appendChild(cNode); parent.appendChild(node); } public void activateState(Element elem) { super.activateState(elem); if (elem != null) { NodeList nl = elem.getElementsByTagName(COUNTER); if (nl != null) { for (int i=0, length = nl.getLength(); i < length; i++) { Node child = nl.item(i).getFirstChild(); if (child != null) { setCounterValue(new Integer(child.getNodeValue()).intValue()+1); break; } } } } } /* * Helper Methods */ private int getCounterValue() { String counterValue = (String)getSession().getUserData().get(COUNTER); return counterValue == null ? 0 : Integer.parseInt(counterValue); } private void setCounterValue(int i) { getSession().getUserData().put(COUNTER,Integer.toString(i)); } private static final String COUNTER = "jbo.counter";
これは、フックを使用したカスタム情報を永続的に管理する単純な例です。
一時VO属性およびEO属性に基づいてはいないがSQL計算の式によりデータベースから直接導出されるVO属性は、非アクティブ化/アクティブ化の目的で同じように扱われます。 そこで、簡単にな説明するために、ここでは一時属性とEOに基づく属性のみを取り上げます。 一時属性はデフォルトでは非アクティブ化されません。 一時属性の性質上、通常は「読取り専用」とされ、ごく簡単に再作成可能だからです。したがって、XMLスナップショットの一部として、これらの値の非アクティブ化は意味がありません。 ただし、各一時属性を非アクティブ化可能なものとする構成はできます。 またそれぞれのVOも非アクティブ化可能なものとして構成できます。 一時VOだけではなく、すべてのVOについても同様です。 デフォルトでのすべてのVOは、非アクティブ化可能としてマーキングされますが、すべての一時属性はそうではありません。 つまり、一時属性のみを含むVOは非アクティブ化可能とマーキングされますが、その非トランザクション型の状態のみが非アクティブ化されます。
前述のとおり、非アクティブ化によって保存される情報は、2つの部分に分かれます。 トランザクション型と非トランザクション型の状態です。 トランザクション型の状態とは、EOデータ(直接または間接エンティティ・オブジェクトに、またはビュー・オブジェクト行を通じてエンティティに対して実行)に対する一連の更新で、DBに保存すべき更新のことです。 非トランザクション型の状態には、カレントの行インデックス、WHERE句、ORDER BY句などのVO属性があります。詳細は「非アクティブ化の対象となる情報」の項を参照してください。
トランザクションにかかわる機能は通常EOレベルで管理できるため、一時属性の非アクティブ化の方が、リソースおよびパフォーマンスの面で高コストであることは大変重要です。 一時VOは、EOに基づいていないため、すべての更新はEOではなくVOのキャッシュで管理されます。 そのため、一時VOまたは一時属性の非アクティブ化には特殊な扱いが必要です。 通常、非アクティブ化は変更後の値のみを保存しますが、一時VOの非アクティブ化では行全体の保存が必要です(行には非アクティブ化可能とマーキングされた属性のみが格納されます)。
次の例を考えてみます。 標準の(一時VOではない)VOには、10行あり、各行に7つの属性があります。 行番号2で2つの属性、行番号5で1つの属性が変更されました。この場合、非アクティブ化処理中に更新された3つの値が非アクティブ化されます。 次に、7つのうち4つの一時属性が非アクティブ可能とマーキングされた同じサイズの一時VOを考えてみます。 前述の標準のVOの場合と同じ更新とします。 非アクティブ化では、それぞれが4つの属性を保持する10行すべての保存が必要です(前述のとおり、7つの属性のうち4つのみが非アクティブ化可能とマーキングされているためです)。 非アクイティブ化される属性は標準のVOの3つに対して、この場合は40です。 非アクティブ化したい一時VOまたは一時属性がある場合は、はじめから一時VOまたは一時属性としないことをお薦めします。
VOを非アクティブ化可能または不可能とする構成には、VOをダブルクリックしてVOエディタを開き、「チューニング」パネルに移動します。「受動状態(現在の行、バインド値など)」チェックボックスは、デフォルトでオンに設定されています。 その他に、「一時的な値をすべて含む」チェックボックスは、デフォルトでオフに設定されています。 このチェックボックスをオンにすると、すべての一時属性が非アクティブ化可能となります。 また、各一時属性を非アクティブ化可能としてマーキングもできます。 VOエディタで、左側のツリーの属性ノードに移動して開くと、各属性が表示されます。 属性ごとに、構成オプションに関連する属性を設定できます。 「受動化」チェックボックスは、一時属性のみに表示される点に注意してください。
カスタム・コードは、一時属性の非アクティブ化が不要です。 これはコストの高い操作です。控えめに使用してください。
パーシャル・ロールバックは、非常に強力な機能となる可能性があります。 データベース・サーバーには、セーブポイント機能があり、開発者はトランザクション全体のロールバックではなく、トランザクション内の一定のポイントにロールバックできます。 ADF BCでは同じ機能を中間層に実装しました。 oracle.jbo.ApplicationModule インタフェースには、この機能を利用する3つのメソッドが用意されています。 それらを次に示します。
public String passivateStateForUndo(String id,byte[] clientData,int flags) public byte[] activateStateForUndo(String id,int flags) public boolean isValidIdForUndo(String id)
これらのメソッドにより、開発者は識別可能なスナップショットンのスタックを作成して、ペンディングしているトランザクション・ステートを名前で指定してリストアできます。 これらのスナップショトは、トランザクションが終了後、すなわちコミットまたはロールバックのイベント後は存続しない点に留意してください。 この機能により、変更を元に戻す、または再実行するなど、アプリケーションの複雑な機能が開発できます。 積極的な活用法としては、ブラウザ上の「戻る」ボタンや「進む」ボタンの実装がありますが、 もっと単純な方法で便利に使用できます。
ADF BCのチューニングは本書の範疇ではありませんが、状態管理に大きな影響を及ぼす、いくつかの非常に重要なパラメータについて触れておく必要があります。また、複雑な問題を簡単に説明するうえで役立つ、単純なテスト・シナリオについても、本書で説明します。
第1のパラメータは jbo.recyclethreshold です。このパラメータは、AMプールが新規のAMインスタンスの作成ではなく、リサイクルできるようデータ・コントロールに関連づけられているAMインスタンスの数を制御します。 このパラメータの値は、アクティブな同時HTTPセッションの予想数と同じにします。 アクティブなセッションを1つと定義すると、タイムアウト前に後続するリクエストの送信が予想されます。 このパラメータの値が適切な値より低いと、AMの過剰なリサイクルを招き、パフォーマンス面においてマイナスの影響が発生します。
bo.ampool.maxavailablesize は、プールで待機できるAMインスタンスの最大数です。リクエストを受け取った際に使用可能なインスタンスがあれば、コストのかかる新規インスタンスを作成することなく提供されます。新規AMインスタンスの作成にはAMそのものの他に、そのデータ・モデルに定義されているすべてのVOのインスタンスも作成が必要です。各VOがインスタンス化された時点でVOの問合せが実行されます。このように、各AMインスタンスの作成には複数の問合せの実行が必要です(VOごとに1つ)。 そのため、AMプールは、AMの作成および削除が最小限となる構成をお薦めします。
bo.ampool.minavailablesize は、プールで待機できるアプリケーション・モジュールの最小数です。
前述のパラメータの両方を同じ値に設定すること、またその値は、同時に処理されるHTTPリクエストの予想数にすることをお薦めします。そうしない場合は、アプリケーション・モジュールの作成、削除が過大になります。 その場合もパフォーマンスに影響があります。 これらのパラメータは両方とも、リリースされた参照、非参照のAMにかかわる点に注意してください。
前述のパラメータがプールに及ぼす影響を例示します。次の場合を考えてみます。同時ユーザーが50という、通常のワークロード用に構成されたプールを仮定します。 同時に処理されるリクエストの数は、平均で10とし、 jbo.ampool.minavailablesize および jbo.ampool.maxavailablesize は、10に設定されるとします。通常の状態では、プールにはAMが50あり頻繁に使用され、プールは与えられた時点でプール内にある利用可能なAMが10以上になることを確認します。 ピーク・タイムがあり、100人の同時ユーザーがこのアプリケーションで作業しているとします。 この状況に対応するため、プールは、さらに50を追加するAMの作成が必要です。 ところが、ピーク時が過ぎると、通常のワークロードに戻ります。 プールには100のAMがあり、そのうち50は使用されません。この非アクティブな50の全てではなく、その一部が参照されます(つまり、最後に処理したデータ・コントロールの状態を保持します)。 これは、このAM がステートフル・リリース・レベルでリリースされた場合にのみ当てはまります。 AMが最後に使用され jbo.ampool.maxinactiveage で指定された時間が経過すると、AMプールからの削除対象としてマーキングされます(現時点で、非アクティブなAMの数がjbo.ampool.maxavailablesizeで指定した数を超えているため)。こうして、プールは縮小に向い、プールに残った非アクティブなAMの数が10になるまで、AMインスタンスを継続して削除します。 ただし、フェイルオーバーが有効化されていない場合は、余分なAMは非アクティブ化されていない状態を保持します(ステートフル・レベルでリリースされた場合)。 フェイルオーバーが有効化されていない場合、AMはその状態を保存してアクセス可能とするために、プールからの削除前に非アクティブ化されます。
ADF BCのチューニングの詳細は、本書では説明しません。 How to Performance Tune an ADF Business Components Applicationを参照してください。
oracle.jbo.server.ApplicationModuleImpl を拡張するクラスで2、3のメソッドをオーバーライドすることにより、非常に興味深いテスト・ツールを作成できます。まず、クラスに次の2つの変数を追加します。
static int idProducer = 0; private int amId;
amIdは、AMインスタンスを一意に識別する変数です。 次のようにして、コンストラクタをオーバーライドします。
public AppModuleImpl() { amId = ++idProducer; System.out.println("AM with ID " + amId + " is being created"); }
クライアントに、次のメソッドを追加して公開します。
public int getAmId() { return amId; }
次に示す、3つのメソッドも追加します。
protected void afterConnect() { System.out.println("Application Module with id " +amId + " just got connected"); super.afterConnect(); } protected void prepareSession(oracle.jbo.Session session) { System.out.println("Application Module with id " + amId + " is about to be used by different session"); super.prepareSession(session); } protected void finalize() { System.out.println("Application Module with id " + amId + " is getting garbage collected"); super.finalize(); }
これらのメソッドは、診断情報を出力します。開発者は、それによって背後で行われている動作と様々なイベントが発生する状態やタイミングを理解できます。 AMプーリングがオンで接続プーリングがオフの場合、AMは作成直後にデータベースに接続し、そのライフサイクルの最後まで接続を保持することがわかります。データベース接続はAMがAMプールから削除されるときにのみ切断します。
prepareSession メソッドの実行は、対象のAMがリサイクルされていることを示します。つまり、その状態がリセットされて、以前の処理とは異なるデータ・コントロールの状態を取得したことを意味します。 このメソッドは、AMがそのライフサイクルで初めてリクエストを処理する場合にも必ず実行されます。
クライアント上で、AMへの参照を取得して、getAmId メソッドを使用して、どのAMがカレントの HttpSession に関連する BindingContext からのデータ・コントロールを処理しているかをチェックできます。 AMプールの正しい構成とリサイクルしきい値が、その時点で処理する HttpSession の数より高い場合、通常、 リクエストは常に同じAMインスタンスにより処理されます。
クライアント側でAMインスタンスへの参照を取得するには、JSPページ用のアクションを作成して、次のように、handleLifecycle メソッドをオーバーライドします。
public class DeptEmpBrowserAction extends DataForwardAction { protected void handleLifecycle(DataActionContext ctx) throws Exception { HttpServletRequest request = actionContext.getHttpServletRequest(); BindingContext bc = ctx.getBindingContext(); YourModule am = (YourModule)bc.findDataControl("YourModuleDataControl") .getDataProvider(); System.out.println("Message from client: The AM with id "+ am.getAmId() + " is in use"); super.handleLifecycle(actionContext); } }
AMプーリングを無効化した場合に、各リクエストが異なるAMインスタンスにより処理されることをチェックできます。 リクエスト間で継承された状態がある場合は、データ・コントロールが同じAMインスタンスを受け取ったのではなく、状態が前のAMから正しく非アクティブ化され、別のAMによってアクティブ化されています。
もう1つのテストケースは、AMプーリングを有効化してリサイクルしきい値を1に設定し、2つの異なるHTTPをセッション(Internet ExplorerとMozilla、または異なるプロセスで実行される、Internet Explorerの2つの異なるウィンドウ)を開くテストです。 このパラメータを使用して、セッション間でのAMインスタンスの受渡しを確認できます。
このような単純なツールを使用して、このドキュメントで説明した複雑な機能を実証できるケースが他にもあります。