53 Using the Active Data Service

This chapter describes how to work with the ADF Model layer and Active Data Service (ADS) to provide real-time updates to ADF Faces components.

This chapter includes the following sections:

53.1 About the Active Data Service

ADF Faces provides a mechanism called Active Data Service (ADS) that allows us push changes from the server to the client. Through ADS, you can inform the browser about the fact that changes are available.

The Fusion technology stack includes Active Data Service (ADS), which is a server-side push framework that allows you to provide real-time data updates for ADF Faces components. You bind ADF Faces components to a data source and ADS pushes the data updates to the browser client without requiring the browser client to explicitly request it. For example, you may have a table bound to attributes of an ADF data control whose values change on the server periodically, and you want the updated values to display in the table. You can configure your application and the component so that whenever the data changes on the server, the ADF Model layer notifies the component and the component rerenders the changed data with the new value highlighted, as shown in Figure 53-1.

Figure 53-1 Table Displays Updated Data as Highlighted

Description of Figure 53-1 follows
Description of "Figure 53-1 Table Displays Updated Data as Highlighted"

53.1.1 Active Data Service Use Cases and Examples

Using ADS is an alternative to using automatic partial page rendering (PPR) to rerender data that changes on the backend as a result of business logic associated with the ADF data control bound to the ADF Faces component. Whereas automatic PPR requires sending a request to the server (typically initiated by the user), ADS enables changed data to be pushed from the data store as the data arrives on the server. Also, in contrast to PPR, ADS makes it possible for the component to rerender only the changed data instead of the entire component. This makes ADS ideal for situations where the application needs to react to data that changes periodically.

To use this functionality, you must configure the application to use ADS. If your application services do not support ADS, then you also need to create a proxy of the service so that the components can display the data as it updates in the source.

Any ADF Faces page can use ADS. However, you can configure only the following ADF Faces components and ADF Data Visualization (DVT) components to work with active data:

  • activeImage

  • activeOutputText

  • chart (all types)

  • gauge (all types)

  • pivotTable

  • tree

  • treeTable

  • geoMap (mapPointTheme only)

  • sunburst

  • treemap

For specific information about ADS support for DVT components, see Active Data Support in Developing Web User Interfaces with Oracle ADF Faces.

Additionally, note that collection-based components (such as table, tree, and pivotTable) support ADS only when the outputText component or sparkChart is configured to display the active data; other components are not supported inside the collection-based component.

53.1.2 Limitations of the Active Data Service Framework

The framework for ADS has the following limitations.

  • ADS does not support active data on ADF Faces table components with filtering enabled. Once a table is filtered at runtime, active data cannot be displayed.

  • ADS does not time out from user inactivity by default. To ensure that an active session is not maintained indefinitely, you can configure the web.xml context-parameter oracle.adf.view.rich.poll.TIMEOUT to specify how long ADS should run before it times out from user inactivity. For more information, see How to Configure Session Timeout for Active Data Service.

  • ADS does not restart if the user attempts to navigate away from the browser page and then chooses to stay on the page by canceling the exit action in the browser-displayed warning dialog. To workaround this limitation, ADF Faces provides client listener support to handle beforeunload events for browsers that support displaying a Confirm on Page Exit dialog. The ADF document that handles this event will allow the user to cancel the exit before ADS stops processing. For more information, see What You May Need to Know About Navigating Away From the ADS Enabled Page in Developing Web User Interfaces with Oracle ADF Faces.

  • ADS executes on a non-request thread and does not support the forced execution of component-level EL expressions. Therefore EL expressions on ADF Faces components that you may use to calculate component properties (such as fill color in a chart configured to use active data) will not be executed when the component value is updated by ADS. To execute EL in this scenario, you should calculate the property value in the model and use EL to bind the component property to the model. Alternatively, your page may apply JavaScript inline to apply the property value change after the active data update occurs. This latter solution effectively forces ADF Faces to rerender the component.

Note:

The ADF DVT components may impose additional limitations. For details, see Active Data Support in Developing Web User Interfaces with Oracle ADF Faces.

53.1.3 Active Data Service Framework

The framework for ADS contains a number of components that work together to send the active data from the source to the UI component. When a data event occurs, if the associated ADF Model layer binding is configured for active data, the Active Data model delivers the data to the Event Manager. The Event Manager then retrieves the data and invokes the Push Service, which delivers the data to the correct component, based on how the service is configured (for more information, see Data Transport Modes). The component then applies the new data pushed from the server. Figure 53-2 shows the ADS framework.

Figure 53-2 Active Data Service Framework

Description of Figure 53-2 follows
Description of "Figure 53-2 Active Data Service Framework"

In order to use the Active Data Service, you need to have a data store that publishes events when data is changed, and you need to create business services that react to those events. By default, ADF Business Components does not react to data events. The Active Data Proxy framework allows all types of data sources, including ADF Business Components, to work with ADS. It combines the ActiveDataModel with the JSF model, so that you need to override functionality only on this proxy rather than on both the ActiveDataModel and the JSF model.

The following comprise the ADS framework:

  • ActiveDataModel interface: Abstraction of the active data model. Its responsibilities include:

    • Starting and stopping active data

    • Keeping track of the current active data event ID

    • Letting the renderer know whether the model needs active data or not.

  • Event Manager: A server-side component that works with the ADF Model layer. It is responsible for the following:

    • Listening to binding events

    • Retrieving active data

    • Managing active data encoding

    • Invoking the Push service to send the encoded active data

  • Push service: A delivery channel that interacts with the Event Manager on the server side and with the Active Data Manager on the client side. It provides the following:

    • Establishing and maintaining the connection between the server and the client

    • Transmitting the active data over this connection from the server to the client

    • Ensuring that active data gets delivered within desired parameters and forcing component update if not

  • Active Data Manager: A client-side component that distributes the active data to the correct component. Specifically, it is responsible for the following;

    • Delivering events from the server side that are coming through the channel, using an event delivery service

    • Handling multiple browser windows through a shared channel

    • Dispatching active data events to rich client components, so that the components can render the change accordingly

  • Active Data Proxy: A proxy that allows all types of data sources to enable push functionality. Specifically, the proxy is responsible for the following:

    • Implementing and delegating ActiveDataModel functionality

    • Delegating to JSF models

    • Listening to data change events from the data source

    • Generating active data events based on the data change events

53.1.4 Data Transport Modes

Active data is sent to the client using data streaming (push) or one of two types of data polling. With data streaming, there is only one request, which stays open. When a data change event occurs, a partial response is sent (the response is not closed), the client is notified, and the associated component is updated to show the new data, as shown in Figure 53-3.

With data polling, the application is configured to poll the data source at specified intervals, as shown in Figure 53-4. With each request, a response is sent and closed, whether or not a data change event has occurred. If the data has changed, then the client is notified and the component is updated.

Long polling is similar to streaming. When the page is rendered, a request is sent to the active channel. However, a response is not returned until there is a data change event. At that point, the connection is closed. As soon as the connection is closed, a new connection is initiated, which results in the connection being active most of the time: there are no specific intervals. Long polling results in the majority of data change events being received when they occur, because the connection is already established and ready to send a response to the client, as shown in Figure 53-5. See What You May Need to Know About Configuring an ADS Transport Mode.

Figure 53-5 Long Polling Mode

Description of Figure 53-5 follows
Description of "Figure 53-5 Long Polling Mode"

To use ADS, you need to configure your application to determine the method of data transport, as well as other performance options.You also need to configure the bindings for your components so that they can use ADS. If you are using ADF Business Components, you need to modify your model to implement the ActiveModel interface to register itself as the listener for active data events using the Active Data Proxy.

Tip:

Oracle offers Oracle Business Activity Monitoring (BAM), which is a complete solution that provides the ability to create an active data store. See Using Active Data in Monitoring Business Activity with Oracle BAM.

53.2 Configuring the Active Data Service

The adf-config.xml file is used to configure ADS. You can configure your ADF application and the component so that whenever the data changes on the server, the component is notified and rerenders with the new value highlighted. ADS can use any of the three modes to determine active data to the component: streaming, polling, or long polling.

You need to configure ADS to determine the data transport mode, as well as to set other configurations, such as a latency threshold and reconnect information.

Note:

If you enable ADS but do not configure it, the ADS framework will use the default values shown in Table 53-1.

53.2.1 How to Configure the Active Data Service

Configuration for running the Fusion web application with ADS in JDeveloper and Integrated WebLogic Server is done in the adf-config.xml file. For information about the adf-config.xml file, including how to create one if you need to, see the Configuration in adf-config.xml section of Developing Web User Interfaces with Oracle ADF Faces.

Note:

To support automatic replication and failover for web applications within a clustered environment, Oracle WebLogic Server supports mechanisms for replicating HTTP session state across clusters. You can configure Oracle Application Development Framework (Oracle ADF) to ensure the Fusion web application's state can be restored from any server in the cluster. To support failover for pages configured to display active data, it is necessary to enable failover for the ADF Business Components application module and to enable ADF Controller to track changes to the ADF memory scopes. With these ADF settings configured, when failover occurs, the ADF server will detect the failover and request all pages configured to display active data to refresh themselves, after that ADS restarts and data is pushed to the client. For details about how to enable failover for the Fusion web application in a high availability environment, see Configuring Oracle ADF for High Availability.

Before you begin:

It may be helpful to have an understanding of the ADS configuration choices. See Configuring the Active Data Service and What You May Need to Know About Configuring an ADS Transport Mode.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. See About the Active Data Service.

To configure the Active Data Service:

  1. In the Application Resources panel, expand the Descriptors and ADF Meta-INF nodes, and then double-click adf-config.xml.
  2. Click the Source tab to open the file in the source editor, and create an entry for each of the elements shown in Table 53-1.

    Table 53-1 ADS Configuration Elements in adf-config.xml

    Element Description Default Value (in milliseconds) Minimum Value (in milliseconds)

    <transport>

    The method by which data will be delivered to the client. Value values are:

    • streaming (default)

    • polling

    • long-polling

       

    <latency threshold>

    Latency threshold in milliseconds. Active data messages with network delays greater than this threshold will be treated as late.

    10000

    1000

    <keep-alive-interval>

    Frequency in milliseconds for sending keep-alive messages when no events are generated.

    10000

    5000

    <polling-interval>

    When <transport> set to polling, frequency in milliseconds of the poll request.

    5000

    1000

    <max-reconnect-attempt-time>

    Maximum period of time in milliseconds a client will attempt to reconnect the push channel to the server upon getting disconnected.

    1800000 (30 minutes)

    0

    <reconnect-wait-time>

    Time interval in milliseconds to wait between reconnect attempts.

    10000

    1000

    Performance Tip:

    Keep the following in mind when configuring the ADS.

    • Set the latency threshold to more than 1000 to avoid frequent component refreshing.

    • Set the keep-alive interval and reconnect wait time to be less than the browser's keep-alive timeout.

    • Set the max reconnect time to be less than your web server's session timeout.

    The following example shows a sample configuration that has content pushed to the client.

    <?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>
    
  3. Synchronize the clocks on the data server and on the application server. If these are not synchronized, then events may appear to ADS to have occurred in the future.

53.2.2 What You May Need to Know About Configuring an ADS Transport Mode

ADS can use one of three transport modes to deliver active data to the component: streaming, polling, or long polling.

When you configure ADS to use the streaming mode, data is pushed to the client whenever a change event is raised by the data. On the client side, after the push channel is established, if there is no activity within the time of the value for the latency-threshold element plus the keep-alive-interval, an establish-channel-request will be sent out repeatedly based on the value of the reconnect-wait-time element, until the amount of time passed reaches the value of the max-reconnect-attempt-time element. After that, the connection will be considered disconnected. For example, given the values shown in How to Configure the Active Data Service, if there is no activity in 15,000 milliseconds, an establish channel request will be sent out every 8,000 milliseconds for up to 90,000 milliseconds. After that, the connection will be considered disconnected.

On the server side, the server disconnects the push channel and starts a timer to clean up with a cleanup-delay-time when there is an empty active data message or when it fails to send out an active data message. The cleanup-delay-time is calculated as max-reconnect-attempt-time + 30 * 1000 ms. Its default value is 30 minutes.

When you configure ADS to use the polling mode, on the client side the polling request is scheduled to be sent out repeatedly after the value of the polling-interval element has been reached. If no response is received after the value of the max-reconnect-attempt-time has elapsed, the connection is treated as disconnected and no more requests will be sent. After receiving a polling response, if the time the response has taken is greater than the polling-interval element, the service sends the next request out right away. If it is less, the next request will be sent as scheduled.

For the server side, the session ends after the polling response is returned. At that point, a timer with a cleanup-delay-time is set up to trigger cleanup. If a new request comes in before the timer fires, the old timer is canceled, and new timer is created.

When you configure ADS to use the long polling mode, requests are made as they are in streaming mode; however, as soon as the connection is treated as disconnected, a new connection is initiated. The result is a significant reduction in latency.

Table 53-2 compares the three different modes.

Table 53-2 Comparison of Streaming, Polling, and Long-Polling Modes

Comparison Benchmark Streaming Polling Long-Polling

Latency

Very good.

Directly after an event occurs on the server side, a partial response is sent to the client. If there is another event, immediately, it is also sent as a partial response. There is almost no latency with this approach.

Poor, depending on the polling interval.

If the polling interval is short (for example, 0.5 seconds), it will slow down the network because the connections are repeatedly opened. It is also expensive on the client- and server-side resources.

Good.

However, when there is a new event immediately after a response has been closed, there is some latency until the new data appears on the client side. On average, this is not a problem.

HTTP Proxy

Poor.

For some older servers, because the response is never released, when a proxy is sitting between client and server, it is possible that the proxy will buffer responses.

This is an unfortunate optimization that prevents real-time data from flowing into the browser. Long polling should be used if a proxy is used.

Good.

Good.

Number of live connections

Poor.

Many concurrent connections, as the stream is always live.

Good.

Connections are live only during the actual poll. Note however that if there is a high polling rate then the number of concurrent connections will also be high.

Poor.

Many concurrent connections, as the stream is almost always live.

Communication channel

HTTP GET

This can result in the display of a "busy" cursor or the animation of a browser's "throbber" icon.

XMLHttpRequest (XHR)

HTTP GET

XMLHttpRequest (XHR)

HTTP GET

53.2.3 How to Configure Session Timeout for Active Data Service

By default, applications that you configure to use ADS will not time out. In polling mode, the client periodically sends polling request to server. In long polling mode, the client sends a new polling request immediately after a polling request is returned from the server. In steaming mode, the server holds the GET request response and will indefinitely write active data to it. Due to the behavior of each data transport mode, ADS will maintain the application session active whether or not the user interacts with the application.

To prevent this situation of maintaining an active session indefinitely due to ADS, the web.xml configuration file contains the oracle.adf.view.rich.poll.TIMEOUT context-parameter, which specifies how long ADS should run before it times out from user inactivity. The ADS client is considered eligible to time out if there is no keyboard or mouse activity. The default timeout period is set at ten minutes. So if the user remains inactive for ten minutes, that is, does not use the keyboard or mouse, then ADS will stop polling or will abort its connection with server, and from that point on, the application participates in the standard server-side session timeout. For example, if the server-side session timeout is 65 minutes, and the polling timeout parameter is set to 15 minutes, then it is expected that the session should expire when the user is inactive for more than 80 minutes.

You can override this timeout setting for a specific web page using the ADF Faces poll component timeout attribute. The poll component allows you to deliver a heartbeat to the server to prevent users from being timed out of their session for specific pages. For more information about the oracle.adf.view.rich.poll.TIMEOUT context-parameter and individual web pages, see the Using Polling Events to Update Pages section of Developing Web User Interfaces with Oracle ADF Faces.

Before you begin:

It may be helpful to have an understanding of the ADS configuration choices. For more information, see Configuring the Active Data Service.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see About the Active Data Service.

To configure the Active Data Service to timeout after session becomes inactive:

  1. In the Applications window, in the user interface project, expand the Web Content and WEB-INF nodes, and then double-click web.xml.
  2. In the overview editor for the web.xml file, click the Application navigation tab and in the Context Initialization Parameters section, click the Create icon to add the polling timeout parameter as follows:

    Name: Enter the context parameter oracle.adf.view.rich.poll.TIMEOUT.

    Value: Enter the amount of time in milliseconds after which all pages configured to use active data will time out and ADS will stop. To disable ADF time out due to user inactivity, enter -1. The default value for ADS to timeout is 600000 milliseconds (ten minutes).

53.3 Configuring Components to Use the Active Data Service

The process of configuring components to use Active Data Service (ADS) is dependent on the usage of the Active Data proxy. You can configure components to use ADS with or without the proxy.

How you configure components to use ADS depends on whether or not you must use the Active Data proxy. If your application uses a data store that publishes events when data is changed, and your business services react to those events (for example, if your application uses BAM), then you do not need to use the Active Data proxy.

If your business services do not react to those events (for example, if your application uses ADF Business Components), then you must use the Active Data proxy and follow different procedures for configuring your components.

Note:

If your business service requires the use of the Active Data proxy, then you can only use the following components with active data:

  • table

  • tree

  • treeTable

  • ADF Data Visualization chart, gauge, mapPointTheme, pivotTable, pivotFilterBar, sunburst, and treemap components

53.3.1 How to Configure Components to Use the Active Data Service Without the Active Data Proxy

To use ADS without the proxy, you need to set a value on the binding element in the corresponding page definition file.

Before you begin:

It may be helpful to have an understanding of the ADS configuration choices. For more information, see Configuring Components to Use the Active Data Service.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see About the Active Data Service.

To configure a component to display active data without the Active Data proxy:

  1. From the Data Controls panel, drag and drop a collection onto a JSF page, and from the context menu, choose the desired component.
  2. If you are dropping the collection as an ADF bound tree or tree table, you need to ensure the following:
    • That the binding represents homogeneous data (that is, only one rule), although an accessor can still access a like accessor.

    • That the binding rule contains a single attribute.

    • That the table does not use filtering.

    • That the tree component's nodeStamp facet contains a single outputText tag and contains no other tags, as described in What You May Need to Know About Displaying Active Data in ADF Trees.

  3. From the Application window, right-click the page and choose Go to Page Definition.

    The editor for the page definition file opens for the selected page.

  4. In the Structure window, select the node that represents the attribute binding for the component.
  5. In the Properties window, expand the Advanced section, and select Push from the ChangeEventPolicy dropdown menu.

Tip:

You can use the statusIndicator component to indicate the server state. For more information, see the Displaying Application Status Using Icons section of Developing Web User Interfaces with Oracle ADF Faces.

53.3.2 How to Configure Components to Use the Active Data Service with the Active Data Proxy

To use ADS with the proxy, you need bind the value of your component to decorator class that will use the proxy. For more information about this class, see Using the Active Data Proxy to set a value on the binding element in the corresponding page definition file.

Before you begin:

It may be helpful to have an understanding of the ADS configuration choices. For more information, see Configuring Components to Use the Active Data Service.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see About the Active Data Service.

To configure a component to display active data with the Active Data proxy:

  1. From the Data Controls panel, drag and drop a collection onto a JSF page, and from the context menu, choose the desired component.
  2. If you are using an ADF bound tree or tree table, you need to ensure the following:
    • That the binding represents homogeneous data (that is, only one rule), although an accessor can still access a like accessor.

    • That the binding rule contains a single attribute.

    • That the table does not use filtering.

    • That the tree component's nodeStamp facet contains a single outputText tag and contains no other tags, as described in What You May Need to Know About Displaying Active Data in ADF Trees.

  3. From the Application window, right-click the page and choose Go to Page Definition.

    The editor for the page definition file opens for the selected page.

  4. In the Structure window, select the node that represents the attribute binding for the component.
  5. In the Properties window, change the value attribute to be bound to a decorator class that you will create for use with the proxy.

    For more information, see Using the Active Data Proxy.

53.3.3 What You May Need to Know About Displaying Active Data in ADF Trees

When you create an ADF Faces tree (or tree table) component, you configure a nodeStamp facet, which is a holder for the component used to display the data for each node of the tree. Each node is rendered (stamped) once, repeatedly for all nodes.

Because of this stamping behavior, only certain types of components are supported as children inside an ADF Faces tree component. When the tree component is not bound to an active data source, all components that have no behavior are supported. However, when you configure the tree to use ADS, only the outputText component is supported inside of the nodeStamp facet, as shown in the following example. The nodeStamp facet must not contain any other tags. So, for example, active data will not work if you add panelGroupLayout tags to the nodeStamp facets of a tree configured to use ADS.

<f:facet name="nodeStamp">
  <af:outputText value="#{row.str2}"/> 
</f:facet>

53.3.4 What Happens at Runtime: How Components Render When Bound to Active Data

After you configure your application and the component for ADS, whenever the data changes on the server, the component is notified and rerenders with only the changed data. In contrast, a component not configured for either active data or automatic PPR will need to be explicitly refreshed after a data change occurs. The explicit refresh will refetch all data that is visible on the client, including data that has not changed. Consequently, this will force the entire component to rerender. Additionally, when the component is bound to an active data source (with active data policy "Push"), the component rerenders with the new value highlighted.

53.3.5 What You May Need to Know About Running ADS in a Google Chrome Browser

When the Fusion web application runs in the Google Chrome web browser and a user presses Ctrl+N (or Ctrl+T) to open a new window (or tab) and then copies the URL from the original window into the new window, active data in the original window will stop streaming. According to the Google Chrome process model, the new browser window will be created in a separate process and both windows will share the same HTTP session. However, because browser windows that are created in two separate processes cannot communicate with each other, ADS will become out of sync between the client and server and will stop streaming. As a workaround, to allow active data in multiple Google Chrome windows, before copying the URL from the original window into the new window users must press Ctrl-Shift-N to open the browser window in incognito mode (private browsing). Because Ctrl-Shift-N opens the window in a separate process and a separate HTTP session, ADS will not attempt to synchronize between the windows and streaming will be unaffected.

53.4 Using the Active Data Proxy

In order to make business services react to events, you need to use the Active Data proxy in the ADF application. Certain procedures must be followed to configure components.

You use the active data proxy when your business services do not react to data events. If you want your components to update based on events passed into ADF Business Components, then you need to use the Active Data Proxy.

Tip:

If your application uses BAM for the business service, then you do not need to use the Active Data Proxy.

Note:

If your business service requires the use of the Active Data Proxy, then you can only use the following components with active data:

  • table

  • tree

  • treeTable

  • ADF Data Visualization mapPointTheme, pivotTable, pivotFilterBar, sunburst, and treemap components

53.4.1 How to Use the Active Data Proxy

You will create a Java class that subclasses one of the following decorator classes:

  • ActiveCollectionModelDecorator class

  • ActiveGeoMapDataModelDecorator class

These classes are wrapper classes that delegate the active data functionality to a default implementation of ActiveDataModel. The ActiveDataModel class listens for data change events and interacts with the Event Manager. Specifically, it does the following:

  • Starts and stops the active data and the ActiveDataModel object, and registers and unregisters listeners to the data source.

  • Wraps the JSF model interface. For example, the ActiveCollectionModelDecorator class wraps the CollectionModel class.

  • Generates active data events based on data change events from the data source.

  • Manages listeners from the Event Manager and pushes active data events to the Event Manager.

You need to implement methods on this Java class that registers itself as the listener of the active data source and gets the model to which the data is being sent.

Note:

The Active Data framework does not support complicated business logic or transformations that require the ADF runtime context, such as a user profile or security. For example, the framework cannot convert an ADF context locale-dependent value and return a locale-specific value.

As an example of complicated business logic, say you have logic that allows a user to move an order from open status to pending. This change results in an update event, which should cause the order to be removed from a data object called "Open Orders." The framework cannot handle this event type transformation based on business logic. Instead, you need to have your data source handle this before publishing the data change event.

Before you begin:

It may be helpful to have an understanding of the active data proxy. For more information, see Using the Active Data Proxy.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see About the Active Data Service.

You will need to complete this task:

  • Implement the logic to fire the active data events asynchronously from the data source. For example, this logic might be a business process that updates the database, or a JMS client that gets notified from JMS.

To use the active data service:

  1. Create a Java class that extends the decorator class appropriate for your component. The following example shows a class created for a table.
    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
    {
    }
    
  2. Implement the method that returns the model. The following example shows an implementation of the getCollectionModel() method that relies on expression language (EL) to avoid needing to typecast to an internal class. The method returns the DepartmentsView1 collection from the binding container.
    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;
    }
    
  3. Create an inner class that is your own implementation of an ActiveDataModel class, which the decorator can use to start and stop the active data and connect and disconnect from the data source. This class should also use the changeCount API to maintain data read consistency, as shown in the following example. For more information, see What You May Need to Know About Maintaining Read Consistency.
    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();
    }
    
  4. Implement the method that will return the ActiveDataModel class, as shown in the following example.
    public ActiveDataModel getActiveDataModel()
      {
        return _activeDataModel;
      }
    
  5. Create a method that creates application-specific events that can be used to insert or update data on the active model.

    The following example shows the triggerDataChange() method, which uses the active model (an instance of MyActiveDataModel) to create ActiveDataUpdateEvent objects to insert and update data. You will need to import oracle.adf.view.rich.activedata.ActiveDataEventUtil and oracle.adf.view.rich.activedata.JboActiveDataEventUtil to implement this method.

    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;
     
    }
    

53.5 What You May Need to Know About Maintaining Read Consistency

Read consistency guarantees a consistent view of the data in ADF application runtime. In order to use active data, your application must maintain read consistency.

Using active data means that your component has two sources of data: the active data feed and the standard data fetch. Because of this, you must make sure your application maintains read consistency.

For example, say your page contains a table and that table has active data enabled. The table has two methods of delivery from which it updates its data: normal table data fetch and active data push. Say the back end data changes from foo to bar to fred. For each of these changes, an active data event is fired. If the table is refreshed before those events hit the browser, the table will display fred because standard data fetch will always get the latest data. But then, because the active data event might take longer, some time after the refresh the data change event would cause foo to arrive at the browser, and so the table would update to display foo instead of fred for a period of time. Therefore, you must implement a way to maintain the read consistency.

To achieve read consistency, the ActiveDataModel has the concept of a change count, which effectively timestamps the data. Both data fetch and active data push need to maintain this changeCount object by monotonically increasing the count, so that if any data returned has a lower changeCount, the active data event can throw it away. For an example of an implementation of the ActiveDataModel class that maintains read consistency, see How to Use the Active Data Proxy.

53.6 Using the Active Data with a Scalar Model

ADS is very much model driven and requires no extra setups in the declarative view. It supports ADF components such as activeCommandToolbarButton, activeImage, activeOutputText, table, tree, and DVT components. Let’s take a look on how to use activeOutputText with a Java Bean, as you may not have a model.

ADF components that display collection-based data can be configured to work with ADS and require no extra setup in the view layer. However, imagine that your JSPX page uses an activeOutputText component to display new counts based on a Java timer. In this case, you will replace the model layer with scalar or "flat" data that you display from a Java Bean.

53.6.1 How to Use the Active Data with a Scalar Model

To implement the scalar model, follow these basic steps (as illustrated in the following example):

  1. Use the ActiveModelContext API to register the bean with ADS so the bean (a scalar model) imitates an actual model.

    Add the registration code to the active attribute's getter method. Adding the registration code to the bean's constructor can cause ADS to fail.

  2. Implement a custom mechanism to push the data to the view layer, as shown in the following example.

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()
  {
    timer.schedule(new UpdateTask(), 2000, 2000);
  }

  public String getState()
  {
    // 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");

    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); 
}

After you create the bean, register the bean as a managed bean in the faces-config.xml file, as the following example for counterBean illustrates:

...
<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>

Once the bean is registered, you can use ADS to stream the data to the view layer. Your ADF Faces component use expression language to receive the pushed data, as illustrated by the activeOutputText component value attribute in the following example:

...
<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>