プライマリ・コンテンツに移動
Oracle® Fusion Middleware Oracle ADF FacesによるWebユーザー・インタフェースの開発
12c (12.2.1.2.0)
E82917-02
目次へ移動
目次

前
次

41 非同期バックエンドでのアクティブ・データ・サービスの使用方法

この章では、ADF Facesコンポーネントに対してリアルタイムのデータ更新を行うために、アクティブ・データ・サービス(ADS)に非同期バックエンドを登録する方法を説明します。

この章の内容は次のとおりです。

41.1 アクティブ・データ・サービスについて

特定のADF Facesコンポーネントを構成すると、アクティブ・データ・サービス(ADS)からのデータを整理して、リアルタイムのデータ更新を実行できます。

Fusionテクノロジ・スタックには、アクティブ・データ・サービス(ADS)が含まれ、これはサーバー側のプッシュ・フレームワークで、これによりADF Facesコンポーネントのリアルタイムのデータ更新を提供できます。ADF Facesコンポーネントをデータ・ソースにバインドすると、ADSでは、ブラウザ・クライアントに明示的な更新リクエストを要求することなく、ブラウザ・クライアントにデータ更新をプッシュします。たとえば、ADFデータ・コントロールの属性にバインドされた表があり、その属性値はサーバー上で定期的に変化するため、表には更新された値が表示されるようにします。ユーザーはJava Beanを作成してActiveModelインタフェースを実装し、それをイベント・リスナーとして登録すると、コンポーネントにバックエンドからのデータ・イベントを通知できます。そのコンポーネントは図41-1に示されるように、新しい値が強調表示された変更済データを再レンダリングします。

図41-1 更新されたデータを強調表示で示す表

この図は周囲のテキストで説明しています

41.1.1 アクティブ・データ・サービスのユースケースおよび例

ADF FacesコンポーネントにバインドされているADFデータ・コントロールに関連付けられたビジネス・ロジックの結果として、バックエンドで変化するデータをレンダリングするには、自動部分ページ・レンダリング(PPR)のかわりにADSを使用します。自動PPRでは(通常、ユーザーが)サーバーに要求を送信する必要がありますが、ADSは、変更されたデータがサーバーに到着したときに、このデータをデータ・ストアからプッシュできるようにします。また、PPRとは対照的に、ADSにより、コンポーネントはコンポーネント全体ではなく、変更されたデータのみをレンダリングできるようになります。したがって、アプリケーションが、定期的に変化するデータに対応する必要がある場合にはADSが理想的です。

この機能を使用するには、ADSを使用するようにアプリケーションを構成する必要があります。アプリケーション・サービスがADSをサポートしていない場合は、サービスのプロキシを作成して、データがソースで更新されたときに、コンポーネントがこのデータを表示できるようにする必要もあります。

どのADF FacesページでもADSを使用できます。ただし、アクティブ・データを処理するように構成できるのは、次のADF FacesコンポーネントおよびADFデータ視覚化(DVT)コンポーネントのみです。

  • activeImage

  • activeOutputText

  • chart (すべてのタイプ)

  • gauge (すべてのタイプ)

  • pivotTable

  • tree

  • treeTable

  • geoMap (mapPointThemeのみ)

  • sunburst

  • treemap

DVTコンポーネントに対するADSサポートの詳細は、「アクティブ・データ・サポート」を参照してください。

また、コレクションベースのコンポーネント(tabletreepivotTableなど)は、outputTextコンポーネントまたはsparkChartがアクティブ・データを表示するように構成されている場合にのみADSをサポートします。その他のコンポーネントは、コレクションベース・コンポーネントの内部でサポートされません。

アクティブ・データ・サービス・フレームワークの詳細と構成に関する重要な情報は、Oracle Application Development FrameworkによるFusion Webアプリケーションの開発で、アクティブ・データ・サービスの使用を参照してください。

41.2 アクティブ・データ・サービスを使用するためのプロセスの概要

ADFアプリケーションでのアクティブ・データ・サービス(ADS)は、アプリケーション・レベルと、アクティブ・データの整理に使用するADF Facesコンポーネントのレベルで構成されます。

ADSを使用するために、必要に応じてアプリケーションを構成して、データ転送の方法やその他のパフォーマンス・オプションを決定することができます。

始める前に:

次のタスクを実行します。

  • データ・ソースから非同期的にアクティブ・データ・イベントを起動するロジックを実装します。たとえば、このロジックとしては、データベースを更新するビジネス・プロセス、またはJMSから通知を受けるJMSクライアントなどが考えられます。

  • アクティブ・データ・フレームワークは、ユーザー・プロファイルやセキュリティなどADF実行時コンテキストを必要とする複雑なビジネス・ロジックや変換をサポートしません。たとえば、このフレームワークはADFコンテキストのロケール依存値を変換し、ロケール固有の値を返すことはできません。そのかわりに、ユーザーは、データ変更イベントを公開する前に、データ・ソースにこれを処理させる必要があります。

  • ユーザーがADF Facesページをアプリケーション用に構成されたADSで実行できるようになるには、その前にWebブラウザのポップアップ・ブロッカを無効にする必要があります。ポップアップ・ブロッカが有効になっているWebブラウザでは、アクティブ・データはサポートされません。

アクティブ・データ・サービスを使用する手順:

  1. 必要に応じて、データ転送モードを決定し、待機しきい値や再接続情報など、その他の構成も設定するようにADSを構成します。ADSの構成はadf-config.xmlファイルで行われます。

    ADSの構成の詳細は、Oracle Application Development FrameworkによるFusion Webアプリケーションの開発で、アクティブ・データ・サービスを構成する方法を参照してください。

  2. オプションで、サーブレットのパラメータを構成し、ユーザーの非アクティブによりタイムアウトとなるまでのアクティブ・セッションの継続時間を指定します。クライアント側のサーブレットのタイムアウト・パラメータの構成は、web.xmlファイルで行われる。

    サーブレットのタイムアウト・パラメータの構成については、Oracle Application Development FrameworkによるFusion Webアプリケーションの開発で、アクティブ・データ・サービスのセッション・タイムアウトを構成する方法を参照してください。

  3. ActiveModelインタフェースを実装し、バックエンドからのアクティブ・データ・イベントのリスナーとして登録するバッキングBeanを作成します。
  4. イベント・オブジェクトをADSフレームワークに渡すようにBaseActiveDataModel APIを拡張するクラスを作成します。
  5. バックエンドからのデータ変更イベントのデータ変更リスナーを登録します。
  6. Webページで、プッシュされたデータを取得および表示するためのADFコンポーネントを実装するマネージドBeanを指定する式を追加して、プッシュされたデータを取得および表示するADF Facesコンポーネントを構成します。

41.3 マネージドBeanでのActiveModelインタフェースの実装

ADFアプリケーションのビュー・レイヤーでADF Facesコンポーネントへのデータを整理する場合は、ActiveModelインタフェースを実装するバッキングBeanを作成し、それをバックエンドからのアクティブ・データ・イベントのリスナーとして登録します。

プロパティとしてアクティブ・モデル実装を含むバッキングBeanを作成します。このクラスでは、JSFモデルをラップするのにADSデコレータ・クラスを使用します。このクラスでは、データをADSフレームワークにプッシュするバックエンドからのコールバックも実装されます。

次のADSデコレータ・クラスのいずれかをサブクラスとして持つJavaクラスを作成する必要があります。

  • ActiveCollectionModelDecoratorクラス

  • ActiveGeoMapDataModelDecoratorクラス

これらのクラスは、アクティブ・データ機能をActiveDataModelのデフォルト実装に委譲するラッパー・クラスです。ActiveDataModelクラスは、データ変更イベントをリッスンし、イベント・マネージャと対話します。

特に、ActiveModelインタフェースを実装するときには、次のことを完了します。

  • JSFモデル・インタフェースをラップします。たとえば、ActiveCollectionModelDecoratorクラスはCollectionModelクラスをラップします。

  • データ・ソースから、データ変更イベントに基づいてアクティブ・データ・イベントを生成します。

ActiveModelインタフェースを実装するには、データの送信先で、それ自体がアクティブ・データ・ソースのリスナーとして登録されるモデルを取得するJavaクラスに、メソッドを実装する必要があります(次の例を参照)。

  1. コンポーネントに適したデコレータ・クラスを拡張するJavaクラスを作成します。

    次の例に、ActiveCollectionModelDecoratorを拡張するStockManagerクラスを示します。このクラスでは、データがADF Faces tableコンポーネント用に表示されます。

  2. ActiveDataModelクラスを返すデコレータ・クラスのメソッドを実装し、スカラー・モデルを返すメソッドを実装します。

    次の例に、既存の非同期バックエンドに登録するgetCollectionModel()メソッドの実装を示しています。メッソッドにより、バックエンドから在庫コレクションのリストが返されます。

  3. アクティブ・モデル上でデータを挿入または更新するために使用できるアプリケーション固有のイベントを作成するメソッドを実装します。

    次の例に示すバックエンドからのonStockUpdate()コールバック・メソッドでは、アクティブ・モデル(ActiveStockModelのインスタンス)を使用して、データをADF FacesコンポーネントにプッシュするActiveDataUpdateEventオブジェクトを作成します。

    package sample.oracle.ads;
    
    import java.util.List;
    import sample.backend.IBackendListener;
    import sample.bean.StockBean;
    import sample.oracle.model.ActiveStockModel;
    
    import oracle.adf.view.rich.event.ActiveDataEntry;
    import oracle.adf.view.rich.event.ActiveDataUpdateEvent;
    import oracle.adf.view.rich.model.ActiveCollectionModelDecorator;
    import oracle.adf.view.rich.model.ActiveDataModel;
     
    import oracle.adfinternal.view.faces.activedata.ActiveDataEventUtil;
     
    import org.apache.myfaces.trinidad.model.CollectionModel;
    import org.apache.myfaces.trinidad.model.SortableModel;
     
    // 1. This example wraps the existing collection model in the page and implements
    //    the ActiveDataModel interface to enable ADS for the page.
    
    public StockManager extends ActiveCollectionModelDecorator implements 
                                                                 IBackendListener
    {
      // 2. Implement methods from ADF ActiveCollectionModelDecorator class to 
      //    return the model.
      @Override
      public ActiveDataModel getActiveDataModel()
      {
        return stockModel;
      }
     
      @Override
      protected CollectionModel getCollectionModel()
      {
        if(collectionModel == null)
        {
          // connect to a backend system to get a Collection
          List<StockBean> stocks = FacesUtil.loadBackEnd().getStocks();
          // make the collection become a (Trinidad) CollectionModel
          collectionModel = new SortableModel(stocks);
        }
     
        return collectionModel;
      }
     
      // 3. Implement a callback method to create active data events and deliver to 
      //    the ADS framework.
    
      /**
       * Callback from the backend to push new data to the decorator.
       * The decorator itself notifies the ADS system that there was a data change.
       * 
       * @param key the rowKey of the updated Stock
       * @param updatedStock the updated stock object
       */
      @Override
      public void onStockUpdate(Integer rowKey, StockBean stock)
      {
        ActiveStockModel asm = getActiveStockModel();
        
        // start the preparation for the ADS update
        asm.prepareDataChange();
     
        // Create an ADS event, using an _internal_ util.
        // This class is not part of the API
        ActiveDataUpdateEvent event = ActiveDataEventUtil.buildActiveDataUpdateEvent(
           ActiveDataEntry.ChangeType.UPDATE, // type
           asm.getCurrentChangeCount(), // changeCount
           new Object[] {rowKey}, // rowKey
           null, //insertKey, null as we don't insert stuff
           new String[] {"value"}, // attribute/property name that changes
           new Object[] { stock.getValue()}  // the payload for the above attribute
           );
     
        // Deliver the new Event object to the ADS framework
        asm.notifyDataChange(event);
     
      }
      
      /**
       * Typesafe caller for getActiveDataModel()
       * @return
       */
      protected ActiveStockModel getActiveStockModel()
      {
        return (ActiveStockModel) getActiveDataModel();
      }
     
      // properties
      private CollectionModel collectionModel; // see getCollectionModel()...
      private ActiveStockModel stockModel = new ActiveStockModel();
    }
    

クラスをマネージドBeanとしてfaces-config.xmlファイルに登録します。次の例では、StockManagerBeanの登録を示しています。マネージドBeanを定義することにより、ADF Facesコンポーネントの値プロパティの式でそのマネージドBeanを指定できます。

...
<managed-bean>
  <managed-bean-name>stockManager</managed-bean-name>
  <managed-bean-class>
      oracle.afdemo.view.feature.rich.StockManager
  </managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

41.4 読取り一貫性の維持に関する必知事項

読取り一貫性とは、ADFアプリケーションがアクティブなデータをプッシュするタイミングが、ADF Facesコンポーネントによるデータのリフレッシュより後に、かつアクティブなデータ・イベントがWebブラウザに届くより前になるよう保証する機能です。

アクティブ・データを使用するということは、コンポーネントに2つのデータ・ソース(アクティブ・データ・フィードと標準データ・フェッチ)があることを意味します。このため、アプリケーションでは読取り一貫性が確保されるようにする必要があります。

たとえば、ページには表が1つ含まれ、この表ではアクティブ・データが有効化されているとします。この表は、データを更新するための配信メソッドとして、標準の表データ・フェッチとアクティブ・データ・プッシュの2種類を持っています。ここで、バック・エンド・データがfooからbarに変更され、さらにfredに変更されたとしましょう。変更のたびに、アクティブ・データ・イベントが起動されます。これらのイベントがブラウザに到着する前に表をリフレッシュした場合、表にはfredと表示されます。これは、標準のデータ・フェッチでは、必ず最新のデータが取得されるからです。しかし、アクティブ・データ・イベントに長い時間がかかることもあるため、場合によっては、リフレッシュの後で、このデータ変更イベントの結果、fooがブラウザに到着し、表が更新されて、しばらくの間、fredのかわりにfooと表示されます。したがって、読取り一貫性を維持する方法を実装する必要があります。

読取り一貫性を実現するために、ActiveDataModelは、効果的にデータにタイムスタンプを押す変更カウントの概念を採用しています。データ・フェッチとアクティブ・データ・プッシュのどちらも、このchangeCountオブジェクトを単調にカウントを増やすことで維持する必要があるため、返されたデータのchangeCountが現在のカウントより低い場合は、アクティブ・イベントにより廃棄される可能性があります。ActiveDataModelクラスの実装を使用して、読取り一貫性を維持する方法の詳細は、「アクティブ・データ・サービスへのイベント渡し」を参照してください。

41.5 ADSページからのナビゲーションに関する必知事項

アクティブ・データをADF FacesコンポーネントにプッシュするようにADFアプリケーションを構成する場合は、ユーザーがアクション取消しのオプションを使用できるように、ADSを終了するナビゲーション・イベントを処理する必要があります。

ユーザーが表示中のADS有効ページを表示するブラウザを閉じようとすると、ADSはブラウザのネイティブのwindow.unloadイベントで停止します。ユーザーがページに戻れるようにするアプリケーションの場合、ユーザーがアクションを取り消すことができるように、アプリケーションで確認プロンプトを表示する必要があります。この場合、アプリケーションはブラウザのネイティブのwindow.onbeforeunloadリスナーではなく、ADFクライアント・リスナーbeforeunloadを使用する必要があります。

ADF beforeunloadイベントを処理できるクライアント・リスナーの例は、「ADFクライアント・リスナーをJSFページからの移動の制御に使用する方法」を参照してください。

41.6 アクティブ・データ・サービスへのイベント渡し

アクティブ・データをADF FacesコンポーネントにプッシュするようにADFアプリケーションを構成する場合は、BaseActiveDataModelクラスを拡張し、マネージドBeanによって作成されるアクティブ・データ・イベントをADSフレームワークに渡すクラスを作成します。

マネージドBeanで作成されたイベントを渡すために、BaseActiveDataModelクラスを拡張するクラスを作成する必要があります。ActiveDataModelクラスは、データ変更イベントをリッスンし、イベント・マネージャと対話します。具体的には、実装するメソッドで次のことを行います。

  • 必要に応じて、アクティブ・データおよびActiveDataModelオブジェクトを開始および停止し、データ・ソースに対するリスナーの登録および登録取消しを行います。

  • イベント・マネージャからリスナーを管理し、アクティブ・データ・イベントをイベント・マネージャにプッシュします。

次の例に、オブジェクトをfireActiveDataUpdate()メソッドに入れることで、モデルのnotifyDataChange()メソッドでEventオブジェクトをADSフレームワークに渡す方法を示します。

import java.util.Collection;

import java.util.concurrent.atomic.AtomicInteger;

import oracle.adf.view.rich.activedata.BaseActiveDataModel;
import oracle.adf.view.rich.event.ActiveDataUpdateEvent;

public class ActiveStockModel extends BaseActiveDataModel
{

  // -------------- API from BaseActiveDataModel ----------

  @Override
  protected void startActiveData(Collection<Object> rowKeys,
                                 int startChangeCount)
  {
    /* We don't do anything here as there is no need for it in this example.
     * You could use a listenerCount to see if the maximum allowed listerners
     * are already attached. You could register listeners here.
     */
      }

  @Override
  protected void stopActiveData(Collection<Object> rowKeys)
  {
    // same as above... no need to disconnect here
  }

  @Override
  public int getCurrentChangeCount()
  {
    return changeCounter.get();
  }
  
  // -------------- Custom API -----------

  /**
   * Increment the change counter.
   */
  public void prepareDataChange()
  {
    changeCounter.incrementAndGet();
  }

  /**
   * Deliver an ActiveDataUpdateEvent object to the ADS framework.
   * 
   * @param event the ActiveDataUpdateEvent object
   */
  public void notifyDataChange(ActiveDataUpdateEvent event)
  {
    // Delegate to internal fireActiveDataUpdate() method.
    fireActiveDataUpdate(event);
  }

  // properties
  private final AtomicInteger changeCounter = new AtomicInteger();
}

41.7 データ更新イベント・リスナーの登録

アクティブ・データをADF FacesコンポーネントにプッシュするようにADFアプリケーションを構成する場合は、faces-config.xmlファイルを使用して、バックエンドでのデータ変更に対するデータ変更リスナーを登録します。

バックエンドからのデータ変更イベントに対するデータ変更リスナーを登録する必要があります。次の例では、リスナーBeanStockBackEndSystemfaces-config.xmlファイルに登録されます。この例では、リスナーをバックエンドに挿入するために式言語が使用されています。

...
<managed-bean>
  <managed-bean-name>backend</managed-bean-name>
  <managed-bean-class>
      oracle.afdemo.backend.StockBackEndSystem
  </managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
  <managed-property>
      <property-name>listener</property-name>
      <value>#{stockManager}</value>
  </managed-property>
</managed-bean>

41.8 アクティブ・データを表示するためのADFコンポーネントの構成

アクティブ・データをADF FacesコンポーネントにプッシュするようにADFアプリケーションを構成する場合は、プッシュされたデータの表示に使用するADF Facesコンポーネントを実装するマネージドBeanを指定する式を追加します。

コレクションベースのデータを表示するADFコンポーネントは、ADSと提携するように構成することができます。ビュー・レイヤーでは余分なセットアップは必要ありません。リスナーを登録すると、ADSを使用してデータをビュー・レイヤーに流すことができます。たとえば、JSPXページでtableコンポーネントを使用して、リスナーを登録しているバックエンド・ソースからの在庫の更新を表示するとします。

次の例に、プッシュされたデータを受信するためにtable コンポーネントのvalue属性で使用される式言語を示します。

...
<f:view>
   <af:document id="d1">
      <af:form id="f1">
        <af:panelStretchLayout topHeight="50px" id="psl1">
          <f:facet name="top">
            <af:outputText value="Oracle ADF Faces goes Push!" id="ot1"/>
          </f:facet>
          <f:facet name="center">
            <!-- id="af_twocol_left_full_header_splitandstretched"  -->
            <af:decorativeBox theme="dark" id="db2">
              <f:facet name="center">
                <af:panelSplitter orientation="horizontal"
                                  splitterPosition="100" id="ps1">
                  <f:facet name="first">
                    <af:outputText value="Some content here." id="menu"/>
                  </f:facet>
                  <f:facet name="second">
                    <af:decorativeBox theme="medium" id="db1">
                      <f:facet name="center">
                        <af:table value="#{stockManager}" var="row"
                                  rowBandingInterval="0" 
                                  id="table1" emptyText="No data...">
                          <af:column sortable="false" headerText="Name"
                                     id="column1">
                            <af:outputText value="#{row.name}" id="outputText1"/>
                          </af:column>
                          <af:column sortable="false" 
                                     headerText="Value...." id="column2">
                            <af:outputText value="#{row.value}" 
                                           id="outputText2" />
                          </af:column>
                        </af:table>
                      </f:facet>
                    </af:decorativeBox>
                  </f:facet>
                </af:panelSplitter>
              </f:facet>
            </af:decorativeBox>
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
   </af:document>
</f:view>