Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11g リリース2 (11.1.2.4.0) B69399-03 |
|
![]() 前 |
![]() 次 |
この章では、Fusion Webアプリケーション状態管理機能およびこれを使用してADFアプリケーション・モジュールのリリース・レベルを指定してWebでステートフルなアプリケーションをサポートする方法について説明します。
この章の内容は次のとおりです。
現実世界のほとんどのビジネス・アプリケーションでは、マルチステップのユーザー・タスクをサポートする必要があります。最近のサイトは、ステップ・バイ・ステップ・スタイルのユーザー・インタフェースを使用し、論理的な順序のページを通してエンド・ユーザーをタスクの完了まで導く傾向があります。タスクが終了すると、ユーザーは全体をまとめて保存またはキャンセルできます。ただし、HTTPプロトコルは、個別でステートレスなリクエストの概念に基づいて構築されるため、ユーザー・アクションの順序を論理的でステートフルな作業ユニットにグループ化する作業はアプリケーション開発者が行うことになります。
一般的な検索後に編集を行うシナリオでは、エンド・ユーザーは、更新する適切な行を検索して発見した後、作業を保存するかキャンセルするかを決定する前に、関連するマスター/ディテール情報の複数のページを開いて編集を行うことができます。エンド・ユーザーが休暇の予約をオンラインで行う別のシナリオを考えてみます。プロセスには、エンド・ユーザーによる次の詳細情報の入力が含まれる場合があります。
旅行に含まれる1つ以上の飛行機利用区間
旅行する1人以上の乗客
座席の選択と食事の指定
異なる都市での1つ以上のホテルの部屋
借りる自動車
ユーザーは途中で、トランザクションの完了、後で行うための予約の保存またはすべての中止などを決定します。
これらのシナリオには複数のWebページにまたがる論理的な作業単位が含まれることは明らかです。これまでの章では、JDeveloperのJSFページ・ナビゲーション・ダイアグラムを使用してこのようなユースケースに対するページ・フローを設計する方法を見てきましたが、それはパズルのほんの一部にすぎません。途中のビジネス・ドメイン・オブジェクト(Trip
、Flight
、Passenger
、Seat
、HotelRoom
、Auto
など)に対してエンド・ユーザーが行う保留変更は、各エンド・ユーザーに対するアプリケーションの進行中状態を表します。これとともに、前のステップで行われた選択に関する他の種類の記録された情報が、アプリケーション状態の全体像を構成します。
これらのマルチステップ・シナリオを容易に想像できる場合もありますが、Webアプリケーションでの実装は、HTTP (hypertext transfer protocol)のステートレスな性質のために複雑になります。図43-1は、サイトへのエンド・ユーザーの訪問がHTTPの一連のリクエスト/レスポンスのペアで構成される様子を示しています。しかし、HTTPには、Webサーバーが異なるユーザーのリクエストを区別したり、同じユーザーがサイトとの対話中に行った異なるリクエストを区別したりするための手段がありません。サーバーは、ユーザーからのリクエストを、常にそれが最初にして唯一のものであるかのように受け取ります。
図43-2で示されているように、ステートレスなHTTPプロトコルを介した同じエンド・ユーザーからのリクエスト・シーケンスの継続を認識するために使用される手法には、cookieと呼ばれる一意の識別子が関係します。Cookieは名前と値のペアであり、サイトに対してユーザーが行う各HTTPリクエストのヘッダー情報で送信されます。ユーザーが行う最初のリクエストには、Cookieは含まれていません。サーバーは、cookieが存在しないことを利用して、ユーザーとサイトとの対話セッションの開始を検出し、そのユーザーに対するそのセッションを表す一意の識別子をブラウザに返します。実際のCookieの値は文字と数字から成る長い文字列ですが、説明を簡単にするために、一意の識別子はサイトを使用する異なるユーザーに対応するAやZなどの1文字であるものとします。
Webブラウザは、サーバーから返されるCookieを認識するための標準的な方法をサポートすることによって、次の項目を識別することが可能になっています。
Cookieを送信したサイト
Cookie値を保持する必要のある期間
そのユーザーがそれ以降に行う各リクエストにおいて、ブラウザは、Cookieの有効期限が切れるまで、リクエストのヘッダーとともにCookieを送信します。サーバーは、Cookieの値を使用して、異なるユーザーによって行われたリクエストを区別します。
Cookieは、最初に作成されてから週、月または年が経過してから有効期限が切れるように単一のブラウザ・セッションを超えて持続するように設定できますが、セッションCookieは、ブラウザを閉じると有効期限が切れます。
Java EEに準拠するWebサーバーが提供するHttpSession
と呼ばれる標準のサーバー・サイド機能を利用すると、Webアプリケーションで、特定のユーザーのセッションに関連するJavaオブジェクトを、属性と値の名前付きのペアとして保管できます。あるリクエストでこのセッションMap
に格納されたオブジェクトは、同じセッションの以降のリクエストが処理されている間に、アプリケーションが取得できます。
<web.xml>
ファイルの<session-timeout>
要素によって構成される時間枠内で、ユーザーが新しいリクエストの送信を続けている間、セッションはアクティブ状態を保ちます。セッションのデフォルトの長さは35分です。
HttpSession
機能はほとんどのアプリケーション状態管理戦略の構成要素ですが、使用方法を誤ると、パフォーマンスと信頼性の問題につながる可能性があります。まず、作成されるセッション・スコープのJavaオブジェクトはJava EE Webサーバーのメモリーに保持されるため、サーバーで障害が発生すると、HTTPセッションのオブジェクトは失われます。
図43-3で示されているように、信頼性を高める1つの方法は、複数のJava EEサーバーをクラスタ構成にすることです。このようにすると、Java EEアプリケーション・サーバーはユーザーごとのHTTPセッションのオブジェクトをクラスタ内の複数のサーバーにレプリケートするため、1台のサーバーが停止しても、オブジェクトはクラスタ内の他のサーバーのメモリーに存在し、ユーザーのリクエストの処理を続行できます。クラスタは異なるサーバーで構成されるため、サーバー間でのHTTPセッション・コンテンツのレプリケーションには、HTTPセッション・オブジェクトに対して行われた変更の、ネットワークを通したブロードキャストが含まれます。
HTTPセッションの乱用は、パフォーマンスに影響を与える可能性があります。
アクティブなユーザーが増えると、サーバーで作成されるHTTPセッションが増加します。
HTTPセッションごとに格納されるオブジェクトが増えると、必要なメモリーが増加します。ユーザーがアクティブでなくなってもメモリーが解放されない点に注意してください。これが発生するのは、セッション・タイムアウトまたは明示的なセッション無効化の場合のみです。ユーザーが常にログアウトするわけではないため、セッション無効化は常に発生するとはかぎりません。
クラスタにおいて、各HTTPセッションで変化するオブジェクト増えると、変化したオブジェクトをクラスタの他のサーバーにレプリケートするために生成されるネットワーク・トラフィックが増加します。
最初は、セッションで保管されるオブジェクトの数を最小限に保つことで問題に対処できるように思われます。しかし、これは、各ユーザーの保留中のアプリケーション状態を一時的に格納するために代替メカニズムを利用することを意味します。最も一般的な代替手段としては、リクエスト間にアプリケーションの状態をデータベース、または共有ファイル・システムのなんらかの種類のファイルに保存するという方法があります。
当然ながら、実際に実行するのは容易ではありません。可能性のある方法として、保留中の変更を基礎のデータベース表に保存し、各HTTPリクエストの終了時にトランザクションをコミットするというものがあります。しかし、このアイデアには2つの重要な障害があります。
データベース制約が失敗する可能性があります。
マルチステップ・プロセスの特定のステップで情報が部分的に不完全になり、このために変更を保存しようとする際にデータベース・レベルのエラーが発生する可能性があります。
変更のロールバックが複雑になります。.
作業の論理的な単位をキャンセルするには、複数になる可能性のある表でコミットしたすべての行を慎重に削除する必要があります。
これらの制限事項のため、制約がなく、すべての列の型が文字ベースとして定義されているデータベース表のシャドウ・セットを利用する解決策が発明されました。このような解決策の使用は、短期間に非常に複雑になります。最終的に、より汎用的で実現可能な方法でこのような問題に対処するには、なんらかの種類の汎用アプリケーション状態管理機能が必要であるという結論に達するでしょう。ADF Business Componentsは、このソリューションをすぐに実装できる状態で提供します。
状態管理を利用すると、43.1項「状態管理が必要な理由」で説明したメモリー、信頼性または実装の複雑さの問題に陥ることなく、マルチステップのユースケースをサポートするWebアプリケーションを簡単に作成できます。
アプリケーション状態管理は、タスク・フロー内の後で使用するための保存の機能、およびモデル・レイヤー内のアプリケーション・モジュール状態管理という2つのレベルで提供されます。
後で使用するための保存は、コントローラ・レイヤーで能動化され、現在のUIおよびコントローラの状態のスナップショットを自動的に保存し、さらにモデル・レイヤーに委譲することでその状態も受動化(保存)されます。
ADFデータ・コントロールを使用しない場合でも、アプリケーション・モジュール状態管理は単体で使用できますが、その場合はモデル状態のみが保存されるため、ほとんどのアプリケーションにとって、これは可能性の低いケースです。
後で使用するための保存では、未完了のトランザクションが検証規則の強制またはデータの送信なしで保存されます。エンド・ユーザーは、もともとはアプリケーションの終了時に保存されたデータと同じものを使用して、同じトランザクションの処理を後で再開できます。
ADF Business Componentsベースのアプリケーションは、各ユーザー・セッションのアプリケーション状態を自動的に管理します。これまでの4GLツールでのようなステートフル・プログラミング・モデルの簡単さを提供しながら、純粋なステートレス・アプリケーションに近いスケーラビリティを提供する方法で実装されます。この重要な機能を最大限に活用するには、背後で行われていることを理解しておく必要があります。
アプリケーション・モジュール・コンポーネントを使用して、完全にステートレスなアプリケーションを実装したり、複数のブラウザ・ページが関係する作業単位をサポートしたりできます。図43-4は、これらのマルチステップ・シナリオをサポートするための状態管理機能の基本アーキテクチャを示しています。アプリケーション・モジュールは、保留中トランザクション状態のXMLドキュメントへの受動化(保存)をサポートし、XMLドキュメントは、データベースの単一の汎用的な表に、一意の受動化スナップショットIDをキーとして格納されます。また、これらの保存されているXMLスナップショットの1つから保留中トランザクション状態を能動化する逆の操作もサポートします。この受動化と能動化は、必要に応じて、アプリケーション・モジュール・プールにより自動的に行われます。
ADFバインディング・コンテキストは、エンド・ユーザーごとのHttpSession
に存在する1つのオブジェクトです。このコンテキストが保持するのは、軽量アプリケーション・モジュール・データ・コントロール・オブジェクトへの参照であり、このオブジェクトは、各リクエスト時(データ・コントロールにアクセスしたとき)におけるプールからのアプリケーション・モジュール・インスタンスの取得と、各リクエストの終了時のプールへの解放を管理します。データ・コントロールは、ユーザー・セッションを識別するADFセッションCookieへの参照を保持します。具体的には、保留中トランザクションで作成または変更されるビジネス・ドメイン・オブジェクトは、この手法を使用するHttpSession
には保存されません。これにより、ユーザーごとに必要なセッション・メモリーが最小になり、サーバーがクラスタ構成の場合のセッション・レプリケーションに関係するネットワーク・トラフィックがなくなります。
信頼性の向上のため、セッション・オブジェクトをシリアライズします。配布セッションに格納されたオブジェクトは、java.io.Serializable
インタフェースを実装する必要があります。このインタフェースを実装すると、クラスタ内の各サーバー・インスタンスに、ワイヤを通じてデータを確実にトランスポートできます。セッション・データを追加する際は、デフォルトのHttpSession.setAttribute (String key, Object value)
メソッドではなく、addObjectToSession(String key, Serializable value)
メソッドなどのカスタム・メソッドを使用します。違いは、シリアライズ不可能なオブジェクトを持つaddObjectToSession()
メソッドをコールする場合にコンパイル時エラーが発生することです。put()
メソッドを使用して、セッションに配置されたシリアライズ不可能なオブジェクトを持ったセッション・オブジェクトをレプリケートしようとする場合、実行時エラーが発生し、ユーザー操作性が機能しない可能性があります。
さらに、複数のアプリケーション・サーバーがあり、オプションのADF Business Componentsフェイルオーバー・サポートを有効にしている場合(43.2.2.2項「オプションのフェイルオーバー・モードを有効にしたときの受動化の変化」を参照)は、以降のエンド・ユーザー・リクエストは、サーバー・ファームまたはクラスタ内の任意のサーバーで処理できます。セッションCookieは、どのサーバーがリクエストを処理するのかに関係なく、必要な場合にデータベース利用のXMLスナップショットから保留中アプリケーション状態を再能動化できます。
アプリケーション・モジュール状態の自動的な受動化と能動化がいつ行われるのかをわかりやすく説明するため、次のような簡単なケースについて考えます。
HTTPリクエストの開始時に、アプリケーション・モジュール・データ・コントロールは、プールからアプリケーション・モジュール・インスタンスをチェックアウトすることで、beginRequest
イベントを処理します。
アプリケーション・モジュール・プールは、参照されないインスタンスを返します。参照されないアプリケーション・モジュールとは、その時点では他のユーザー・セッションに対する保留中の状態を管理していないモジュールです。
リクエストの終了時に、アプリケーション・モジュール・データ・コントロールは、アプリケーション・モジュール・インスタンスを管理対象状態モードでプールに戻すことで、endRequest
イベントを処理します。
アプリケーション・モジュール・インスタンスは、この時点では、それを使用していたデータ・コントロールによって参照されています。また、アプリケーション・モジュール・インスタンスは、データ・コントロールによって行われ、メモリーに格納された、保留中のトランザクション状態(つまり、エンティティ・オブジェクトおよびビュー・オブジェクトのキャッシュ、行われてまだコミットされていない変更、およびカーソル状態)をまだ含んでいるオブジェクトです。この後で説明するように、それは、このデータ・コントロールに専用のものではなく、参照されているだけです。
以降のリクエストでは、SessionCookie
によって識別される同じデータ・コントロールが、アプリケーション・モジュール・インスタンスを再びチェックアウトします。
プールが使用するユーザー・アフィニティについてステートレスなアルゴリズムのため、状態がまだメモリー内に存在しているまったく同じアプリケーション・モジュール・インスタンスがプールから返されると思われる場合があります。(このアルゴリズムの詳細は、44.1項「アプリケーション・モジュール・プーリングについて」および44.2.7.2項「プールのサイズ指定に関するパラメータ」の参照プール・サイズに関する説明を参照してください。)
ときには、サイトに同時にアクセスするユーザーが非常に多いため、アプリケーション・モジュール・インスタンスを異なるユーザー・セッションが順番に再利用する必要があります。この場合、アプリケーション・プールは、次に示すように、現在参照されているアプリケーション・モジュール・インスタンスを別のセッションによる使用のためにリサイクルする必要があります。
ユーザーAのセッションのアプリケーション・モジュール・データ・コントロールは、リクエストの終了時に、アプリケーション・モジュール・インスタンスをアプリケーション・プールにチェックインします。このインスタンスの名前をAM1
とします。
ユーザーZの新しいセッションのアプリケーション・モジュール・データ・コントロールは、初めてプールにアプリケーション・モジュール・インスタンスを要求しますが、使用可能な参照されていないインスタンスはありません。このとき、アプリケーション・モジュール・プールは次のようにします。
インスタンスAM1
の状態をデータベースに受動化します。
別のセッションが使用するための準備として、AM1
の状態をリセットします。
AM1
インスタンスをユーザーZのデータ・コントロールに返します。
その後のリクエストで、ユーザーAのセッションのアプリケーション・モジュール・データ・コントロールが、プールにアプリケーション・モジュール・インスタンスを要求します。このとき、アプリケーション・モジュール・プールは次のようにします。
参照されていないインスタンスを取得します。
これは、前述の手順2と同じ手順に従って取得されたインスタンスAM1
である場合も、または途中で参照されなくなった場合は別のインスタンスAM2
である場合もあります。
ユーザーAに対する適切な保留中状態をデータベースから能動化します。
アプリケーション・モジュール・インスタンスをユーザーAのデータ・コントロールに返します。
受動化、能動化およびリサイクルのプロセスにより、データ・コントロールごとに専用のアプリケーション・モジュール・インスタンスを必要とせずに、データ・コントロールによって参照される状態をリクエスト間で保持できます。前述のシナリオのブラウザ・ユーザーはどちらも、複数のHTTPリクエストにわたってアプリケーション・トランザクションを継続しますが、エンド・ユーザーには、バックグラウンドで受動化と能動化が行われているかどうかはわかりません。保留中の変更が継続して表示されるだけです。プロセスでは、エンド・ユーザーが作業の論理的な単位をコミットできる状態になるまで、保留中の変更を基礎のアプリケーション・データベースの表に保存する必要はありません。
セッションのメモリー使用量が低い状態に維持される点に注意してください。これは、セッションによって直接参照される(そしてレプリケート可能な)唯一のビジネス・コンポーネント・オブジェクトが、データ・コントロールとセッションCookieであるためです。
アプリケーション・モジュール・プールは、アプリケーション・モジュール・インスタンスを、その保留中状態を管理している現在のデータ・コントロールにできるかぎり結び付けておこうとします。これは、ユーザー・セッション・アフィニティの維持と呼ばれます。データ・コントロールが、各リクエストでまったく同じアプリケーション・モジュール・インスタンスを使用し続けた場合、保留中状態を永続化されたスナップショットから再能動化する際のオーバーヘッドがなくなるので、最善のパフォーマンスが得られます。
jbo.dofailover
パラメータは、受動化が発生するタイミングと頻度を制御します。アプリケーション・モジュールの構成でこのパラメータを設定するには、ビジネス・コンポーネントの構成ダイアログの「プーリングおよびスケーラビリティ」タブを使用します。フェイルオーバー機能を無効にすると(デフォルトの設定)、アプリケーション・モジュールの保留状態は、必要なときにのみ受動化されます。これは、プールが現在参照されているアプリケーション・モジュール・インスタンスを別のデータ・コントロールに引き渡す必要があると決定する直前に発生します。
受動化アクティビティ(および対応する待機)は、フェイルオーバーが無効かつセッション・レプリケーションが無効の場合のみ発生します。フェイルオーバーまたはセッション・レプリケーションのいずれかが有効の場合、リサイクル・スレッドは、取得済のスナップショットを再利用してフェイルオーバーをサポートします。
注意: 受動化は、アプリケーション・モジュールがタイムアウトするときに発生することもあります。アプリケーション・プールの削除アルゴリズム( |
これに対し、フェイルオーバー機能が有効になっていると、アプリケーション・モジュールの保留状態は、アプリケーション・モジュール・プールにチェックインして戻されるたびに受動化されます。これにより、アプリケーション・サーバーの障害に対する最もペシミスティックな保護が提供されます。アプリケーション・モジュール・インスタンスの状態は常に保存され、任意のアプリケーション・モジュール・インスタンスがいつでも能動化できます。もちろん、この機能には、リクエストごとの積極的な受動化によるオーバーヘッドの増加という代償が伴います。
フェイルオーバーが有効化されている場合、接続を強制的にプールに復帰させるようにOracle WebLogic Serverを構成していると障害が発生することがあります。このタイプの障害によって、SQLException
(接続はすでに閉じられている)が生成され、サーバー・ログに保存されます。この例外は、ユーザー・インタフェースからは通知されません。確実に状態の受動化が発生し、ユーザーの変更内容が保存されるようにするには、サーバー管理者は、weblogic-application.xml
デプロイメント・ディスクリプタの<connection-check-params>
pool-params要素にあるパラメータinactive-connection-timeout-seconds
に適切な値を設定する必要があります。ほとんどの場合、デプロイメント・ディスクリプタ・パラメータを数分に設定すると、非アクティブ時の接続タイムアウトおよびその結果発生する受動化の失敗の強制が回避されます。ご使用の環境に合せて、この設定値を調整してください。
ベスト・プラクティス: デフォルトでは、フェイルオーバー機能は無効になっています( 可用性を高めるには、フェイルオーバー機能を有効化( アプリケーション・モジュール・プーリングおよび「アフィニティによるステートレス」アルゴリズムの詳細は、44.1項「アプリケーション・モジュール・プーリングについて」および44.2.7.2項「プールのサイズ指定に関するパラメータ」の参照プール・サイズに関する説明を参照してください。 |
注意: JDeveloper環境内でフェイルオーバー・サポートを使用するアプリケーションを実行またはデバッグするときは、アプリケーション・サーバーを頻繁に起動および停止しています。ADFのフェイルオーバー・メカニズムでは、ユーザーがサーバーを停止してアプリケーション・サーバーの障害をシミュレートしているのか、それとも初期状態のサーバー・インスタンスで何かを最初から再テストするために停止しているのかを認識する手段がありません。後者の場合は、サーバーでアプリケーションを再起動する前に、ブラウザを終了することをお薦めします。これにより、アプリケーションでフェイルオーバー・メカニズムの正しい機能に関する部分をテストするつもりがない場合に、このメカニズムが動作することによって発生する混乱を避けることができます。 |
データ・コントロールは、現在のHTTPリクエストの処理が完了したことを示すendRequest
通知を処理するとき、アプリケーション・モジュール・インスタンスをアプリケーション・モジュール・プールにチェックインして戻すことで解放します。アプリケーション・モジュール・プールは、インスタンスを管理し、インスタンスをプールに戻すときに使用される解放レベルに基づいて状態管理タスクを実行します(または実行しません)。
アプリケーション・モジュールのインスタンスをプールに戻す場合に使用される解放レベルには次の3つがあります。
管理 - デフォルトのレベルです。このレベルでは、アプリケーション・モジュール・プールは同じデータ・コントロールに対して同じアプリケーション・モジュール・インスタンスを保持しようとしますが、必要に応じてインスタンスを解放する場合があります。
非管理 - 現在のリクエスト以降の状態は保持されません。
予約 - アプリケーション・モジュール・インスタンスとデータ・コントロールの間で1対1の関係が保持されます。
注意: 通常は、予約解放レベルを使用しないことを強くお薦めします。データ・コントロールとアプリケーション・モジュールの相互関係が1対1になり、アプリケーションのスケーラビリティと信頼性が急激に低下するため、通常はこのモードを使用しないようにします。 |
これはデフォルトの解放レベルで、アプリケーション・モジュールの状態には関連性があり、複数のHTTPリクエストにわたってこのデータ・コントロールの状態を保持する必要があることを意味します。管理レベルは、次のリクエストに対してこのデータ・コントロールが物理的に同じアプリケーション・モジュール・インスタンスを受け取ることは保証しませんが、同じ状態のアプリケーション・モジュールが提供されて、毎回論理的に同じアプリケーション・モジュール・インスタンスであることは保証します。フレームワークは、現在利用できる場合、同じデータ・コントロールに対しては、アプリケーション・モジュールの同じインスタンスを提供できるように最善の努力をします。これはパフォーマンスの向上を目的として行われることで、同じアプリケーション・モジュールであれば、前回のリクエストで同じデータ・コントロールにサービスを提供した後の状態が保持されているため、前回の状態を能動化する必要がありません。ただし、すべてのリクエストでデータ・コントロールが同じインスタンスを受け取る保証はなく、以前にデータ・コントロールにサービスを提供したアプリケーション・モジュールがビジー状態または利用できない場合は、別のアプリケーション・モジュールがそのデータ・コントロールの状態を能動化します。このため、コントローラ・レイヤーのコードで、HTTPリクエスト間で、アプリケーション・モジュール・オブジェクト、ビュー・オブジェクト、またはビュー行に対する参照をキャッシュすることは有効ではありません。
このモードは、JDeveloperの以前のリリースではステートフル解放モードと呼ばれていました。
注意:
|
このモードは、このデータ・コントロールに関連付けられた状態を現在のHTTPリクエストの終了後に維持する必要がないことを意味します。状態管理に関連するオーバーヘッドがないため、これはパフォーマンスに関して最も効率のよいレベルです。ただし、状態を管理する必要のないアプリケーション、または状態をそれ以上維持する必要がない場合のみに使用を限定する必要があります。通常、ユーザーが作業の論理単位を終了したことを通知する場合は、プログラムでアプリケーション・モジュールを非管理レベルにして解放できます。
パフォーマンスに関するヒント: デフォルトの解放レベルは「管理対象」で、これは、複数のHTTPリクエストにわたってデータ・コントロールが有効であるためには、アプリケーション・モジュールの状態が適切で、保護対象である必要があることを意味します。受動化を回避して、高パフォーマンスを達成するには、特定のページに対して、実行時にリリース・レベルをプログラム的に「管理対象外」に設定します。典型的な例は、ログアウト・ページからHTTPリクエストにサービスを提供した後に、アプリケーション・モジュールを解放する場合です。 |
このモードは、JDeveloperの以前のリリースではステートレス解放モードと呼ばれていました。
このレベルは、各データ・コントロールが、最初のリクエスト、およびそれ以降にこのデータ・コントロールと関連付けられているHttpSession
から受信するすべてのリクエストの間、専用のアプリケーション・モジュールを割り当てられることを保証します。このデータ・コントロールは、常にアプリケーション・モジュールの同じ物理インスタンスを受け取ります。このモードは、下位互換性および非常にまれで特殊なユースケースのために用意されています。
予約レベルの使用例は、postChanges()
メソッドまたはPL/SQLストアドプロシージャからのリクエストが、リクエストの終了時にcommit()
またはrollback()
を発行しないために、データベース状態が保留中になる場合です。この場合、予約レベルのかわりに他のいずれかの解放レベルが使用されると、アプリケーション・モジュール・インスタンスが再利用されるときに、このアプリケーション・モジュール・インスタンスに関連付けられたデータベース接続上でロールバックが発行され、コミットされていない変更はすべて失われます。
パフォーマンスに関するヒント: 予約レベルを使用する必要がある場合、 |
予約モードにより悪影響が発生することもあります。なんらかの理由でアプリケーション・モジュールが失われると、データ・コントロールはプールからかわりに他のアプリケーション・モジュールを受け取ることができません。したがってHttpSession
も失われるために、信頼性が損われます。管理レベルではこのようなことはありません。
予約レベルは、アプリケーションが各リクエストで同じアプリケーション・モジュールを使用することが絶対的に必要であることを意味するため、予約解放レベルで解放されたアプリケーション・モジュールについては、フェイルオーバー・オプションは無視されます。
アプリケーションがサブクラス化されたエンティティ・オブジェクトを使用する場合、新しいエンティティのキー属性を事前に移入する必要があります。キーを事前に移入しないと、受動化および能動化が失敗します。create()
メソッドをオーバーライドするかまたはコミット後に実際の値をデータベースからフェッチする前に一時的に負の値をキーに割り当てるDBSequence
タイプを使用することによってキー属性を事前に移入できます。詳細は、4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照してください。
後で使用するための保存を有効化するには、まずエンド・ユーザーがアプリケーションの使用を停止した場合にアプリケーションの状態およびデータを保存するポイントに、セーブポイントを追加する必要があります。これを使用して、データおよびリージョン、ビュー・ポート、ポートレットに関する状態情報を保存できます。後でセーブポイント・リストア・アクティビティを使用して、セーブポイントに関連づけられたアプリケーションの状態とデータをリストアします。
セーブポイントの作成およびリストア方法の詳細は、22.7項「タスク・フローでのセーブポイントの使用」を参照してください。
後で使用するための保存では、暗黙の保存を実行することもできます。これらの保存では、たとえばユーザー・セッションがタイムアウトしたり、ブラウザ・ウィンドウが閉じられた場合に、エンド・ユーザーが明示的に保存のアクションを実行しなくても、自動的にデータが保存されます。
暗黙の保存を実行する方法の詳細は、22.7項「タスク・フローでのセーブポイントの使用」を参照してください。
アプリケーション・モジュールでデフォルトの管理状態解放レベルを使用しない場合は、プログラムで目的のレベルを設定できます。
非管理レベルを使用してアプリケーション・モジュールを解放するようデータ・コントロールを設定するには、DCDataControl
クラスでresetState()
メソッドを呼び出します(oracle.adf.model.binding
パッケージにあります)。
このメソッドは、リクエスト中のいつでも呼び出すことができます。リクエストの終了時にプールに解放されるとき、アプリケーション・モジュールは状態を受動化しません。このメソッドは現在のリクエストの現在のアプリケーション・モジュール・インスタンスにのみ適用されることに注意してください。その後、アプリケーション・モジュールは非管理レベルでプールに解放され、参照されなくなり、リセットされます。そのアプリケーション・モジュールが次にクライアントによって使用される場合は、デフォルトにより再び管理レベルで使用されます。
注意: ユーザーが作業の論理単位を終了したことを通知する場合は、プログラムでアプリケーション・モジュールを非管理レベルにして解放できます。後で説明するように、 |
予約レベルを使用してアプリケーション・モジュールを解放するようデータ・コントロールを設定するには、DCJboDataControl
クラスのsetReleaseLevel()
メソッドを呼び出し(oracle.adf.model.bc4j
パッケージにあります)、整数定数ApplicationModule.RELEASE_LEVEL_RESERVED
を渡します。
アプリケーション・モジュールの解放レベルが予約に変更されると、明示的に変更されるまで、それ以降のすべてのリクエストに対して予約レベルのままになります。
アプリケーション・モジュールを予約レベルに設定した場合、DCJboDataControl
クラスのsetReleaseLevel()
メソッドを呼び出し、整数定数ApplicationModule.RELEASE_LEVEL_MANAGED
を渡すことで、管理レベルに戻すことができます。
例43-1では、JSFバッキングBeanのアクション・メソッドからUserModuleDataControl
という名前のデータ・コントロールでresetState()
メソッドを呼び出しています。
例43-1 JSFバッキングBeanのアクション・メソッドでのデータ・コントロールのresetState()の呼出し
package devguide.advanced.releasestateless.controller.backing; import devguide.advanced.releasestateless.controller.JSFUtils; import oracle.adf.model.BindingContext; import oracle.adf.model.binding.DCDataControl; /** * JSF Backing bean for the "Example.jspx" page */ public class Example { /** * In an action method, call resetState() on the data control to cause * it to release to the pool with the "unmanaged" release level. * In other words, as a stateless application module. */ public String commandButton_action() { // Add event code here... getDataControl("UserModuleDataControl").resetState(); return null; } private DCDataControl getDataControl(String name) { BindingContext bc = (BindingContext)JSFUtils.resolveExpression("#{data}"); return bc.findDataControl(name); } }
例43-2では、カスタムADFページ・フェーズ・リスナー・クラスを使用して、ADFライフサイクルのafter-prepareRenderフェーズからUserModuleDataControl
という名前のデータ・コントロールでresetState()
メソッドを呼び出しています。ページのページ定義のControllerClass
属性にこのクラスの完全修飾名を設定することで、このカスタム・クラスを特定のページと関連付けます。
例43-2 カスタムPagePhaseListenerでのデータ・コントロールのresetState()の呼出し
package devguide.advanced.releasestateless.controller; import oracle.adf.controller.v2.lifecycle.Lifecycle; import oracle.adf.controller.v2.lifecycle.PagePhaseEvent; import oracle.adf.controller.v2.lifecycle.PagePhaseListener; import oracle.adf.model.binding.DCDataControl; public class ReleaseStatelessPagePhaseListener implements PagePhaseListener { /** * In the "after" phase of the final "prepareRender" ADF Lifecycle * phase, call resetState() on the data control to cause it to release * to the pool with the "unmanaged" release level. In other words, * as a stateless application module. * * @param event ADF page phase event */ public void afterPhase(PagePhaseEvent event) { if (event.getPhaseId() == Lifecycle.PREPARE_RENDER_ID) { getDataControl("UserModuleDataControl", event).resetState(); } } // Required to implement the PagePhaseListener interface public void beforePhase(PagePhaseEvent event) {} private DCDataControl getDataControl(String name, PagePhaseEvent event) { return event.getLifecycleContext() .getBindingContext() .findDataControl(name); } }
例43-3では、ADFページ・コントローラ・クラスのオーバーライドされたprepareRender()
メソッドからUserModuleDataControl
という名前のデータ・コントロールでresetState()
メソッドを呼び出しています。ページのページ定義のControllerClass
属性にこのクラスの完全修飾名を設定することで、このカスタム・クラスを特定のページと関連付けます。
注意: カスタム |
例43-3 カスタムADF PageControllerでのデータ・コントロールのresetState()の呼出し
package devguide.advanced.releasestateless.controller; import oracle.adf.controller.v2.context.LifecycleContext; import oracle.adf.controller.v2.lifecycle.PageController; import oracle.adf.controller.v2.lifecycle.PagePhaseEvent; import oracle.adf.model.binding.DCDataControl; public class ReleaseStatelessPageController extends PageController { /** * After calling the super in the final prepareRender() phase * of the ADF Lifecycle, call resetState() on the data control * to cause it to release to the pool with the "unmanaged" * release level. In other words, as a stateless application module. * * @param lcCtx ADF lifecycle context */ public void prepareRender(LifecycleContext lcCtx) { super.prepareRender(lcCtx); getDataControl("UserModuleDataControl", lcCtx).resetState(); } private DCDataControl getDataControl(String name, LifecycleContext lcCtx) { return lcCtx.getBindingContext().findDataControl(name); } }
すべてのリクエストを完全にステートレスな方法で処理するFusion Webアプリケーションを作成する場合は、例43-4
で示すように、グローバル・カスタムPageLifecycleクラスを使用します。カスタム・ライフサイクルを使用するためのアプリケーションの構成方法の詳細は、25.2項「JSFページ・ライフサイクルとADFページ・ライフサイクルについて」を参照してください。
例43-4 カスタムADF PageLifecycleでのデータ・コントロールのresetState()の呼出し
package devguide.advanced.releasestateless.controller; import oracle.adf.controller.faces.lifecycle.FacesPageLifecycle; import oracle.adf.controller.v2.context.LifecycleContext; import oracle.adf.model.binding.DCDataControl; public class ReleaseStatelessPageLifecycle extends FacesPageLifecycle { /** * After calling the super in the final prepareRender() phase * of the ADF Lifecycle, call resetState() on the data control * to cause it to release to the pool with the "unmanaged" * release level. In other words, as a stateless application module. * * @param lcCtx ADF lifecycle context */ public void prepareRender(LifecycleContext lcCtx) { super.prepareRender(lcCtx); getDataControl("UserModuleDataControl", lcCtx).resetState(); } private DCDataControl getDataControl(String name, LifecycleContext lcCtx) { return lcCtx.getBindingContext().findDataControl(name); } }
アプリケーション・モデルの受動化によって保存される情報は、トランザクション状態と非トランザクション状態の2つの部分に分かれます。トランザクション状態は、エンティティ・オブジェクト・データに対して行われた、データベースへの保存が意図されている更新のセットであり、エンティティ・オブジェクトに対して直接実行されるか、ビュー・オブジェクトの行を介してエンティティに実行されます。非トランザクション状態は、現在の行インデックス、WHERE
句、ORDER BY
句など、ビュー・オブジェクトの実行時設定で構成されます。
アプリケーション・モジュールの受動化スナップショットの一部として保存される情報には、次のものが含まれます。
このユーザー・セッションに対するルート・アプリケーション・モジュールのエンティティ・キャッシュにおける新しいエンティティ、変更されたエンティティおよび削除されたエンティティ(変更の場合は新旧の値を含みます)
アクティブ・ビュー・オブジェクトごとに次のもの(静的および動的に作成されたもの両方):
各行セットの現在の行インジケータ(通常は1)
新しい行とその位置。(新しい行は、更新された行とは別に扱われます。ビュー・オブジェクトにおけるそのインデックスも追跡されます。)
ViewCriteriaおよびビュー基準行などの関連するすべてのパラメータ。
行セットが実行されたかどうかを示すフラグ
範囲の開始と範囲のサイズ
アクセス・モード
フェッチ・モードとフェッチ・サイズ
すべてのビュー・オブジェクト・レベルのカスタム・データ
注意: 設計時に受動化のためにビュー・オブジェクト一時属性が選択されている場合、その属性を保存できます。ただし、これにより、取得される行数が多くなると、スナップショットのサイズも大きくなっていくため、この機能は慎重に使用してください。 |
動的に作成された場合、またはビュー定義から変更された場合は、SELECT
、FROM
、WHERE
、ORDER BY
の各句
注意: ADF Business Componentsのランタイム診断を有効にしている場合は、各XML状態スナップショットの内容も保存されます。診断を有効にする方法の詳細は、6.3.8項「ADF Business Componentsデバッグ診断を有効化する方法」を参照してください。 |
デフォルトでは、受動化スナップショットはデータベースに保存されますが、かわりにファイル・システムを使用するよう構成できます。
受動化されたXMLスナップショットは、jbo.server.internal_connection
プロパティで指定されている接続を使用して、PS_TXN
という名前の表のBLOB
列に書き込まれます。受動化レコードが保存されるたびに、受動化レコードには、PS_TXN_SEQ
順序から取得された順序番号に基づいて、一意の受動化スナップショットIDが割り当てられます。ADFバインディング・コンテキストのアプリケーション・モジュール・データ・コントロールによって保持されるADFセッションCookieは、かわりに作成された最新の受動化スナップショットIDと、使用された前回のIDを記憶しています。
ADFランタイムは、PS_TXN
表およびPS_TXN_SEQ
順序の作成に使用する必要のあるデータベース接続およびスキーマを制御するjbo.server.internal_connection
という名前の構成プロパティを認識します。この構成パラメータの値が明示的に設定されていない場合、状態管理機能は、現在のアプリケーション・データベース接続の資格証明を使用して、一時的な表を作成します。
一時的な情報を別に保持するため、状態管理機能は、データベース接続プールから別の接続インスタンスを使用しますが、データベース資格証明は現在のユーザーと同じです。フレームワークは、一時的な表を作成し、存在しない場合には順序を作成することがあるため、jbo.server.internal_connection
の値を設定しないということは、現在のデータベース・ユーザーが、CREATE TABLE
、CREATE INDEX
およびCREATE SEQUENCE
の各権限を持つ必要があることを意味します。これは望ましくない場合が多いため、jbo.server.internal_connection
プロパティには常に適切な値を指定し、表とスキーマが作成される状態管理スキーマに対して資格証明を提供することをお薦めします。構成のjbo.server.internal_connection
プロパティに対する有効な値は、次のとおりです。
次のような、完全修飾されたJDBC接続URL:
jdbc:oracle:thin:
username
/
password
@
host
:
port
:
SID
次のような、JDBCデータソース名:
java:/comp/env/jdbc/YourJavaEEDataSourceName
パフォーマンスに関するヒント:
|
受動化された情報は、複数の場所に格納できます。格納場所は、プログラムで制御することも、またはアプリケーション・モジュール構成でオプションを構成して制御することもできます。選択肢は、データベースまたはローカル・ファイル・システムに格納されるファイルです。
ファイル
ファイルにアクセスする方がデータベースにアクセスするより速いため、これは使用できる最も速い選択肢である場合があります。中間層全体(1つまたは複数のOracle Application Serverのインストールおよびそのすべてのサーバー・インスタンス)が同じマシンにインストールされているか、共通の共有ファイル・システムにアクセスできる場合に、受動化された情報にすべてからアクセス可能であれば、これは適切な選択です。通常、この選択肢は、使用されているOracle Application Serverが1台である小さい中間層に適しています。つまり、Oracle Application Serverが1台で、そのすべてのコンポーネントが1台の物理マシンにインストールされているような小型の中間層には、非常にふさわしい選択です。永続スナップショット・ファイルの場所と名前は、jbo.tmpdir
プロパティ(指定されている場合)によって決まります。この指定は、構成プロパティに関する通常のADFプロパティ優先順位の規則に従います。他になにも指定されていない場合、場所はuser.dir
によって決まります(指定されている場合)。これはデフォルトのプロパティであり、OS固有です。
データベース
これがデフォルトの選択です。ファイルへの受動化より少し遅くなる場合がありますが、信頼性ははるかに優れています。ファイルへの受動化でよくある問題は、リモートでインストールされているOracle Application Serverインスタンスがアクセスできないことです。この場合、クラスタ環境では、1つのノードがダウンすると、他のノードは受動化された情報にアクセスできず、フェイルオーバーが機能しません。可能性のあるもう1つの問題は、リモート・ノードがファイルにアクセスできたとしても、ローカル・ノードとリモート・ノードでアクセス時間が大きく異なり、パフォーマンスが一定でなくなります。データベース・アクセスの場合、時間はすべてのノードでほぼ同じです。
設計時に選択した値を設定するには、jbo.passivationstore
プロパティにdatabase
またはfile
を設定します。値null
は、接続の種類に固有のデフォルトを使用することを示します。OracleまたはDB2の場合はデータベースによる受動化を使用し、それ以外はファイル・シリアライズを使用します。
保管方法をプログラムで設定するには、oracle.jbo.ApplicationModule
インタフェースのsetStoreForPassiveState()
メソッドを使用します。指定できるパラメータ値は次のとおりです。
PASSIVATE_TO_DATABASE
PASSIVATE_TO_FILE
通常の環境では、ADF状態管理機能は、受動化スナップショット・レコードを自動的にクリーンアップします。
前述のとおり、セッションCookieのかわりに受動化レコードがデータベースに保存されたとき、この受動化レコードには新しい一意のスナップショットIDが設定されます。同じトランザクションの一部として、その同じセッションCookieによって使用されていた前のスナップショットIDを持つ受動化レコードは削除されます。これにより、サーバー障害がなければ、受動化スナップショット・レコードはアクティブなエンド・ユーザー・セッションごとに1つのみ存在します。
セッションCookieに関係する受動化スナップショット・レコードは、アプリケーション・モジュールが非管理状態レベルでプールにチェックインされると削除されます。これは次の場合に発生します。
コードがアプリケーション・モジュール・データ・コントロールでresetState()
を明示的に呼び出す場合
たとえば明示的なログアウト機能の実装の一部などとして、コードがHttpSession
を明示的に無効化する場合
アイドル時間に対するセッション・タイムアウトのしきい値を超え、フェイルオーバー・モードが無効(デフォルト)になっているため、HttpSession
がタイムアウトする場合
いずれの場合も、アプリケーション・モジュール・プールは、セッションCookieによって参照されていたアプリケーション・モジュールも参照されていない状態にリセットします。基礎となるデータベース表には変更が保存されていないため、保留中のセッション状態スナップショットが削除されると、その時点までにユーザー・セッションが完了した未終了作業のトレースは残りません。
フェイルオーバー・モードが有効になっていると、セッションが非アクティブなためにHttpSession
がタイムアウトした場合、ユーザーがブラウザに戻ったときに作業を再開できるよう、受動化スナップショットが維持されます。
アクションが中断された後、エンド・ユーザーがブラウザに戻ってアプリケーションの使用を続けると、なにも変化がなかったかのように作業は継続されます。セッションCookieは、リクエストを処理する前のユーザーの最新の保留中状態スナップショットで、使用可能なアプリケーション・モジュール・インスタンスを再能動化するために使用されます。これにより、ユーザーの次のリクエストが新しいHttpSession
のコンテキストで処理されても(別のアプリケーション・サーバー・インスタンスであっても)、ユーザーはこのことに気付きません。
注意: アプリケーション・モジュールが予約レベルで解放された場合は、 |
JDeveloperでは、アプリケーション・モジュール状態管理表を定期的にクリーンアップするための手段として、adfbc_purge_statesnapshots.sql
スクリプトが提供されています。このファイルは、Oracle Middlewareインストール・ディレクトリのoracle_common
サブディレクトリにあります(例: ORACLE_HOME
\oracle_common\common\sql
)。
サーバーが開発時や障害時などに異常な方法でシャットダウンすると、永続的スナップショット・レコードが時間とともに蓄積します。SQL*Plusでスクリプトを実行すると、BC4J_CLEANUP
PL/SQLパッケージが作成されます。このパッケージには、2つの関連するプロシージャがあります。
PROCEDURE Session_State(olderThan DATE)
このプロシージャは、特定の日付より古いセッションのアプリケーション・モジュール・セッション状態記憶域をクリーンアップします。
PROCEDURE Session_State(olderThan_minutes INTEGER)
このプロシージャは、特定の分数より古いセッションのアプリケーション・モジュール・セッション状態記憶域をクリーンアップします。
このパッケージの適切なプロシージャの呼出しをデータベース・ジョブとして発行することで、ADFの一時的永続記憶域の定期的なクリーンアップをスケジュールできます。
例43-5で示すような匿名PL/SQLブロックを使用することで、bc4j_cleanup.session_state()
の実行を、翌日の午前2時に開始して以降は毎日実行し、状態が1日(1440分)を超えているセッションをクリーンアップするようスケジュールできます。
例43-5 状態管理表の定期的クリーンアップのスケジュール
SET SERVEROUTPUT ON DECLARE jobId BINARY_INTEGER; firstRun DATE; BEGIN -- Start the job tomorrow at 2am firstRun := TO_DATE(TO_CHAR(SYSDATE+1,'DD-MON-YYYY')||' 02:00', 'DD-MON-YYYY HH24:MI'); -- Submit the job, indicating it should repeat once a day dbms_job.submit(job => jobId, -- Run the ADF Purge Script for Session State -- to cleanup sessions older than 1 day (1440 minutes) what => 'bc4j_cleanup.session_state(1440);', next_date => firstRun, -- When completed, automatically reschedule -- for 1 day later interval => 'SYSDATE + 1' ); dbms_output.put_line('Successfully submitted job. Job Id is '||jobId); END; . /
HTTPはステートレス・プロトコルであるため、クライアントがブラウザを閉じたり週末で不在になったことの暗黙の通知を、サーバーが受け取ることはありません。このため、Java EEに準拠するサーバーは、ユーザーがリクエスを停止したときにHTTPセッションに結び付けられているリソースを解放できるよう、標準的で構成可能なセッション・タイムアウトのメカニズムを提供します。プログラムでタイムアウトを強制することもできます。
web.xml
ファイルのsession-timeoutタグを使用して、セッションのタイムアウトのしきい値を構成できます。デフォルト値は35分です。HttpSession
がタイムアウトすると、BindingContext
はスコープ外となり、それとともに、アプリケーション・モジュールを参照していたすべてのデータ・コントロールは、管理状態レベルでプールに解放されます。アプリケーション・モジュール・プールは、参照されていたアプリケーション・モジュールをリセットし、インスタンスを再び参照されていない状態にします。
セッション・タイムアウトの期限が切れる前にユーザーのセッションを終了するには、ユーザーがログアウトのボタンやリンクをクリックした際に、バッキングBeanからHttpSession
オブジェクトのinvalidate()
メソッドを呼び出します。これにより、セッションがタイムアウトした場合と同じ方法でHttpSession
がクリーンアップされます。JSFとADFを使用して、セッションを無効化した後は、単に転送を行うのではなく、表示する次のページへのリダイレクトを行う必要があります。例43-6は、「ログアウト」ボタンからこのタスクを実行するサンプル・コードです。
例43-6 プログラムによるセッションの終了
public String logoutButton_action() throws IOException{ ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext(); HttpServletResponse response = (HttpServletResponse)ectx.getResponse(); HttpSession session = (HttpSession)ectx.getSession(false); session.invalidate(); response.sendRedirect("Welcome.jspx"); return null; }
暗黙のタイムアウトと同様に、HTTPセッションをこの方法でクリーンアップすると、参照されていたアプリケーション・モジュールは非参照としてマークされます。
メンバー変数の形式、またはoracle.jbo.Session
ユーザー・データ・ハッシュテーブルに格納されるカスタム情報の形式で、カスタム・ユーザー定義情報をアプリケーション・モジュールに追加することは、ごく一般的な方法です。ADF状態管理機能では、ApplicationModuleImpl
クラス内のpassivateState()
メソッド、およびactivateState()
メソッドまたはprepareForActivation()
メソッドのいずれかをオーバーライドすることで、このカスタム情報を受動化スナップショットにも保存するためのメカニズムが提供されています。
注意:
|
passivateState()
およびactivateState()
をオーバーライドして、カスタム・アプリケーション・モジュール状態情報を受動化/能動化サイクルに確実に組み込むことができます。例43-7に、これを行う方法を示します。
注意: ビュー・オブジェクトを能動化した後で、能動化プロセスの最後に、 |
例にあるjbo.counter
には、アプリケーション・モジュール状態の受動化と能動化の過程で保持するカスタム値が含まれています。各アプリケーション・モジュールにはoracle.jbo.Session
というオブジェクトが関連付けられており、アプリケーション・モジュール固有のセッションレベル状態を格納します。セッションにはユーザー・データ・ハッシュテーブルがあり、一時的な情報を格納できます。ユーザー固有のデータをアプリケーション・モジュールの受動化と能動化の過程で保持するには、このカスタム値をアプリケーション・モジュール状態の受動化スナップショットに保存してリストアするコードを記述する必要があります。
例43-7 状態スナップショットXMLドキュメントでのカスタム情報の受動化と能動化
/** * Overridden framework method to passivate custom XML elements * into the pending state snapshot document */ public void passivateState(Document doc, Element parent) { // 1. Retrieve the value of the value to save int counterValue = getCounterValue(); // 2. Create an XML element to contain the value Node node = doc.createElement(COUNTER); // 3. Create an XML text node to represent the value Node cNode = doc.createTextNode(Integer.toString(counterValue)); // 4. Append the text node as a child of the element node.appendChild(cNode); // 5. Append the element to the parent element passed in parent.appendChild(node); } /** * Overridden framework method to activate custom XML elements * into the pending state snapshot document */ public void activateState(Element elem) { super.activateState(elem); if (elem != null) { // 1. Search the element for any <jbo.counter> elements NodeList nl = elem.getElementsByTagName(COUNTER); if (nl != null) { // 2. If any found, loop over the nodes found for (int i=0, length = nl.getLength(); i < length; i++) { // 3. Get first child node of the <jbo.counter> element Node child = nl.item(i).getFirstChild(); if (child != null) { // 4. Set the counter value to the activated value 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";
例43-7では、activateState()
がオーバーライドされると、次の手順が実行されます。
jbo.counter
要素の要素を検索します。
見つかった場合は、ノード・リストで見つかったノードをループします。
jbo.counter
要素の最初の子ノードを取得します。
これはDOM Textノードで、その値は前述のpassivateState()
メソッドが呼び出されたときに保存された、jbo.counter
属性の値を表す文字列です。
カウンタの値に、スナップショットから能動化された値を設定します。
passivateState()
がオーバーライドされると、次の手順で逆の処理を実行します。
保存する値を取得します。
値を収めるXML要素を作成します。
値を表すためのXMLテキスト・ノードを作成します。
テキスト・ノードを要素の子として追加します。
渡される親要素に要素を追加します。
注意: XMLドキュメントでノードを操作するために使用するAPIは、 |
デフォルトでは、すべてのビュー・オブジェクトが受動化可能に設定されており、これによりそれらの状態が保存されます。ただし、一時属性を持つビュー・オブジェクトは、デフォルトではそれらの属性を受動化しません。ビュー・オブジェクトの概要エディタの「チューニング」ページを使用して、ビュー・オブジェクトの受動化方法、さらにどの属性が受動化されるかを変更できます。
ビュー・オブジェクトごとに、宣言的に受動化可能または不可能に構成できます。ビュー・オブジェクトが受動化可能ではない場合は、ビュー・オブジェクトに関する情報はアプリケーション・モジュールの受動化スナップショットに書き込まれません。
パフォーマンスに関するヒント: 読取り専用のビュー・オブジェクトは、更新されず、XML定義から簡単に再作成できるため、受動化する必要がありません。これにより、受動化と能動化に関連するパフォーマンスのオーバーヘッドが取り除かれ、アプリケーション・モジュール・プールを維持するために必要なCPU使用率も減少します。 |
ビュー・オブジェクトの受動化状態を設定する手順:
アプリケーション・ナビゲータで、ビュー・オブジェクトをダブルクリックし、概要エディタでそれを開きます。
「一般」ページで、「チューニング」セクションを展開します。
「状態の受動化」を選択してビュー・オブジェクトのデータの保存を確認します。
このとき、オプションで「すべての一時属性を含む」を選択してすべての一時属性を受動化できますが、詳細は43.8.4項「一時ビュー・オブジェクトの受動化について」を参照してください。
能動化メカニズムは、ビュー・オブジェクトを、最後の受動化が発生したときの状態に戻すように設計されています。それを保証するために、Oracle ADFは、最後の問合せ実行で使用されたバインド変数の値を状態スナップショットに格納します。このバインド変数は、受動化時に行セットに設定された変数に追加されるものです。受動化状態も、ユーザーが適用したWHERE句を、受動化時の行セットに関連するビュー・オブジェクトに格納します。
一時ビュー・オブジェクト(一時属性のみを含むビュー・オブジェクト)は受動化可能に設定されていますが、ビュー・オブジェクトがデフォルトで受動化される設定になっているため、現在行およびその他の非トランザクション状態に関連する情報のみを受動化します。
パフォーマンスに関するヒント: 一時ビュー・オブジェクトの属性は、デフォルトでは受動化されません。その性質上、一時ビュー・オブジェクトの属性は読取り専用として意図されていることが普通であり、簡単に再作成できます。そのため、通常は、その値をXMLスナップショットの一部として受動化しても意味がありません。これにより、受動化と能動化に関連するパフォーマンスのオーバーヘッドが取り除かれ、アプリケーション・モジュール・プールを維持するために必要なCPU使用率も減少します。 |
一時ビュー・オブジェクトの属性の受動化状態を個別に設定する手順:
アプリケーション・ナビゲータで、ビュー・オブジェクトをダブルクリックし、概要エディタでそれを開きます。
「属性」ページで、受動化する一時属性を選択し、「詳細」タブをクリックします。
「詳細」セクションで、「受動化」チェック・ボックスを選択します。
トランザクション機能はエンティティ・オブジェクト・レベルで管理されることが普通であるため、一時ビュー・オブジェクト属性の受動化はリソースやパフォーマンスの点で効率が低下します。一時ビュー・オブジェクトはエンティティ・オブジェクトに基づいていないため、すべての更新は、エンティティ・キャッシュではなく、ビュー・オブジェクト行キャッシュで管理されます。したがって、一時ビュー・オブジェクトまたは一時ビュー・オブジェクト属性を受動化するには、特別なランタイム処理が必要になります。
通常、受動化では変更された値のみを保存しますが、一時ビュー・オブジェクトの受動化では行全体を保存する必要があります。行には、受動化として設定されたビュー・オブジェクト属性のみが含まれます。
受動化を使用すると、ビュー・オブジェクトを使用して、1つまたは複数のグローバル変数を、それぞれ別の一時属性に格納することができます。一時属性を受動化済としてマークすると、ADF Business Componentsフレームワークでは、高スループットとフェイルオーバーを実現するシナリオで受動化と能動化が発生しても、その一時的な値が保持されます。このため、低効率のHTTPセッション・レプリケーションでなく、状態管理メカニズムによりバックアップされるセッション・レベルのグローバル値を実装するほうが簡単です。また、必要に応じてUIのコントロールにバインドすることも簡単になります。
様々な画面の起動方法において、値を保存するには2つの基本的な方法があります。1つはコントローラ中心、もう1つはモデル中心の方法です。
ADFコントローラでのタスクの実装
コントローラ中心の方法により、ページ・フロー・スコープの属性を使用した値の保存および参照が行われます。この方法は、いずれかのADF Business Componentsの実装によってグローバル値を内部で参照する必要がない場合に適しています。
ページ・フロー・スコープの詳細は、18.2.4項「タスク・フロー用のメモリー・スコープについて」を参照してください。
ADF MODELでのタスクの実装
モデル中心の方法では、概念的にはOracle Formsの非データベース・ブロックと等しい一時ビュー・オブジェクトが作成されます。
5.2.1項「エンティティ・ベースのビュー・オブジェクトの作成方法」を参照して、ビュー・オブジェクト・ウィザードを使用して新規ビュー・オブジェクトを作成します。
ウィザードのステップ1では、「問合せベースではなく、プログラムによって移入された行」のオプションを選択します。
ステップ2では、「新規」をクリックして一時属性名、およびビューオブジェクトに含まれるタイプを定義します。「更新可能」オプションは、必ず「常に」に設定します。
「終了」をクリックすると、概要エディタに新規作成されたビュー・オブジェクトが表示されます。
ビュー・オブジェクトでの問合せの実行を無効にします。
概要エディタの「一般」ページで、「チューニング」セクションを展開し、「データベースから取得」グループ・ボックスで行なしオプションを選択します。
ビュー・オブジェクトのデータがロールバック操作中にクリアされないよう注意してください。これを行うには、ビュー・オブジェクトのカスタムJavaクラスを有効にして、2つのロールバック・メソッドをオーバーライドします。
概要エディタの「Java」ページで、「Javaクラス」セクションの「編集」アイコンをクリックして、「Java」ダイアログを開きます。
「Java」ダイアログで「ビュー・オブジェクト・クラスの生成」を選択して、「OK」をクリックします。
概要エディタでは、「Javaクラス」セクションの「ビュー・オブジェクト・クラス」の隣のハイパーリンクをクリックすると、ソース・エディタが開きます。
「ソース」メニューで「メソッドのオーバーライド」を選択します。
「メソッドのオーバーライド」ダイアログで、オーバーライドするbeforeRollback()
メソッドおよびafterRollback()
メソッドを選択し、次に「OK」をクリックします。
beforeRollback()
メソッドおよびafterRollback()
メソッドの両方で、Javaコードでsuper
のコールをコメント・アウトします。
9.2.3.2項「既存のアプリケーション・モジュールへのマスター/ディテール・ビュー・オブジェクト・インスタンスの追加」に示すとおり、一時ビュー・オブジェクトのインスタンスをアプリケーション・モジュールのデータ・モデルに追加します。
新規ユーザーがアプリケーション・モジュールの使用を開始した場合は、ビュー・オブジェクトに空の行を作成します。
アプリケーション・モジュールのJavaクラスがない場合は、これを有効にします。
9.11.1項「組込みフレームワーク・メソッドのオーバーライド方法」に示すとおり、アプリケーション・モジュールのprepareSession()
メソッドをオーバーライドします。
super.prepareSession()
をコールした後、新規行を一時ビュー・オブジェクトに作成するコードを追加し、これをビュー・オブジェクトに挿入します。
これで、データ・コントロール・パネルを使用して、読取り専用要素および更新可能UI要素を、その他のビュー・オブジェクトと同様に、グローバルなビュー・オブジェクト属性にバインドできます。
データベース・サーバーでは、トランザクション全体をロールバックするのではなく、開発者がトランザクション内の特定のポイントまでロールバックできるセーブポイント機能にはなじみがあると思われます。アプリケーション・モジュールも同じ機能を提供しますが、中間層に実装されています。
ベスト・プラクティス: Oracle ADFでは、22.7項「タスク・フローでのセーブポイントの使用」に示すとおり、セーブポイントを処理する宣言的な方法が提供されます。宣言的な方法がニーズを満たさない場合のみ、43.9.1項「セーブポイントに対する状態管理の使用」に示すプログラム的な方法を使用します。 |
状態管理を使用して中間層セーブポイントを実装するには、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)
これらのメソッドを使用すると、名前付きスナップショットのスタックを作成し、名前を指定してスタックから保留中トランザクションの状態をリストアできます。これらのスナップショットは、トランザクションの期間(たとえば、コミットまたはロールバックのイベントまで)を超えては存在しないことに注意してください。この機能を使用すると、変更を元に戻したりやりなおしたりといったアプリケーションの複雑な機能を開発できます。この機能を有効に利用するもう1つの用途は、ブラウザの「戻る」ボタンと「進む」ボタンをアプリケーション固有の方法で動作するようにすることです。それ以外では、これらのメソッドを単純に使用するだけでも、十分に便利な場合があります。
保留状態を受動化スナップショットから能動化したときにアプリケーション・モジュールが機能することを明確にテストしていない場合、システムに高い負荷がかかることによってシステムのこの面が初めてテストされたときに、本番環境で予期せず不愉快な経験をする可能性があります。
jbo.ampool.doampooling
構成プロパティは、ビジネス・コンポーネントの構成ダイアログの「プーリングおよびスケーラビリティ」タブの「アプリケーション・モジュール・プーリングの有効化」オプションに対応しています。デフォルトでは、このチェック・ボックスは選択されていて、アプリケーション・モジュール・プーリングは有効になっています。本番環境でアプリケーションをデプロイする場合は、常にjbo.ampool.doampooling
をデフォルト設定のtrue
にしてアプリケーションを実行します。ただし、テスト環境でアプリケーションを実行するかぎり、テストでは、このプロパティをfalse
に設定することで重要な役割を果せます。このプロパティがfalseの場合、実質的にアプリケーション・プールはありません。リクエストの最後でアプリケーション・モジュール・インスタンスが解放されると、ただちに削除されます。同じユーザー・セッションによって行われる以降のリクエストでは、リクエストを処理するために新しいアプリケーション・モジュール・インスタンスを作成する必要があり、アプリケーション・モジュールの保留状態を受動化ストアから再能動化する必要があります。
全体的なテスト計画の一部として、jbo.ampool.doampooling
構成パラメータをfalse
に設定した状態でアプリケーション・モジュールをテストする方法を採用することをお薦めします。このように設定すると、アプリケーション・モジュール・プーリングは完全に無効になり、ページ・リクエストがあるたびに、システムは受動化スナップショットからアプリケーション・モジュールの保留状態を能動化するように強制されます。これは、カスタム・アプリケーション・コードで行われている想定のために本番環境で発生する可能性のある問題を検出する優れた方法です。
注意: テストが終了し、アプリケーションを本番環境でデプロイする準備が整ったら、アプリケーション・モジュールのプーリングを再度有効化することが重要です。構成プロパティ |
たとえば、受動化されていると確信している一時ビュー・オブジェクト属性がある場合、この手法を使用すると、意図したとおりに動作しているかどうかをテストできます。さらに、次のものを導入している状況を考えてみます。
アプリケーション・モジュール、ビュー・オブジェクトまたはエンティティ・オブジェクトのプライベート・メンバー・フィールド
Session
ユーザー・データ・ハッシュテーブルのカスタム・ユーザー・セッション状態
カスタム・コードでは、このカスタム状態がHTTPリクエスト間で維持されているものとされている場合があります。JDeveloper Integrated WebLogic Serverを使用して1人のユーザーでテストしている場合、または少数のユーザーでテストしている場合は、問題なく動いているように見えます。これは、ADFアプリケーション・モジュール・プールのアフィニティによるステートレスの最適化によるものです。システムの負荷が許せば、プールは後のリクエストでもユーザーに同じアプリケーション・モジュール・インスタンスを返し続けます。しかし、負荷が大きい、現実の使用においては、この最適化を実現できない場合があり、使用できる任意のアプリケーション・モジュール・インスタンスを取得して、受動化スナップショットから保留状態を再能動化することが必要になります。カスタム・コンポーネントの状態を受動化スナップショットに保存してそこからリロードするためのpassivateState()
とactivateState()
のオーバーライド(43.7項「カスタム・ユーザー固有情報の管理」の説明を参照)が正しく行われていないと、この再能動化ステップの後でカスタム状態が失われます(つまり、null
になるか、デフォルト値に戻ります)。jbo.ampool.doampooling
をfalseに設定してテストすることで、コードに存在するこの種の状況を素早く検出できます。
ADFの状態管理メカニズムは、受動化と能動化を利用して、アプリケーション・モジュール・インスタンスの状態を管理します。この機能の安定した実装は、すべての保留中の変更が中間層のアプリケーション・モジュール・トランザクションによって管理される場合にのみ可能です。最もスケーラブルな戦略は、保留中の変更を中間層のオブジェクトに維持し、HTTPリクエストの間で保留データベース状態が存在するような操作を実行しないようにすることです。これにより、アプリケーション・モジュール・プールが提供するパフォーマンスの最適化を最大限に利用し、アプリケーションにとって最も安定したランタイム動作を実現できます。
jbo.doconnectionpooling
構成パラメータをtrue
に設定する場合(一般に、データベース接続の共通プールを複数のアプリケーション・モジュール・プールで共有するため)、アプリケーション・モジュールをアプリケーション・モジュール・プールに解放すると、そのJDBC接続が解放されてデータベース接続プールに戻され、その接続でROLLBACK
が発行されます。その結果、ポストされていてコミットされていなかったすべての変更が失われます。次のリクエストで、アプリケーション・モジュールが使用されると、プールからJDBC接続を受け取りますが、前回使用していたものとは異なるJDBC接続インスタンスである可能性があります。これにより、データベースにポストされ、前のリクエストの間にコミットされていなかった変更は、失われます。
注意:
|
Webアプリケーションのデフォルト・モードであるオプティミスティック・ロックを使用することをお薦めします。ペシミスティック・ロックは、行レベル・ロックの形でデータベースに保留トランザクション状態を作り出すため、Webアプリケーションでは使用しないようにする必要があります。ペシミスティック・ロックを設定しても状態管理は動作しますが、ロック・モードは予想したほどのパフォーマンスになりません。裏側では、アプリケーション・モジュールがリサイクルされるたびに、JDBC接続でロールバックが発行されます。これにより、ペシミスティック・ロックが作成していたすべてのロックが解放されます。
パフォーマンスに関するヒント: 常に、Webアプリケーションのデフォルト・モードであるオプティミスティック・ロックを使用します。オプティミスティック・ロックのみが、アプリケーション・モジュールの非管理解放レベル・モードと互換性があり、この場合、Webページが終了すると、アプリケーション・モジュール・インスタンスをすぐに解放することができます。これにより、多くのユーザーがアプリケーションに同時にアクセスすることが見込まれるWebアプリケーションで、最高レベルのパフォーマンスが実現されます。 |
構成がオプティミスティック・ロックを使用していることを確認するには、ビジネス・コンポーネントの構成ダイアログの「プロパティ」タブを開き、jbo.locking.mode
プロパティの値がoptimistic
またはoptupdate
になっていることを確認します。
注意: ビジネス・コンポーネントの構成ダイアログを開くには、アプリケーション・ナビゲータでアプリケーション・モジュールを右クリックし、コンテキスト・メニューから「構成」を選択します。続いて「構成の管理」ダイアログで、編集する構成を選択し、「編集」をクリックします。 |
オプティミスティック・ロック(optimistic
)では、SELECT FOR UPDATE
文を発行して行をロックし、更新識別子属性を比較することによって、行が別のユーザーによって変更されているかどうかを判定します。また、更新識別子が指定されていない場合は、エンティティ・オブジェクトがキャッシュにフェッチされたときに存在していた、現在のエンティティのすべての永続的な属性の値を比較します。
オプティミスティック・ロック(optupdate
)では、ロックは実行されません。UPDATE
文が、現在のエンティティ・オブジェクトがフェッチされた時から属性値が変わっていない場合にかぎり既存の行と一致するWHERE
句を含むことによって、行が別のユーザーによって更新されたかどうかを判定します。
検証前の変更をコミットせず、トランザクションから強制的に送信するようなトランザクション・レベルのpostChanges()
メソッドが存在します。このメソッドは、同一のHTTPリクエスト内でのトランザクションのコミットまたはロールバックが必ず実行される場合を除き、Webアプリケーションでの使用はお薦めしません。使用した場合、複数の異なるクライアントにより、アプリケーション・モジュールとデータベース接続の両方がプールされ、シリアルに共有されるという事態が環境内で発生する可能性があります。
なんらかの理由で、postChanges()
メソッドを呼び出して、またはPL/SQLのストアド・プロシージャを呼び出して、リクエストの間にデータベースにトランザクション状態を作成する必要があり、同じリクエストの終わりまでにコミットまたはロールバックを発行できない場合は、後のリクエストでコミットまたはロールバックするまでの間に、そのリクエストから予約レベルでアプリケーション・モジュール・インスタンスを解放する必要があります。
パフォーマンスに関するヒント: データベースでのトランザクション状態の作成から最後のコミットまたはロールバックの実行までを可能なかぎり短くして使用します。これは、予約レベルがアプリケーションのスケーラビリティと信頼性に悪影響を及ぼすため、予約レベルを長時間使用する必要がないようにします。 |
アプリケーション・モジュールを予約レベルで解放すると、予約レベルを明示的に管理レベルまたは非管理レベルに変更して戻すまで、以降のすべてのリクエストは予約レベルのままになります。したがって、コミットまたはロールバックを発行したら、責任を持って予約レベルを管理レベルに戻す必要があります。
詳細は、43.4項「実行時におけるアプリケーション・モジュールの解放レベルの設定」を参照してください。