Oracle® Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework 11g Release 1 (11.1.1) B31974-06 |
|
![]() Previous |
![]() Next |
This chapter provides information for using the Oracle ADF Model layer and Oracle ADF Faces components with an active data service.
This chapter includes the following sections:
The Fusion technology stack includes the Active Data Service (ADS), which allows you to bind ADF Faces components to an active data source. For example, you may have a table whose values might change on the server periodically, and you want the updated values to display. You can configure your application and the component so that whenever the data changes on the server, the component is notified and rerenders with the new value highlighted, as shown in Figure 42-1.
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, only the following ADF Faces components can work with active data:
activeCommandToolbarButton
activeImage
activeOutputText
table
Note: Do not use filtering on a table that will be using active data. Once a table is filtered at runtime, active data cannot be displayed. |
tree
All DVT components
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 Section 42.1.2, "Data Transport Modes"). The component then applies the new data pushed from the server. Figure 42-2 shows the ADS 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 do 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
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 42-3.
With data polling, the application is configured to poll the data source at specified intervals, as shown in Figure 42-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 42-5.
For more information, see Section 42.2.2, "What You May Need to Know About Transport Modes."
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.
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 42-1. |
Configuration for ADS is done in the adf-config.xml
file. For more 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 the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.
To configure the Active Data Service:
In the Application Resources panel, double-click the adf-config.xml file.
Click the Source tab to open the file in the source editor, and create an entry for each of the elements shown in Table 42-1.
Table 42-1 ADS Configuration Elements in adf-config.xml
Element | Description | Default Value (in milliseconds) | Minimum Value (in milliseconds) |
---|---|---|---|
|
The method by which data will be delivered to the client. Value values are:
For more information, see Section 42.2.2, "What You May Need to Know About Transport Modes." |
||
|
Latency threshold in milliseconds. Active data messages with network delays greater than this threshold will be treated as late. |
10000 |
1000 |
|
Frequency in milliseconds for sending keep-alive messages when no events are generated. |
10000 |
5000 |
|
When |
5000 |
1000 |
|
Maximum period of time in milliseconds a client will attempt to reconnect the push channel upon getting disconnected. |
1800000 (30 minutes) |
0 |
|
Time interval in milliseconds to wait between reconnect attempts. |
10000 |
1000 |
Example 42-1 shows a sample configuration that has content pushed to the client.
Example 42-1 Sample Configuration for ADS in adf-config.xml
<?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>
Create a properties file with an entry for the ADS configuration.
From the Main menu, choose File > New.
In the New Gallery, select General Category and then File, and click OK.
Name the file adf-config.properties
and save it to the META-INF\services
directory.
Add the following line:
http\://xmlns.oracle.com/adf/activedata/config=oracle.adfinternal.view. faces.activedata.ActiveDataConfiguration$ActiveDataConfigCallback
Save and close the file.
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.
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 in Example 42-1, 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 42-2 compares the three different modes.
Table 42-2 Comparison of Streaming, Polling, and Long-Polling Modes
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 |
This can result in the display of a "busy" cursor or the animation of a browser's "throbber" icon. |
|
|
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:
|
To use ADS without the proxy, you need to set a value on the binding element in the corresponding page definition file.
To configure a component to display active data without the Active Data proxy:
Drop a component onto a JSF page.
If you are using an ADF bound tree or tree table, you need to ensure the following:
The binding represents homogeneous data (that is, only one rule), although an accessor can still access a like accessor.
The binding rule contains a single attribute.
The table does not use filtering.
Open the page's associated page definition file.
In the Structure window for the page definition file, select the node that represents the attribute binding for the component. In the Property Inspector, set the ChangeEventPolicy attribute to Push.
Tip: You can use thestatusIndicator component to indicate the server state. For more information, see the "Displaying Application Status Using Icons" section of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework. |
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 Section 42.4, "Using the Active Data Proxy." to set a value on the binding element in the corresponding page definition file.
To configure a component to display active data without the Active Data proxy:
Drop a component onto a JSF page.
Change the value attribute to be bound to a decorator class that you will create for use with the proxy. For more information, see Section 42.4, "Using the Active Data Proxy."
If you are using an ADF bound tree or tree table, you need to ensure the following:
The binding represents homogeneous data (that is, only one rule), although an accessor can still access a like accessor.
The binding rule contains a single attribute.
The table does not use filtering.
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:
|
You need to create a Java class that subclasses one of the following decorator classes:
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 |
Before you begin:
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:
Create a Java class that extends the decorator class appropriate for your component. Example 42-2 shows a class created for a table.
Example 42-2 Extend the Decorator Class
package view; import org.apache.myfaces.trinidad.model.CollectionModel; /** * 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 { }
Implement the method that returns the model. Example 42-3 shows an implementation of the getCollectionModel
method, which returns the DepartmentsView1
collection from the binding container.
Example 42-3 Return the Model
/** * The FacesCtrlHierBinding class is an internal class used to cast. Because * it is not a public API, it is subject to change in future releases. * * @return The real collection model that we are delegating to */ public CollectionModel getCollectionModel() { // prototype sample code if (_model == null) { DCBindingContainer bindings = (DCBindingContainer) ADFContext.getCurrent().getRequestScope().get("bindings"); FacesCtrlHierBinding hierBinding = (FacesCtrlHierBinding) bindings.findCtrlBinding("beanData"); _model = hierBinding.getCollectionModel(); } return _model; }
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 Example 42-4. For more information, see Section 42.4.1, "What You May Need to Know About Read Consistency."
Example 42-4 Connect to the Data Source
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(); }
Implement the method that will return the ActiveDataModel
class, as shown in Example 42-5.
Create a method that creates application-specific events that can be used to insert or update data on the active model.
Example 42-6 shows the triggerDataChange()
method, which uses the active model (an instance of MyActiveDataModel
) to create ActiveDataUpdateEvent
objects to insert and update data.
Example 42-6 Create Event Objects to Update Model
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; }
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. Example 42-6 shows how you can use your implementation of the ActiveDataModel
class to maintain read consistency.