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 // Liststocks = 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/