Oracle Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11gリリース1 (11.1.1.7.0) B52028-05 |
|
前 |
次 |
ここでは、Fusion WebアプリケーションでのADFモデル・レイヤーおよびADF Facesコンポーネントとアクティブ・データ・サービス(ADS)の併用について説明します。
この章の内容は次のとおりです。
Fusionテクノロジ・スタックにはアクティブ・データ・サービス(ADS)が含まれます。これは、ADF Facesコンポーネントに対してリアルタイムでデータ更新を提供するためのサーバー側のプッシュ・フレームワークです。ADF Facesコンポーネントをデータ・ソースにバインドすると、ブラウザ・クライアントが明示的に要求しなくても、ADSにより、このデータ更新がブラウザ・クライアントにプッシュされます。たとえば、ADFデータ・コントロールの属性にバインドされた表があるとします。この表の値はサーバー上で定期的に変更されるため、更新された値を表に反映する必要があります。サーバー上でデータが変更されたときには必ず、ADFモデル・レイヤーがコンポーネントに通知し、コンポーネントが変更後のデータを再レンダリングして、新しくなった値をハイライトするように(図42-1参照)アプリケーションとコンポーネントを構成することができます。
ADF FacesコンポーネントにバインドされているADFデータ・コントロールに関連付けられたビジネス・ロジックの結果として、バックエンドで変化するデータをレンダリングするには、自動部分ページ・レンダリング(PPR)のかわりにADSを使用します。自動PPRでは(通常、ユーザーが)サーバーに要求を送信する必要がありますが、ADSは、変更されたデータがサーバーに到着したときに、このデータをデータ・ストアからプッシュできるようにします。また、PPRとは対照的に、ADSにより、コンポーネントはコンポーネント全体ではなく、変更されたデータのみをレンダリングできるようになります。したがって、アプリケーションが、定期的に変化するデータに対応する必要がある場合にはADSが理想的です。
この機能を使用するには、ADSを使用するようにアプリケーションを構築する必要があります。アプリケーション・サービスがADSをサポートしていない場合は、サービスのプロキシを作成して、データがソースで更新されたときに、コンポーネントがこのデータを表示できるようにする必要もあります。
ADF FacesページはどれでもADSを使用できます。ただし、アクティブ・データと連動するように構成できるのは、次のADF Facesコンポーネントのみです。
activeCommandToolbarButton
activeImage
activeOutputText
table
tree
treeTable
DVTグラフ、ゲージ、および地理マップのコンポーネント
ADS対応フレームワークの制限事項は次のとおりです。
ADSは、PPRを使用して動的に追加された、ADF Facesコンポーネントのアクティブ・データをサポートしません。
ADSは、タスク・フロー・コール・アクティビティから呼び出されたWebページまたはリージョンに表示されるADF Facesコンポーネントのアクティブ・データをサポートしません。アクティブ・データが要求されたときは、タスク・フロー・コール・アクティビティを、バインド・タスク・フローとともに使用しないでください。
ADSは、フィルタリングが有効化されているADF Faces表コンポーネントのアクティブ・データをサポートしません。実行時に表をフィルタリングすると、アクティブ・データは表示されません。
ADSのフレームワークには、ソースからUIコンポーネントにアクティブ・データを送信するために提携しあう、多数のコンポーネントが含まれます。データ・イベントが発生したとき、関連するADFモデル・レイヤー・バインディングがアクティブ・データ用に構成されている場合、アクティブ・データ・モデルにより、データがイベント・マネージャに配信されます。その後、イベント・マネージャはデータを取得し、プッシュ・サービスを呼び出します。プッシュ・サービスは、その構成内容(詳細は42.1.3項「データ・トランスポート・モード」参照)に基づいて、このデータを適切なコンポーネントに配信します。その後、このコンポーネントはサーバーからプッシュされた新しいデータを適用します。図42-2に、ADSフレームワークを示します。
アクティブ・データ・サービスを使用するには、データが変化したときにイベントを公開するデータ・ストアが必要です。また、このようなイベントに反応するビジネス・サービスを作成する必要もあります。デフォルトでは、ADFビジネス・コンポーネントは、データ・イベントには反応しません。アクティブ・データ・プロキシ・フレームワークでは、ADFビジネス・コンポーネントを含むあらゆるタイプのデータ・ソースとADSを提携させることができます。このフレームワークはActiveDataModel
をJSFモデルと組み合せます。したがって、ActiveDataModel
とJSFモデルの両方ではなく、このプロキシのみの機能をオーバーライドする必要があります。
ADSフレームワークは次のアイテムにより構成されています。
ActiveDataModel
インタフェース: アクティブ・データ・モデルを抽象化したもの。その役割は次のとおりです。
アクティブ・データの開始と停止
現在のアクティブ・データのイベントIDを追跡
モデルがアクティブ・データを必要としているかどうかをレンダラに通知
イベント・マネージャ: ADFモデル・レイヤーと提携するサーバー側コンポーネント。次の役割があります。
バインディング・イベントのリスニング
アクティブ・データの取得
アクティブ・データ・エンコーディングの管理
エンコード済みアクティブ・データを送信するためにプッシュ・サービスを起動
プッシュ・サービス: 配信チャネルの1つで、サーバー側のイベント・マネージャ、およびクライアント側のアクティブ・データ・マネージャと対話します。その機能は次のとおりです。
サーバーとクライアントの間の接続を確立し維持
サーバーからクライアントへ、この接続を経由してアクティブ・データを転送
望ましいパラメータの範囲内でアクティブ・データが配信されることを保証し、配信されない場合はコンポーネントの更新を強行
アクティブ・データ・マネージャ: クライアント側コンポーネントの1つで、アクティブ・データを適切なコンポーネントに配信します。特に次の役割があります。
チャネル経由でサーバー側からやってきたイベントを、イベント配信サービスを使用して配信
共有チャネルを通じて複数のブラウザ・ウィンドウに対応
コンポーネントが変更を適宜、レンダリングできるように、リッチ・クライアント・コンポーネントにアクティブ・データ・イベントをディスパッチ
アクティブ・データ・プロキシ: すべてのタイプのデータ・ソースがプッシュ機能を有効化できるようにするプロキシプロキシには特に次のような役割があります。
ActiveDataModel
機能の実装と委譲
JSFモデルへの委譲
データ・ソースからデータ変更イベントをリスニング
データ変更イベントに基づいてアクティブ・データ・イベントを生成
アクティブ・データは、データ・ストリーミング(プッシュ)、または2種類のデータ・ポーリングのいずれか1つを用いて、クライアントに送信されます。データ・ストリーミングを使用する場合、要求は1つのみで、これは開かれたままになります。データ変更イベントが発生した場合、部分的な応答が送信され(この応答はクローズされない)、クライアントに通知が送られて、関連するコンポーネントが更新されて、新しいデータが表示されます(図42-3を参照)。
データ・ポーリングでは、アプリケーションは指定された間隔でデータ・ソースをポーリングするように構成されています(図42-4を参照)。データ変更イベントが発生したかどうかに関係なく、応答は要求ごとに送信され、閉じられます。データが変更された場合は、クライアントに通知が送られ、コンポーネントが更新されます。
ロング・ポーリングはストリーミングに似ています。ページがレンダリングされるとき、アクティブ・チャネルに要求が送信されます。しかし、データ変更イベントが発生するまで、応答は返されません。このイベントが発生した時点で、接続が閉じられます。接続が閉じられると同時に、新しい接続が開始されます。これにより、ほとんど常時接続されていることになります。特定のインターバルはありません。ロング・ポーリングの結果、データ変更イベントが起きたときに、その大半が受信されます。これは、接続が常時確立され、クライアントに応答を送信できる体制が整っているからです(図42-5参照)。
詳細は、42.2.2項「トランスポート・モードについて」を参照してください。
ADSを使用するには、アプリケーションを構成して、データ・トランスポート方法などのパフォーマンス・オプションを決定する必要があります。また、コンポーネントがADSを使用できるように、コンポーネントのバインディングを構成する必要もあります。ADFビジネス・コンポーネントを使用している場合、アクティブ・データ・プロキシを使用し、アクティブ・データ・イベントのリスナーとしてモデル自身を登録するために、ActiveModel
インタフェースを実装するようにモデルを修正する必要があります。
ヒント: OracleのOracle Business Activity Monitoring (BAM)は、アクティブ・データ・ストアを作成する能力を提供する完成されたソリューションです。詳細は、『Oracle Fusion Middleware Oracle SOA Suite開発者ガイド』を参照してください。 |
データ・トランスポート・モードを決定すると同時に、待機しきい値や再接続情報など、その他の構成を行うようにADSを構成する必要があります。
JDeveloperおよび統合WebLogic ServerでADSを使ってFusion Webアプリケーションを実行するための構成は、adf-config.xml
ファイルで行われます。adf-config.xml
ファイル(必要に応じて、このファイルを作成する方法を含む)の詳細は、『Oracle Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド』でadf-config.xmlの構成に関する項を参照してください。
アクティブ・データ・サービスを構成するには:
「アプリケーション・リソース」パネルで、adf-config.xmlファイルをダブルクリックして開きます。
「ソース」タブをクリックして、ソース・エディタでこのファイルを開き、表42-1に示す要素1つにつき1個のエントリを作成します。
表42-1 adf-config.xmlのADS構成要素
要素 | 説明 | デフォルト値(ミリ秒) | 最小値(ミリ秒) |
---|---|---|---|
|
データがクライアントに送信される方法。有効な値は次のとおりです。
詳細は、42.2.2項「トランスポート・モードについて」を参照してください。 |
||
|
待機しきい値(ミリ秒単位)。このしきい値よりもネットワーク遅延が大きいアクティブなデータ・メッセージは遅延とみなされます。 |
10000 |
1000 |
|
イベントが生成されていないときに、キープアライブ・メッセージを送信する頻度(ミリ秒単位)。 |
10000 |
5000 |
|
|
5000 |
1000 |
|
クライアントが接続を切断された場合に、サーバーへのプッシュ・チャンネルの再接続の試行を続ける最大期間(ミリ秒単位)。 |
1800000 |
0 |
|
再接続の試行の間の間隔(ミリ秒単位)。 |
10000 |
1000 |
パフォーマンスに関するヒント: ADSを構成するときには、次の点に注意してください。
|
例42-1はクライアントにプッシュされたコンテンツを持つサンプル構成を示しています。
表42-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構成に対するエントリを持つプロパティ・ファイルを作成します。
メイン・メニューから「ファイル」→「新規」の順に選択します。
「新規ギャラリ」で、「一般」カテゴリ、「ファイル」の順に選択し、「OK」をクリックします。
ファイルにadf-config.properties
という名前を付け、META-INF\services
ディレクトリに保存します。
次の行を追加します。
http\://xmlns.oracle.com/adf/activedata/config=oracle.adfinternal.view. faces.activedata.ActiveDataConfiguration$ActiveDataConfigCallback
ファイルを保存して閉じます。
データ・サーバーのクロックとアプリケーション・サーバーのクロックを同期します。これらが同期されていない場合、ADSはイベントが未来に発生するものと解釈する可能性があります。
ADSは3種類のトランスポート・モード(ストリーミング、ポーリング、またはロング・ポーリング)の1つを使用していコンポーネントにアクティブ・データを配信します。
ストリーミング・モードを使用するようにADSを構成すると、データにより変更イベントが発生するたびに、このデータがクライアントにプッシュされます。クライアント側では、プッシュ・チャネルの確立後、latency-threshold
要素とkeep-alive-interval
要素の合計時間以内にアクティビティが何も発生しなかった場合、reconnect-wait-time
要素の値に基づいて、経過時間がmax-reconnect-attempt-time
要素の値に到達するまで、establish-channel-request
が繰り返し送信されます。その後、接続は切断されているものとみなされます。たとえば、例42-1のように値が指定されている場合、15,000ミリ秒間にアクティビティが何も行われていない場合、チャネルの確立要求が、最高90,000ミリ秒の間、8,000ミリ秒に1回の間隔で送信されます。その後、接続は切断されているものとみなされます。
サーバー側では、サーバーにより、プッシュ・チャネルが切断され、空のアクティブ・データ・メッセージがある、またはアクティブ・データ・メッセージの送信に失敗したときに、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を構成した場合、要求はストリーミング・モードと同じように行われます。ただし、接続が切断と判断されると同時に、新しい接続が開始されます。その結果、待機時間が大幅に短縮されます。
表42-2はこれら3種類のモードを比較したものです。
表42-2 ストリーミング、ポーリングおよびロング・ポーリング・モードの比較
ストリーミング | ポーリング | ロング・ポーリング | |
---|---|---|---|
待機時間 |
非常に良好。 サーバー側でイベントが発生した直後、クライアントに部分的な応答が送信される。別のイベントが発生した場合も、即座に部分的な応答が送信される。このアプローチには待機時間はほとんどない。 |
よくない(ただし、ポーリング間隔による)。 ポーリング間隔が短い(たとえば、0.5秒)場合、接続が繰り返し開かれるため、ネットワークの速度が低下する原因となる。また、クライアント側リソースおよびサーバー側リソースが大量に消費される。 |
良い。 ただし、応答がクローズされた直後に新しいイベントが発生したとき、新しいデータがクライアント側に現れるまでに多少の待機時間を要する。ほとんどの場合、これは問題にはならない。 |
HTTPプロキシ |
良くない。 一部の古いサーバーでは応答が絶対にリリースされないため、クライアントとサーバーの間にプロキシが存在するとき、このプロキシが応答をバッファする可能性がある。 これはリアルタイムのデータがブラウザに流入することを妨げる、不適切な最適化である。プロキシを使用している場合は、ロング・ポーリングを使用すべきである。 |
良い。 |
良い。 |
ライブ接続数 |
良くない。 ストリームが常に有効であるため、多数の接続が同時に存在する。 |
良い。 接続は、実際のポーリング中のみ有効である。ただし、ポーリング・レートが高い場合、同時接続数も増えますので注意してください。 |
良くない。 ストリームがほとんど常に有効であるため、多数の接続が同時に存在する。 |
通信チャネル |
この結果、「ビジー」カーソル、またはブラウザの「スロバー」アイコンのアニメーションが表示される。 |
|
|
ADSの使用に向けてコンポーネントを構成する方法は、アクティブ・データ・プロキシを使用する必要があるかどうかによって異なります。データが変更されたときにイベントを公開するデータ・ストアをアプリケーションが使用しているときに、ビジネス・サービスがこのようなイベントに反応する(たとえば、アプリケーションがBAMを使用している)場合、アクティブ・データ・プロキシを使用する必要はありません。
ビジネス・サービスがこれらのイベントに反応しない(たとえば、アプリケーションがADFビジネス・コンポーネントを使用している)場合は、アクティブ・データ・プロキシを使用し、別の手順に従って、コンポーネントを構成する必要があります。
注意: ビジネス・サービスでアクティブ・データ・プロキシを使用する必要がある場合、アクティブ・データとともに使用できるのは次のコンポーネントのみです。
|
プロキシを使わずにADSを使用するには、対応するページ定義ファイルでバインディング要素に値を設定する必要があります。
アクティブ・データ・プロキシを使わずに、アクティブ・データを表示するコンポーネントを構成するには:
JSFページ上にコンポーネントをドロップします。
ADFバインド・ツリー、またはツリー表を使用している場合は、次の点を確認する必要があります。
アクセッサが依然として、似たようなアクセッサにアクセスしているとしても、バインディングが同種のデータ(つまり、ルールは1つのみ)を表していること。
バインディング・ルールに含まれる属性は1つであること。
表がフィルタリングを使用していないこと。
42.3.3項「ADFツリーでのアクティブ・データの表示について」に記載されているとおり、ツリー・コンポーネントのnodeStamp
ファセットにあるのはoutputText
タグ1つのみで、それ以外のタグは含まれていないこと。
そのページの関連ページ定義ファイルを開きます。
ページ定義ファイルの構造ウィンドウで、コンポーネントにバインドされた属性を表すノードを選択します。プロパティ・インスペクタで、ChangeEventPolicy属性をPushに設定します。
ヒント: サーバーの状態を示すために、 |
プロキシとADSを併用するには、コンポーネントの値と、このプロキシを使用するデコレータ・クラスをバインドする必要があります。対応するページ定義ファイルでバインディング要素に値を設定するには、42.4項「アクティブ・データ・プロキシの使用」でこのクラスの詳細を参照してください。
アクティブ・データ・プロキシを使って、アクティブ・データを表示するコンポーネントを構成するには:
JSFページ上にコンポーネントをドロップします。
プロキシで使用するために作成するデコレータ・クラスにバインドされるように値属性を変更します。詳細は、42.4項「アクティブ・データ・プロキシの使用」を参照してください。
ADFバインド・ツリー、またはツリー表を使用している場合は、次の点を確認する必要があります。
アクセッサが依然として、似たようなアクセッサにアクセスしているとしても、バインディングが同種のデータ(つまり、ルールは1つのみ)を表していること。
バインディング・ルールに含まれる属性は1つであること。
表がフィルタリングを使用していないこと。
42.3.3項「ADFツリーでのアクティブ・データの表示について」に記載されているとおり、ツリー・コンポーネントのnodeStamp
ファセットにあるのはoutputText
タグ1つのみで、それ以外のタグは含まれていないこと。
ADF Facesツリー(または、ツリー表)コンポーネントを作成するときには、nodeStamp
ファセットを構成します。これはツリーの各ノードのデータを表示するために使用されるコンポーネントのホルダーです。各ノードが一度ずつ、すべてのノードで繰り返しレンダリング(スタンプ設定)されます。
このスタンプ設定動作のため、ADF Facesツリー・コンポーネントで子としてサポートされるのは、特定タイプのコンポーネントのみです。ツリー・コンポーネントがアクティブ・データ・ソースにバインドされていない場合、動作を持たないコンポーネントがすべてサポートされます。ただし、ADSを使用するツリーを構成する場合、例42-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クラスを作成します。例42-3に、表用に作成されたクラスを示します。
例42-3 デコレータ・クラスを拡張
package view; import oracle.adf.view.rich.model.ActiveCollectionModelDecorator; /** * 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 { }
モデルを戻すメソッドを実装します。例42-4に、getCollectionModel()
メソッドの実装を示します。これは、式言語(EL)に依存して、内部クラスへのタイプキャストが必要になる事態を回避します。このメソッドは、バインディング・コンテナからDepartmentsView1
コレクションを戻します。
例42-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
クラスを独自に実装した内部クラスを作成します。デコレータはこれをアクティブ・データの開始および停止、データ・ソースへの接続および接続解除に使用します。例42-5に示すとおり、このクラスは、データ読取り一貫性を維持するために、changeCount
APIも使用します。詳細は、42.4.1項「読取り一貫性について」を参照してください。
例42-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
クラスを戻すメソッドを実装します(例42-6参照)。
アクティブ・モデルへのデータの挿入または更新に使用できる、アプリケーション固有のイベントを作成するメソッドを作成します。
例42-7に示すtriggerDataChange()
メソッドは、アクティブ・モデル(MyActiveDataModel
のインスタンス)を使用して、データを挿入および更新するためのActiveDataUpdateEvent
オブジェクトを作成します。
例42-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 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 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 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
オブジェクトを1つずつ増やして、維持する必要があります。これにより、データのいずれかのchangeCount
が小さかった場合、アクティブ・データ・イベントはこのデータを破棄することができます。例42-7は、ActiveDataModel
クラスの実装を使用して、読取り一貫性を維持する方法を示しています。
コレクションベースのデータを表示するADFコンポーネントは、ADSと提携するように構成することができます。ビュー・レイヤーでは余分なセットアップは必要ありません。しかし、JSPXページで、Javaタイマーに基づき新しいカウントを表示するために、activeOutputText
コンポーネントを使用する例を考えてみましょう。この場合、モデル・レイヤーを、Java Beanから表示されるスカラー、つまり"フラットな"データで置き換えます。
スカラー・モデルを実装するには、例42-8に示す基本手順に従います。
このBean(スカラー・モデル)が実際のモデルを模倣するように、ActiveModelContext
APIを使用して、ADSとともにBeanを登録します。
データをビュー・レイヤーにプッシュするカスタム・メカニズムを実装します。
例42-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.event.ActiveDataEntry; import oracle.adf.view.rich.event.ActiveDataUpdateEvent; import oracle.adfinternal.view.faces.activedata.ActiveDataEventUtil; 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
ファイルに登録します(例42-9のcounterBean
を参照)。
例42-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コンポーネントでは、プッシュされたデータを受け取るために式言語が使用されます(例42-10のactiveOutputText
コンポーネントvalue
属性を参照)。
例42-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>