Active Data Service是ADF中的一个高级特性,本文介绍如何在Table中使用ADS。
重点步骤说明:
1. 页面代码
<af:table value="#{stockManager}" var="row" rowBandingInterval="0" id="t1">
<af:column sortable="false" headerText="Ticket" align="start" id="c1">
<af:outputText value="#{row.ticket}" id="ot1"/>
</af:column>
<af:column sortable="false" headerText="Value" align="start" id="c2">
<af:outputText value="#{row.value}" id="ot2"/>
</af:column>
</af:table>
说明:Table的value属性绑定到一个Managed Bean。
2. adfc-config.xml
<managed-bean id="__1">
<managed-bean-name>stockManager</managed-bean-name>
<managed-bean-class>view.StockManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean id="__2">
<managed-bean-name>stockBackend</managed-bean-name>
<managed-bean-class>view.StockBackEnd</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>listener</property-name>
<value>#{stockManager}</value>
</managed-property>
</managed-bean>
说明:注册Managed Bean:StockManager和StockBackEnd。
其中,StockBackEnd有一个属性:listener,指向StockManager。
3. 完整的StoreManager的代码
package view;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import oracle.adf.view.rich.activedata.ActiveDataEventUtil;
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 org.apache.myfaces.trinidad.model.CollectionModel;
import org.apache.myfaces.trinidad.model.SortableModel;
public class StockManager extends ActiveCollectionModelDecorator implements IBackendListener {
@Override
public ActiveDataModel getActiveDataModel() {
return stockModel;
}
@Override
protected CollectionModel getCollectionModel() {
if (collectionModel == null) {
FacesContext ctx = FacesContext.getCurrentInstance();
ExpressionFactory ef = ctx.getApplication().getExpressionFactory();
ValueExpression ve = ef.createValueExpression(ctx.getELContext(), "#{stockBackend}", StockBackEnd.class);
StockBackEnd context = (StockBackEnd)ve.getValue(ctx.getELContext());
collectionModel = new SortableModel(context.getStocks());
}
return collectionModel;
// if (collectionModel == null) {
// // connect to a backend system to get a Collection
// List stocks = FacesUtil.loadBackEnd().getStocks();
// // make the collection become a (Trinidad) CollectionModel
// collectionModel = new SortableModel(stocks);
// }
// return collectionModel;
}
/**
* 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, Stock stock) {
if (rowKey != null) {
System.out.println("Stock " + stock.getTicket() + " Changed is : " + stock.getValue() );
ActiveStockModel asm = getActiveStockModel();
asm.prepareDataChange();
ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
asm.getCurrentChangeCount(), new Object[] { rowKey },
null, new String[] { "value" },
new Object[] { stock.getValue() });
// Deliver the new Event object to the ADS framework
asm.notifyDataChange(event);
}
}
/**
* Typesafe caller for getActiveDataModel()
* @return
*/
protected ActiveStockModel getActiveStockModel() {
return (ActiveStockModel)getActiveDataModel();
}
private CollectionModel collectionModel;
private ActiveStockModel stockModel = new ActiveStockModel();
}
说明:
(1)StockManager继承了ActiveCollectionModelDecorator,重写了方法getCollectionModel,该方法返回集合对象。这里使用了ValueExpression动态绑定到StockBackEnd,这样不用自己构造CollectionModel对象。
(2)StockManager实现了IBackendListener接口,重写了方法onStockUpdate,该方法向ADS framework发送UpdateEvent。
4. 完整的StockBackEnd的代码
package view;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class StockBackEnd {
public StockBackEnd() {
stocks.add(new Stock("中国联通", 1.0));
stocks.add(new Stock("中国石油", 2.0));
stocks.add(new Stock("盐湖钾肥", 3.0));
}
public void setListener(IBackendListener listener) {
this.listener = listener;
}
public IBackendListener getListener() {
return listener;
}
private IBackendListener listener;
private final List<Stock> stocks = new CopyOnWriteArrayList<Stock>();
public List<Stock> getStocks() {
return stocks;
}
public void changeData() {
List<Stock> rows = getStocks();
Stock dataRow = null;
for (int rowKey = 0; rowKey < rows.size(); rowKey++) {
dataRow = rows.get(rowKey);
dataRow.setValue(dataRow.getValue() + 1.0);
listener.onStockUpdate(rowKey, dataRow);
Long stoptime = 2000L;
try {
Thread.sleep(stoptime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@PostConstruct
private void startUpdateProcess() {
Runnable dataChanger = new Runnable() {
public void run() {
// wait 10 seconds before we start to update our stocks...
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
while (listener != null) {
try {
Thread.sleep(3000);
changeData();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
Thread newThread = new Thread(dataChanger);
newThread.start();
}
@PreDestroy
private void stopUpdateProcess() {
if (listener != null) {
System.out.println("listener " + listener);
listener = null;
}
}
}
说明:
(1)StockBackEnd定义了数据源,实际情况中,你应该在这里重新定义你获取数据的方式。
(2)StockBackEnd定义了触发ChangeData的方式:这里是在执行完毕构造函数后,启动一个线程,每隔10秒,执行一次ChangeData方法。
实际情况中,你应该在这里重新定义触发方式,比如使用JMS。
6. 完整的ActiveStockModel的代码
package view;
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 {
@Override
protected void startActiveData(Collection<Object> rowKeys, int startChangeCount) {
}
@Override
protected void stopActiveData(Collection<Object> rowKeys) {
}
@Override
public int getCurrentChangeCount() {
return changeCounter.get();
}
/**
* 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) {
fireActiveDataUpdate(event);
}
private final AtomicInteger changeCounter = new AtomicInteger();
}
说明:该类的作用是把ActiveDataUpdateEvent对象发送给ADS framework。
7. 运行效果
每行数据依次被更新(背景色显示为蓝色),而整个Table并没有被刷新。
Project 下载:ADF_ADS_Table.7z
参考文献:
1.《Web User Interface Developer’s Guide for ADF》之 Using the Active Data Service with an Asynchronous Backend
2.《Fusion Developer's Guide for ADF》之 Using the Active Data Service
3. http://biemond.blogspot.com/2009/12/adf-data-push-with-active-data-service.html
4. http://matthiaswessendorf.wordpress.com/2010/01/07/adf%E2%80%99s-active-data-service-and-scalar-data-like-activeoutputtext/
5. http://matthiaswessendorf.wordpress.com/2009/12/05/adf-faces-and-server-side-push/
6. http://matthiaswessendorf.wordpress.com/2009/12/11/adfs-active-data-service-and-multiple-push-windows/
7. http://technology.amis.nl/2011/10/19/adf-faces-handle-task-in-background-process-and-show-real-time-progress-indicator-for-asynchronous-job-using-server-push-in-adf/
没有评论:
发表评论