| Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11g リリース2(11.1.2.2.0) B69399-01 |
|
![]() 前 |
![]() 次 |
ここでは、Fusion WebアプリケーションでのADFモデル・レイヤーおよびADF Facesコンポーネントとアクティブ・データ・サービス(ADS)の併用について説明します。
この章の内容は次のとおりです。
Fusionテクノロジ・スタックにはアクティブ・データ・サービス(ADS)が含まれます。これは、ADF Facesコンポーネントに対してリアルタイムでデータ更新を提供するためのサーバー側のプッシュ・フレームワークです。ADF Facesコンポーネントをデータ・ソースにバインドすると、ADSでは、ブラウザ・クライアントに明示的な更新リクエストを要求することなく、ブラウザ・クライアントにデータ更新をプッシュします。たとえば、ADFデータ・コントロールの属性にバインドされた表があり、その属性値はサーバー上で定期的に変化するため、表には更新された値が表示されるようにします。サーバー上でデータが変更されたときには必ず、ADFモデル・レイヤーがコンポーネントに通知し、コンポーネントが変更後のデータを再レンダリングして、新しくなった値をハイライトするように(図45-1参照)アプリケーションとコンポーネントを構成できます。
ADF FacesコンポーネントにバインドされているADFデータ・コントロールに関連付けられたビジネス・ロジックの結果として、バックエンドで変化するデータをレンダリングするには、自動部分ページ・レンダリング(PPR)の代わりにADSを使用します。自動PPRでは(通常、ユーザーが)サーバーに要求を送信する必要がありますが、ADSは、変更されたデータがサーバーに到着したときに、このデータをデータ・ストアからプッシュできるようにします。また、PPRとは対照的に、ADSにより、コンポーネントはコンポーネント全体ではなく、変更されたデータのみをレンダリングできるようになります。したがって、アプリケーションが、定期的に変化するデータに対応する必要がある場合にはADSが理想的です。
この機能を使用するには、ADSを使用するようにアプリケーションを構築する必要があります。アプリケーション・サービスがADSをサポートしていない場合は、サービスのプロキシを作成して、データがソースで更新されたときに、コンポーネントがこのデータを表示できるようにする必要もあります。
どのADF FacesページでもADSを使用できます。ただし、アクティブ・データを処理するように構成できるのは、次のADF Facesコンポーネントのみです。
activeCommandToolbarButton
activeImage
activeOutputText
pivotTable
table
|
注意: アクティブ・データが使用される表ではフィルタリングを使用しないでください。表が実行時にフィルタ処理されると、アクティブ・データは表示できません。現在のところ、ADSでは、 |
tree
treeTable
DVTグラフ、ゲージ、および地理マップのコンポーネント
ADS対応フレームワークの制限事項は次のとおりです。
ADSは、PPRを使用して動的に追加された、ADF Facesコンポーネントのアクティブ・データをサポートしません。
ADSは、タスク・フロー・コール・アクティビティから呼び出されたWebページまたはリージョンに表示されるADF Facesコンポーネントのアクティブ・データをサポートしません。アクティブ・データが要求されたときは、タスク・フロー・コール・アクティビティを、バインド・タスク・フローとともに使用しないでください。
Webブラウザでポップアップ・ブロックが有効になっている場合、ADSはアクティブ・データをサポートしません。
アプリケーション用に構成されたADSでADF Facesページを実行するには、まず、Webブラウザのポップアップ・ブロックを無効化する必要があります。
ADSは、フィルタリングが有効化されているADF Faces表コンポーネントのアクティブ・データをサポートしません。実行時に表をフィルタリングすると、アクティブ・データは表示されません。
ADSのフレームワークには、ソースからUIコンポーネントにアクティブ・データを送信するために提携しあう、多数のコンポーネントが含まれます。データ・イベントが発生したとき、関連するADFモデル・レイヤー・バインディングがアクティブ・データ用に構成されている場合、アクティブ・データ・モデルにより、データがイベント・マネージャに配信されます。その後、イベント・マネージャはデータを取得し、プッシュ・サービスを呼び出します。プッシュ・サービスは、その構成内容(詳細は45.1.4項「データ・トランスポート・モード」参照)に基づいて、このデータを適切なコンポーネントに配信します。その後、このコンポーネントはサーバーからプッシュされた新しいデータを適用します。図45-2に、ADSフレームワークを示します。
アクティブ・データ・サービスを使用するには、データが変化したときにイベントを公開するデータ・ストアが必要です。また、このようなイベントに反応するビジネス・サービスを作成する必要もあります。デフォルトでは、ADFビジネス・コンポーネントは、データ・イベントには反応しません。アクティブ・データ・プロキシ・フレームワークでは、ADFビジネス・コンポーネントを含むあらゆるタイプのデータ・ソースとADSを提携させることができます。このフレームワークはActiveDataModelをJSFモデルと組み合せます。したがって、ActiveDataModelとJSFモデルの両方ではなく、このプロキシのみの機能をオーバーライドする必要があります。
ADSフレームワークは次のアイテムにより構成されています。
ActiveDataModelインタフェース: アクティブ・データ・モデルを抽象化したもの。その役割は次のとおりです。
アクティブ・データの開始と停止
現在のアクティブ・データのイベントIDを追跡
モデルがアクティブ・データを必要としているかどうかをレンダラに通知
イベント・マネージャ: ADFモデル・レイヤーと提携するサーバー側コンポーネント。次の役割があります。
バインディング・イベントのリスニング
アクティブ・データの取得
アクティブ・データ・エンコーディングの管理
エンコード済みアクティブ・データを送信するためにプッシュ・サービスを起動
プッシュ・サービス: 配信チャネルの1つで、サーバー側のイベント・マネージャ、およびクライアント側のアクティブ・データ・マネージャと対話します。その機能は次のとおりです。
サーバーとクライアントの間の接続を確立し維持
サーバーからクライアントへ、この接続を経由してアクティブ・データを転送
望ましいパラメータの範囲内でアクティブ・データが配信されることを保証し、配信されない場合はコンポーネントの更新を強行
アクティブ・データ・マネージャ: クライアント側コンポーネントの1つで、アクティブ・データを適切なコンポーネントに配信します。特に次の役割があります。
チャネル経由でサーバー側からやってきたイベントを、イベント配信サービスを使用して配信
共有チャネルを通じて複数のブラウザ・ウィンドウに対応
コンポーネントが変更を適宜、レンダリングできるように、リッチ・クライアント・コンポーネントにアクティブ・データ・イベントをディスパッチ
アクティブ・データ・プロキシ: すべてのタイプのデータ・ソースがプッシュ機能を有効化できるようにするプロキシプロキシには特に次のような役割があります。
ActiveDataModel機能の実装と委譲
JSFモデルへの委譲
データ・ソースからデータ変更イベントをリスニング
データ変更イベントに基づいてアクティブ・データ・イベントを生成
アクティブ・データは、データ・ストリーミング(プッシュ)、または2種類のデータ・ポーリングのいずれか1つを用いて、クライアントに送信されます。データ・ストリーミングを使用する場合、要求は1つのみで、これは開かれたままになります。データ変更イベントが発生した場合、部分的な応答が送信され(この応答はクローズされない)、クライアントに通知が送られて、関連するコンポーネントが更新されて、新しいデータが表示されます(図45-3を参照)。
データ・ポーリングでは、アプリケーションは指定された間隔でデータ・ソースをポーリングするように構成されています(図45-4を参照)。データ変更イベントが発生したかどうかに関係なく、応答は要求ごとに送信され、閉じられます。データが変更された場合は、クライアントに通知が送られ、コンポーネントが更新されます。
ロング・ポーリングはストリーミングに似ています。ページがレンダリングされるとき、アクティブ・チャネルに要求が送信されます。しかし、データ変更イベントが発生するまで、応答は返されません。このイベントが発生した時点で、接続が閉じられます。接続が閉じられると同時に、新しい接続が開始されます。これにより、ほとんど常時接続されていることになります。特定のインターバルはありません。ロング・ポーリングの結果、データ変更イベントが起きたときに、その大半が受信されます。これは、接続が常時確立され、クライアントに応答を送信できる体制が整っているからです(図45-5参照)。
詳細は、45.2.2項「トランスポート・モードについて」を参照してください。
ADSを使用するには、アプリケーションを構成して、データ・トランスポート方法などのパフォーマンス・オプションを決定する必要があります。また、コンポーネントがADSを使用できるように、コンポーネントのバインディングを構成する必要もあります。ADFビジネス・コンポーネントを使用している場合、アクティブ・データ・プロキシを使用し、アクティブ・データ・イベントのリスナーとしてモデル自身を登録するために、ActiveModelインタフェースを実装するようにモデルを修正する必要があります。
データ・トランスポート・モードを決定すると同時に、待機しきい値や再接続情報など、その他の構成を行うようにADSを構成する必要があります。
JDeveloperおよび統合WebLogic ServerでADSを使ってFusion Webアプリケーションを実行するための構成は、adf-config.xmlファイルで行われます。adf-config.xmlファイル(必要に応じて、このファイルを作成する方法を含む)の詳細は、『Oracle Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド』でadf-config.xmlの構成に関する項を参照してください。
|
注意: クラスタ環境内のWebアプリケーションに対して自動的なレプリケーションとフェイルオーバーをサポートするために、Oracle WebLogic Serverは、クラスタ間でHTTPセッションの状態をレプリケートするメカニズムをサポートしています。Oracle Application Development Framework(Oracle ADF)を構成して、Fusion Webアプリケーションの状態をサーバーからクラスタにリストアできます。アクティブ・データを表示するように構成されているページに対してフェイルオーバーをサポートするには、ADFビジネス・コンポーネント・アプリケーション・モジュールのフェイルオーバーを有効にし、ADF ControllerがADFメモリー・スコープへの変更を追跡できるようにする必要があります。これらのADF設定を構成すると、フェイルオーバーの発生時にADFサーバーがフェイルオーバーを検出し、アクティブ・データを表示するように構成されているすべてのページに対し、ページをリフレッシュするように要求します。その後でADSが再起動し、データがクライアントにプッシュされます。高可用性環境でFusion Webアプリケーションのフェイルオーバーを有効にする方法の詳細は、『Oracle Fusion Middleware高可用性ガイド』を参照してください。 |
アクティブ・データ・サービスを構成する手順:
「アプリケーション・リソース」パネルで、adf-config.xmlファイルをダブルクリックして開きます。
「ソース」タブをクリックして、ソース・エディタでこのファイルを開き、表45-1に示す要素1つにつき1個のエントリを作成します。
表45-1 adf-config.xmlのADS構成要素
| 要素 | 説明 | デフォルト値(ミリ秒) | 最小値(ミリ秒) |
|---|---|---|---|
|
|
データがクライアントに送信される方法。有効な値は次のとおりです。
詳細は、45.2.2項「トランスポート・モードについて」を参照してください。 |
||
|
|
待機しきい値(ミリ秒単位)。このしきい値よりもネットワーク遅延が大きいアクティブなデータ・メッセージは遅延とみなされます。 |
10000 |
1000 |
|
|
イベントが生成されていないときに、キープアライブ・メッセージを送信する頻度(ミリ秒単位)。 |
10000 |
5000 |
|
|
|
5000 |
1000 |
|
|
クライアントが接続を切断された場合に、サーバーへのプッシュ・チャンネルの再接続の試行を続ける最大期間(ミリ秒単位)。 |
1800000 |
0 |
|
|
再接続の試行の間の間隔(ミリ秒単位)。 |
10000 |
1000 |
|
パフォーマンスに関するヒント: ADSを構成するときには、次の点に注意してください。
|
例45-1はクライアントにプッシュされたコンテンツを持つサンプル構成を示しています。
表45-1 adf-config.xmlでのADS構成例
<?xml version="1.0" encoding="utf-8" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config"
xmlns:ads="http://xmlns.oracle.com/adf/activedata/config">
<ads:adf-activedata-config xmlns=
"http://xmlns.oracle.com/adf/activedata/config">
<transport>streaming</transport>
<latency-threshold>5000</latency-threshold>
<keep-alive-interval>10000</keep-alive-interval>
<max-reconnect-attempt-time>90000</max-reconnect-attempt-time>
<reconnect-wait-time>8000</reconnect-wait-time>
</ads:adf-activedata-config>
</adf-config>
データ・サーバーのクロックとアプリケーション・サーバーのクロックを同期します。これらが同期されていない場合、ADSはイベントが未来に発生するものと解釈する可能性があります。
ADSは3種類のトランスポート・モード(ストリーミング、ポーリング、またはロング・ポーリング)の1つを使用していコンポーネントにアクティブ・データを配信します。
ストリーミング・モードを使用するようにADSを構成すると、データにより変更イベントが発生するたびに、このデータがクライアントにプッシュされます。クライアント側では、プッシュ・チャネルの確立後、latency-threshold要素とkeep-alive-interval要素の合計時間以内にアクティビティが何も発生しなかった場合、reconnect-wait-time要素の値に基づいて、経過時間がmax-reconnect-attempt-time要素の値に到達するまで、establish-channel-requestが繰り返し送信されます。その後、接続は切断されているものとみなされます。たとえば、例45-1のように値が指定されている場合、15,000ミリ秒間にアクティビティが行われていない場合、チャネルの確立要求が、最高90,000ミリ秒の間、8,000ミリ秒間隔で送信されます。その後、接続は切断されているものとみなされます。
サーバー側では、サーバーにより、プッシュ・チャネルが切断され、空のアクティブ・データ・メッセージがある、またはアクティブ・データ・メッセージの送信に失敗したときに、cleanup-delay-timeを使ってクリーンアップするためのタイマーが起動されます。cleanup-delay-timeは、max-reconnect-attempt-time + 30 × 1000 msの式で求められます。デフォルト値は30分です。
ポーリング・モードを使用するようにADSを構成すると、クライアント側では、polling-interval要素の値に達した後で、ポーリング要求を繰り返し送信するようにスケジュールが設定されます。max-reconnect-attempt-timeだけの時間が経過しても応答が得られない場合、接続は切断されているものとして扱われ、それ以上、要求は送信されません。ポーリング応答の受信後、応答にかかった時間がpolling-interval要素を超える場合、サービスは次の要求を即座に送信します。超えていない場合、次の要求はスケジュールどおり送信されます。
サーバー側では、ポーリング応答が返された後で、セッションが終了します。その時点で、クリーンアップをトリガーするために、cleanup-delay-timeを使ったタイマーが設定されます。このタイマーの起動前に新しい要求が到着した場合、古いタイマーはキャンセルされ、新しいタイマーが作成されます。
ロング・ポーリング・モードを使用するようにADSを構成した場合、要求はストリーミング・モードと同じように行われます。ただし、接続が切断と判断されると同時に、新しい接続が開始されます。その結果、待機時間が大幅に短縮されます。
表45-2はこれら3種類のモードを比較したものです。
表45-2 ストリーミング、ポーリングおよびロング・ポーリング・モードの比較
| ストリーミング | ポーリング | ロング・ポーリング | |
|---|---|---|---|
|
待機時間 |
非常に良好。 サーバー側でイベントが発生した直後、クライアントに部分的な応答が送信される。別のイベントが発生した場合も、即座に部分的な応答が送信される。このアプローチには待機時間はほとんどない。 |
よくない(ただし、ポーリング間隔による)。 ポーリング間隔が短い(たとえば、0.5秒)場合、接続が繰り返し開かれるため、ネットワークの速度が低下する原因となる。また、クライアント側リソースおよびサーバー側リソースが大量に消費される。 |
良い。 ただし、応答がクローズされた直後に新しいイベントが発生したとき、新しいデータがクライアント側に現れるまでに多少の待機時間を要する。ほとんどの場合、これは問題にはならない。 |
|
HTTPプロキシ |
良くない。 一部の古いサーバーでは応答が絶対にリリースされないため、クライアントとサーバーの間にプロキシが存在するとき、このプロキシが応答をバッファする可能性がある。 これはリアルタイムのデータがブラウザに流入することを妨げる、不適切な最適化である。プロキシを使用している場合は、ロング・ポーリングを使用すべきである。 |
良い。 |
良い。 |
|
ライブ接続数 |
良くない。 ストリームが常に有効であるため、多数の接続が同時に存在する。 |
良い。 接続は、実際のポーリング中のみ有効である。ただし、ポーリング・レートが高い場合、同時接続数も増えますので注意してください。 |
良くない。 ストリームがほとんど常に有効であるため、多数の接続が同時に存在する。 |
|
通信チャネル |
この結果、ビジー・カーソル、またはブラウザのスロバー・アイコンのアニメーションが表示される。 |
|
|
ADSの使用に向けてコンポーネントを構成する方法は、アクティブ・データ・プロキシを使用する必要があるかどうかによって異なります。データが変更されたときにイベントを公開するデータ・ストアをアプリケーションが使用しているときに、ビジネス・サービスがこのようなイベントに反応する(たとえば、アプリケーションがBAMを使用している)場合、アクティブ・データ・プロキシを使用する必要はありません。
ビジネス・サービスがこれらのイベントに反応しない(たとえば、アプリケーションがADFビジネス・コンポーネントを使用している)場合は、アクティブ・データ・プロキシを使用し、別の手順に従って、コンポーネントを構成する必要があります。
|
注意: ビジネス・サービスでアクティブ・データ・プロキシを使用する必要がある場合、アクティブ・データとともに使用できるのは次のADF Facesコンポーネントのみです。
|
プロキシを使わずにADSを使用するには、対応するページ定義ファイルでバインディング要素に値を設定する必要があります。
アクティブ・データ・プロキシを使わずに、アクティブ・データを表示するコンポーネントを構成する手順:
JSFページ上にコンポーネントをドロップします。
ADFバインド・ツリー、またはツリー・テーブルを使用している場合は、次の点を確認する必要があります。
アクセッサが依然として、似たようなアクセッサにアクセスしているとしても、バインディングが同種のデータ(つまり、ルールは1つのみ)を表していること。
バインディング・ルールに含まれる属性は1つであること。
テーブルがフィルタリングを使用していないこと。
45.3.3項「ADFツリーでのアクティブ・データの表示について」に記載されているとおり、ツリー・コンポーネントのnodeStampファセットにあるのはoutputTextタグ1つのみで、それ以外のタグは含まれていないこと。
そのページの関連ページ定義ファイルを開きます。
ページ定義ファイルの構造ウィンドウで、コンポーネントにバインドされた属性を表すノードを選択します。プロパティ・インスペクタで、ChangeEventPolicy属性をPushに設定します。
|
ヒント: サーバーの状態を示すために、 |
プロキシとADSを併用するには、コンポーネントの値と、このプロキシを使用するデコレータ・クラスをバインドする必要があります。対応するページ定義ファイルでバインディング要素に値を設定するには、45.4項「アクティブ・データ・プロキシの使用」でこのクラスの詳細を参照してください。
アクティブ・データ・プロキシを使って、アクティブ・データを表示するコンポーネントを構成する手順:
JSFページ上にコンポーネントをドロップします。
プロキシで使用するために作成するデコレータ・クラスにバインドされるように値属性を変更します。詳細は、45.4項「アクティブ・データ・プロキシの使用」を参照してください。
ADFバインド・ツリー、またはツリー・テーブルを使用している場合は、次の点を確認する必要があります。
アクセッサが依然として、似たようなアクセッサにアクセスしているとしても、バインディングが同種のデータ(つまり、ルールは1つのみ)を表していること。
バインディング・ルールに含まれる属性は1つであること。
テーブルがフィルタリングを使用していないこと。
45.3.3項「ADFツリーでのアクティブ・データの表示について」に記載されているとおり、ツリー・コンポーネントのnodeStampファセットにあるのはoutputTextタグ1つのみで、それ以外のタグは含まれていないこと。
ADF Facesツリー(または、ツリー・テーブル)コンポーネントを作成するときには、nodeStampファセットを構成します。これはツリーの各ノードのデータを表示するために使用されるコンポーネントのホルダです。各ノードが一度ずつ、すべてのノードで繰り返しレンダリング(スタンプ設定)されます。
このスタンプ設定動作のため、ADF Facesツリー・コンポーネントで子としてサポートされるのは、特定タイプのコンポーネントのみです。ツリー・コンポーネントがアクティブ・データ・ソースにバインドされていない場合、動作を持たないコンポーネントがすべてサポートされます。ただし、ADSを使用するツリーを構成する場合、例45-2に示すとおり、nodeStampファセット内ではoutputTextコンポーネントのみがサポートされます。nodeStampファセットには他のタグが含まれていてはいけません。したがって、たとえば、ADSを使用するように構成されたツリーのnodeStampファセットにpanelGroupLayoutタグを追加した場合、アクティブ・データは機能しません。
アプリケーションとADSのコンポーネントの構成後、サーバーでデータが変化すると必ず、コンポーネントに通知が送られ、変更されたデータのみを使って再レンダリングが行われます。対照的に、アクティブ・データまたは自動PPRのどちら向けにも構成されていないコンポーネントは、データ変更後に明示的にリフレッシュする必要があります。明示的なリフレッシュは、変更されていないデータを含め、クライアントに表示されているデータすべてを再フェッチします。この結果、コンポーネント全体が強制的に再レンダリングされます。さらに、コンポーネントが(アクティブ・データ・ポリシー「Push」を使用して)アクティブ・データ・ソースにバインドされている場合、このコンポーネントによる再レンダリングが行われ、新しい値がハイライトされます。
Fusion WebアプリケーションがGoogle Chrome Webブラウザで実行されているときに、ユーザーが[Ctrl]+[N]キー(または[Ctrl]+[T]キー)を押して、新しいウィンドウ(またはタブ)を開き、元のウィンドウから新しいウィンドウにURLをコピーすると、元のウィンドウのアクティブ・データはストリーミングを停止します。Google Chromeのプロセス・モデルによれば、新しいブラウザ・ウィンドウは別のプロセスで作成され、両方のウィンドウは同じHTTPセッションを共有します。ただし、2つの異なるプロセスで作成されたブラウザ・ウィンドウは互いに通信できないため、クライアントとサーバーの間でADSが同期しなくなり、ADSのストリーミングが停止します。回避策として、複数のGoogle Chromeウィンドウでアクティブ・データを許可するには、元のウィンドウから新しいウィンドウにURLをコピーする前に、ユーザーは[Ctrl]キーと[Shift]キーと[N]キーを同時に押して、ブラウザ・ウィンドウを匿名モードで開きます(プライベート・ブラウジング)。[Ctrl]キーと[Shift]キーと[N]キーを同時に押すと、別のプロセスと別のHTTPセッションでウィンドウが開かれるため、ADSは2つのウィンドウを同期しようとしなくなり、ストリーミングは影響を受けません。
ビジネス・サービスがデータ・イベントに反応しないときには、アクティブ・データ・プロキシを使用します。ADFビジネス・コンポーネントに渡されたイベントに基づいてコンポーネントを更新する必要がある場合は、アクティブ・データ・プロキシを使用する必要があります。
|
ヒント: アプリケーションがビジネス・サービスにBAMを使用している場合、アクティブ・データ・プロキシを使用する必要はありません。 |
|
注意: ビジネス・サービスでアクティブ・データ・プロキシを使用する必要がある場合、アクティブ・データとともに使用できるのは次のコンポーネントのみです。
|
次のデコレータ・クラスの1つをサブクラスするJavaクラスを作成する必要があります。
ActiveCollectionModelDecoratorクラス
ActiveDataModelDecoratorクラス(グラフと使用)
ActiveGeoMapDataModelDecoratorクラス
ActiveGaugeDataModelDecoratorクラス
これらのクラスは、アクティブ・データ機能をActiveDataModelのデフォルト実装に委譲するラッパー・クラスです。ActiveDataModelクラスは、データ変更イベントをリッスンし、イベント・マネージャと対話します。具体的には、次の処理が行われます。
アクティブ・データおよびActiveDataModelオブジェクトを開始および停止し、データ・ソースへのリスナーの登録および登録解除を行います。
JSFモデル・インタフェースをラップします。たとえば、ActiveCollectionModelDecoratorクラスはCollectionModelクラスをラップします。
データ・ソースから、データ変更イベントに基づいてアクティブ・データ・イベントを生成します。
イベント・マネージャからリスナーを管理し、アクティブ・データ・イベントをイベント・マネージャにプッシュします。
アクティブ・データ・ソースのリスナーとして自分自身を登録し、データの送信先モデルを取得するこのJavaクラスにメソッドを実装する必要があります。
|
注意: アクティブ・データ・フレームワークは、ユーザー・プロファイルやセキュリティなどADF実行時コンテキストを必要とする複雑なビジネス・ロジックや変換をサポートしません。たとえば、このフレームワークはADFコンテキストのロケール依存値を変換し、ロケール固有の値を返すことはできません。 複雑なビジネス・ロジックの例として、たとえば、注文を |
作業を始める前に、次のようにします。
データ・ソースから非同期的にアクティブ・データ・イベントを起動するロジックを実装します。たとえば、このロジックとしては、データベースを更新するビジネス・プロセス、またはJMSから通知を受けるJMSクライアントなどが考えられます。
アクティブ・データ・サービスを使用する手順:
コンポーネントに適したデコレータ・クラスを拡張するJavaクラスを作成します。例45-3に、表用に作成されたクラスを示します。
例45-3 デコレータ・クラスの拡張
package view;
import oracle.adf.view.rich.model.ActiveCollectionModelDecorator;
import oracle.adf.view.rich.activedata.ActiveDataEventUtil;
import oracle.adf.view.rich.activedata.JboActiveDataEventUtil;
/**
* This code wraps the existing collection model in the page and implements the
ActiveDataModel interface to enable ADS for the existing page.
*/
public class DeptModel
extends ActiveCollectionModelDecorator
{
}
モデルを戻すメソッドを実装します。例45-4に、getCollectionModel()メソッドの実装を示します。これは、式言語(EL)に依存して、内部クラスへのタイプキャストが必要になる事態を回避します。このメソッドは、バインディング・コンテナからDepartmentsView1コレクションを戻します。
例45-4 モデルを戻す
public CollectionModel getCollectionModel()
{
if (_model == null)
{
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext el = fc.getELContext();
// This is EL to avoid typecasting to an Internal class.
ValueExpression ve = elFactory.createValueExpression(el,
"#{bindings.DepartmentsView1.collectionModel}", Object.class);
// Now GET the collectionModel
_model = (CollectionModel)ve.getValue(el);
}
return _model;
}
ActiveDataModelクラスを独自に実装した内部クラスを作成します。デコレータはこれをアクティブ・データの開始および停止、データ・ソースへの接続および接続解除に使用します。例45-5に示すとおり、このクラスは、データ読取り一貫性を維持するために、changeCount APIも使用します。詳細は、45.4.1項「読取り一貫性について」を参照してください。
例45-5 データ・ソースへの接続
public class MyActiveDataModel extends BaseActiveDataModel
{
protected void startActiveData(Collection<Object> rowKeys,
int startChangeCount)
{
_listenerCount.incrementAndGet();
if (_listenerCount.get() == 1)
{
System.out.println("start up");
Runnable dataChanger = new Runnable()
{
public void run()
{
System.out.println("MyThread starting.");
try
{
Thread.sleep(2000);
System.out.println("thread running");
triggerDataChange(MyActiveDataModel.this);
}
catch (Exception exc)
{
System.out.println("MyThread exceptioned out.");
}
System.out.println("MyThread terminating.");
}
};
Thread newThrd = new Thread(dataChanger);
newThrd.start();
}
}
protected void stopActiveData(Collection<Object> rowKeys)
{
_listenerCount.decrementAndGet();
if (_listenerCount.get() == 0)
{
System.out.println("tear down");
}
}
public int getCurrentChangeCount()
{
return _currEventId.get();
}
public void bumpChangeCount()
{
_currEventId.incrementAndGet();
}
public void dataChanged(ActiveDataUpdateEvent event)
{
fireActiveDataUpdate(event);
}
private final AtomicInteger _listenerCount = new AtomicInteger(0);
private final AtomicInteger _currEventId = new AtomicInteger();
}
ActiveDataModelクラスを戻すメソッドを実装します(例45-6参照)。
アクティブ・モデルへのデータの挿入または更新に使用できる、アプリケーション固有のイベントを作成するメソッドを作成します。
例45-7に示すtriggerDataChange()メソッドは、アクティブ・モデル(MyActiveDataModelのインスタンス)を使用して、データを挿入および更新するためのActiveDataUpdateEventオブジェクトを作成します。このメソッドを実装するには、oracle.adf.view.rich.activedata.ActiveDataEventUtilおよびoracle.adf.view.rich.activedata.JboActiveDataEventUtilをインポートする必要があります。
例45-7 イベント・オブジェクトを作成してモデルを更新
public void triggerDataChange(MyActiveDataModel l)
throws Exception
{
// do an update on dept 1
l.bumpChangeCount();
ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
l.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Key
(new Object[]{ new Long(1), new Integer(0) })),
null,
new String[] { "name" },
new Object[] { "Name Pushed" });
l.dataChanged(event);
try
{
Thread.sleep(2000);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
// insert dept 99
l.bumpChangeCount();
event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.INSERT_AFTER,
l.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Key
(new Object[]{ new Long(99), new Integer(0) })),
JboActiveDataEventUtil.convertKeyPath(null),
new String[]{ "addr1", "name" },
new Object[]{ "Addr Inserted", "Name Inserted" });
l.dataChanged(event);
try
{
Thread.sleep(2000);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
// delete dept 10
l.bumpChangeCount();
event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.REMOVE,
l.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Key
(new Object[]{ new Long(9), new Integer(0) })),
null, null, null);
l.dataChanged(event);
try
{
Thread.sleep(2000);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
// refresh the entire table
l.bumpChangeCount();
event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.REFRESH,
l.getCurrentChangeCount(),
null, null, null, null);
l.dataChanged(event);
}
private MyActiveDataModel _activeDataModel = new MyActiveDataModel();
private CollectionModel _model = null;
}
アクティブ・データを使用するということは、コンポーネントに2つのデータ・ソース(アクティブ・データ・フィードと標準データ・フェッチ)があることを意味します。このため、アプリケーションでは読取り一貫性が確保されるようにする必要があります。
たとえば、ページには表が1つ含まれ、この表ではアクティブ・データが有効化されているとします。この表は、データを更新するための配信メソッドとして、標準の表データ・フェッチとアクティブ・データ・プッシュの2種類を持っています。ここで、バック・エンド・データがfooからbarに変更され、さらにfredに変更されたとしましょう。変更のたびに、アクティブ・データ・イベントが起動されます。これらのイベントがブラウザに到着する前に表をリフレッシュした場合、表にはfredと表示されます。これは、標準のデータ・フェッチでは、必ず最新のデータが取得されるからです。しかし、アクティブ・データ・イベントに長い時間がかかることもあるため、場合によっては、リフレッシュの後で、このデータ変更イベントの結果、fooがブラウザに到着し、表が更新されて、しばらくの間、fredの代わりにfooと表示されます。したがって、読取り一貫性を維持する方法を実装する必要があります。
読取り一貫性を実現するために、ActiveDataModelには、データのタイムスタンプを効果的に記録する変更カウントの概念があります。データ・フェッチとアクティブ・データ・プッシュのどちらも、このchangeCountオブジェクトを単調にカウントを増やすことで維持する必要があるため、返されたデータのchangeCountが現在のカウントより低い場合は、アクティブ・イベントにより廃棄される可能性があります。例45-7は、ActiveDataModelクラスの実装を使用して、読取り一貫性を維持する方法を示しています。
コレクションベースのデータを表示するADFコンポーネントは、ADSと提携するように構成することができます。ビュー・レイヤーでは余分なセットアップは必要ありません。しかし、JSPXページで、Javaタイマーに基づき新しいカウントを表示するために、activeOutputTextコンポーネントを使用する例を考えてみましょう。この場合、モデル・レイヤーを、JavaBeanから表示されるスカラー、つまり"フラットな"データで置き換えます。
スカラー・モデルを実装するには、例45-8に示す基本手順に従います。
このBean(スカラー・モデル)が実際のモデルを模倣するように、ActiveModelContext APIを使用して、ADSとともにBeanを登録します。
データをビュー・レイヤーにプッシュするカスタム・メカニズムを実装します。
例45-8 Java Beanにスカラー・モデルを実装
package oracle.adfdemo.view.feature.rich;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import oracle.adf.view.rich.activedata.ActiveModelContext;
import oracle.adf.view.rich.activedata.BaseActiveDataModel;
import oracle.adf.view.rich.activedata.ActiveDataEventUtil;
import oracle.adf.view.rich.event.ActiveDataEntry;
import oracle.adf.view.rich.event.ActiveDataUpdateEvent;
public class CounterBean extends BaseActiveDataModel
// Example using a Java timer to create new counts
{
public CounterBean()
// 1. Use the ActiveModelContext API to register the scalar model key path for
// the "state" attribute.
{
ActiveModelContext context = ActiveModelContext.getActiveModelContext();
Object[] keyPath = new String[0];
context.addActiveModelInfo(this, keyPath, "state");
timer.schedule(new UpdateTask(), 2000, 2000);
}
public String getState()
{
return String.valueOf(counter);
}
// Not needed. We do not need to connect to a (real) active data scource.
protected void startActiveData(Collection<Object> rowKeys, int
startChangeCount) {}
// Not needed. We do not need to connect to a (real) active data scource.
protected void stopActiveData(Collection<Object> rowKeys) {}
public int getCurrentChangeCount()
{
return counter.get();
}
protected class UpdateTask extends TimerTask
{
public void run()
{
counter.incrementAndGet();
// 2. Use ActiveDataEventUtil to create an event object to update the model.
ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(
ActiveDataEntry.ChangeType.UPDATE,
counter.get(),
new String[0],
null,
new String[] { "state" },
new Object[] { counter.get() });
fireActiveDataUpdate(event);
}
}
private static final Timer timer = new Timer();
private final AtomicInteger counter = new AtomicInteger(0);
}
Beanの作成後、これをマネージドBeanとしてfaces-config.xmlファイルに登録します(例45-9のcounterBeanを参照)。
例45-9 マネージドBeanの登録
...
<managed-bean>
<managed-bean-name>counterBean</managed-bean-name>
<managed-bean-class>
oracle.adfdemo.view.feature.rich.CounterBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Beanの登録が完了すると、ADSを使用して、データをビュー・レイヤーにストリーミングできるようになります。ADF Facesコンポーネントでは、プッシュされたデータを受け取るために式言語が使用されます(例45-10のactiveOutputTextコンポーネントvalue属性を参照)。
例45-10 アクティブ・データの表示
...
<f:view>
<af:document title="Active Data Visual Design Demo"
binding="#{templateBindings.documentComponent}"
smallIconSource="#{aboutBean.smallIconSource}"
largeIconSource="#{aboutBean.largeIconSource}" theme="dark"
id="d1">
<af:pageTemplate id="dmoTpl" viewId="#{templates.componentTemplate}">
<f:attribute name="tagName" value="Active Data Visual Design"/>
<f:attribute name="demoKind" value="visualDesign"/>
<f:attribute name="customEditorPresent" value="true"/>
<f:facet name="center">
<af:panelGroupLayout layout="scroll">
<af:activeOutputText
value="#{counterBean.state}"
inlineStyle=
"color:brown;
font-size:100px;
font-weight:bold;
text-align:center;"
/>
</af:panelGroupLayout>
</f:facet>
</af:pageTemplate>
</af:document>
</f:view>