This chapter provides information on registering an asynchronous backend to provide real-time data updates to ADF Faces components.
This chapter includes the following sections:
Section 35.2, "Process Overview for Using Active Data Service"
Section 35.3, "Implement the ActiveModel Interface in a Managed Bean"
Section 35.6, "Configure the ADF Component to Display Active Data"
The Fusion technology stack includes the 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 create a Java bean to implement the ActiveModel
interface and register it as an event listener to notify the component of a data event from the backend, and the component rerenders the changed data with the new value highlighted, as shown in Figure 35-1.
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 to 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. Currently, ADS supports table
components with the outputText
component contained within a column; other components are not supported inside the table column.
tree
treeTable
DVT graph, gauge, and geographical map components
For details about the active data service framework and important configuration information, see Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.
To use ADS, you can optionally configure your application to determine the method of data transport, as well as other performance options.
Complete the following tasks:
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.
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. Instead, you need to have your data source handle this before publishing the data change event.
Before users can run the ADF Faces page with ADS configured for the application, they must disable the popup blocker for their web browser. Active data is not supported in web browsers that have popup blockers enabled.
To use the Active Data Service:
Optionally, configure ADS to determine the data transport mode, as well as to set other configurations, such as a latency threshold and reconnect information. Configuration for ADS is done in the adf-config.xml
file.
For details about configuring ADS, see Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.
Create a backing bean that implements the ActiveModel
interface and register it as the listener for active data events from your backend.
Create a class that extends the BaseActiveDataModel
API to pass the Event object to the ADS framework.
Register a data change listener for data change events from the backend.
In the web page, configure the ADF Faces component to capture and display the pushed data by adding an expression to name the managed bean that implements the the ADF component that you use to capture and display the pushed data.
Create a backing bean that contains the active model implementation as its property. This class uses an ADS decorator class to wrap the JSF model. This class should also implement a callback from the backend that will push data into the ADS framework.
You need to create a Java class that subclasses one of the following ADS decorator classes:
ActiveCollectionModelDecorator
class
ActiveDataModelDecorator
class (for use with graphs)
ActiveGeoMapDataModelDecorator
class
ActiveGaugeDataModelDecorator
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, when you implement the ActiveModel
interface, you accomplish the following:
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.
To implement the ActiveModel
interface, you need to implement methods on your Java class that gets the model to which the data is being sent and registers itself as the listener of the active data source (as illustrated in Example 35-1):
Create a Java class that extends the decorator class appropriate for your component.
Example 35-1 shows a StockManager
class that extends ActiveCollectionModelDecorator
. In this case, the data is displayed for an ADF Faces table
component.
Implement the methods of the decorator class that will return the ActiveDataModel
class and implement the method that returns the scalar model.
Example 35-1 shows an implementation of the getCollectionModel()
method that registers with an existing asynchronous backend. The method returns the list of stocks collection from the backend.
Implement a method that creates application-specific events that can be used to insert or update data on the active model.
Example 35-1 shows the onStockUpdate()
callback method from the backend, which uses the active model (an instance of ActiveStockModel
) to create ActiveDataUpdateEvent
objects to push data to the ADF Faces component.
Example 35-1 Extend the Decorator Class
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 our 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(); }
Register the class as a managed bean in the faces-config.xml
file. Example 35-2 shows the bean StockManager
is registered. Defining the managed bean allows you to specify the managed bean in an expression for the ADF Faces component's value property.
Example 35-2 Register as a Managed 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>
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 35-3 shows how you can use your implementation of the ActiveDataModel
class to maintain read consistency.
You need to create a class that extends BaseActiveDataModel
class to pass the event created by your managed bean. The ActiveDataModel
class listens for data change events and interacts with the Event Manager. Specifically, the methods you implement do the following:
Optionally, starts and stops the active data and the ActiveDataModel
object, and registers and unregisters listeners to the data source.
Manages listeners from the Event Manager and pushes active data events to the Event Manager.
Example 35-3 shows the notifyDataChange()
method of the model passes the Event
object to the ADS framework, by placing the object into the fireActiveDataUpdate()
method.
Example 35-3 Pass the Event Object into 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(); }
You need to register a data change listener for data change events from the backend. Example 35-4 shows the listener bean StockBackEndSystem
is registered in the faces-config.xml
file. Note that for this example, expression language is used to inject a listener to the backend.
Example 35-4 Register the Data Update Event Listener
... <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>
ADF components that display collection-based data can be configured to work with ADS and require no extra setup in the view layer. Once the listener is registered, you can use ADS to stream the data to the view layer. For example, imagine that your JSPX page uses a table
component to display stock updates from a backend source on which you register a listener.
Example 35-5 shows the expression language used on the table
component value
attribute to receive the pushed data.
Example 35-5 Display the Active Data
... <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>