- activeCommandToolbarButton
- activeImage
- activeOutputText
- table
- tree
- All DVT components
In the next weeks I will make an ADS example with EJB and JMS, The ADS Page components will then listen on a topic for EJB data change events and refreshes the page with push .
But first let me show what you need to do.
Configure the adf-config.xml , located in the .adf\META-INF folder ( workspace level). With this ADF file you can configure the push parameters, study these parameters because they can influence the application performance.
<?xml version="1.0" encoding="windows-1252" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config"
xmlns:sec="http://xmlns.oracle.com/adf/security/config"
xmlns:ads="http://xmlns.oracle.com/adf/activedata/config">
<sec:adf-security-child xmlns="http://xmlns.oracle.com/adf/security/config">
<CredentialStoreContext credentialStoreClass="oracle.adf.share.security.providers.jps.CSFCredentialStore"
credentialStoreLocation="../../src/META-INF/jps-config.xml"/>
</sec:adf-security-child>
<ads:adf-activedata-config xmlns="http://xmlns.oracle.com/adf/activedata/config">
<!--
transport allows three settings:
* streaming (default)
* polling
* long-polling
-->
<transport>long-polling</transport>
<latency-threshold>10000</latency-threshold>
<keep-alive-interval>10000</keep-alive-interval>
<polling-interval>3000</polling-interval>
<max-reconnect-attempt-time>1800000</max-reconnect-attempt-time>
<reconnect-wait-time>10000</reconnect-wait-time>
</ads:adf-activedata-config>
</adf-config>
Then create in the .adf\META-INF folder a new folder called services and put a new file in called adf-config.properties and add the following content.
http\://xmlns.oracle.com/adf/activedata/config=oracle.adfinternal.view.faces.activedata.ActiveDataConfiguration$ActiveDataConfigCallback
Go to your JSF page and drag and drop for example departments from your datacontrol to your page. Choose a readonly table ( when you want to use inputtext instead of outputtext JSF components then keep in mind you need to reset the uicomponent else it won't get the new value ) and don't use Filtering on the table.
This is how your JSF can look like.
<af:table value="#{bindings.departmentsFindAll.collectionModel}"
var="row"
rows="#{bindings.departmentsFindAll.rangeSize}"
emptyText="#{bindings.departmentsFindAll.viewable ? 'No data to display.' : 'Access Denied.'}"
fetchSize="#{bindings.departmentsFindAll.rangeSize}"
rowBandingInterval="0"
selectedRowKeys="#{bindings.departmentsFindAll.collectionModel.selectedRow}"
selectionListener="#{bindings.departmentsFindAll.collectionModel.makeCurrent}"
rowSelection="single" id="t3">
<af:column sortProperty="departmentId" sortable="true"
headerText="#{bindings.departmentsFindAll.hints.departmentId.label}"
id="c8">
<af:outputText value="#{row.departmentId}" id="ot9">
<af:convertNumber groupingUsed="false"
pattern="#{bindings.departmentsFindAll.hints.departmentId.format}"/>
</af:outputText>
</af:column>
<af:column sortProperty="departmentName" sortable="true"
headerText="#{bindings.departmentsFindAll.hints.departmentName.label}"
id="c9">
<af:outputText value="#{row.departmentName}" id="ot8"/>
</af:column>
</af:table>
Here the pagedef of the page ,we need to have a handle to the departmentsFindAll tree in our Active Data Proxy framework.
<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
version="11.1.1.55.36" id="ADStablePageDef"
Package="nl.whitehorses.app.view.pageDefs">
<parameters/>
<executables>
<iterator Binds="root" RangeSize="25" DataControl="CountrySessionEJBLocal"
id="CountrySessionEJBLocalIterator"/>
<accessorIterator MasterBinding="CountrySessionEJBLocalIterator"
Binds="departmentsFindAll" RangeSize="25"
DataControl="CountrySessionEJBLocal"
BeanClass="nl.whitehorses.model2.Departments"
id="departmentsFindAllIterator"/>
</executables>
<bindings>
<tree IterBinding="departmentsFindAllIterator" id="departmentsFindAll">
<nodeDefinition DefName="nl.whitehorses.model2.Departments"
Name="departmentsFindAll0">
<AttrNames>
<Item Value="departmentId"/>
<Item Value="departmentName"/>
</AttrNames>
</nodeDefinition>
</tree>
</bindings>
</pageDefinition>
Now we can make our own DepartmentModel where we add Active Data Service to it. We need the departmentsFindAll tree attribute from the pagedef.
package nl.whitehorses.app.ads;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.view.rich.model.ActiveCollectionModelDecorator;
import oracle.adf.view.rich.model.ActiveDataModel;
import oracle.adfinternal.view.faces.model.binding.FacesCtrlHierBinding;
import org.apache.myfaces.trinidad.model.CollectionModel;
public class DepartmentModel extends ActiveCollectionModelDecorator {
private MyActiveDataModel _activeDataModel = new MyActiveDataModel();
private CollectionModel _model = null;
public CollectionModel getCollectionModel() {
if (_model == null) {
DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
FacesCtrlHierBinding treeData = (FacesCtrlHierBinding)dcBindings.getControlBinding("departmentsFindAll");
_model = treeData.getCollectionModel();
}
return _model;
}
public ActiveDataModel getActiveDataModel() {
return _activeDataModel;
}
public MyActiveDataModel getMyActiveDataModel() {
return _activeDataModel;
}
}
Make your own active ActiveDataModel in which we start a thread where we fire change events.
package nl.whitehorses.app.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 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");
Change chg = new Change();
chg.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();
}
For demo purposes we send some change events. For this we need to know the Key of the department iterator and the attribute which changed. See the ADS help page for other events like insert ,delete etc..
package nl.whitehorses.app.ads;
import oracle.adf.view.rich.event.ActiveDataEntry;
import oracle.adf.view.rich.event.ActiveDataUpdateEvent;
import oracle.adfinternal.view.faces.activedata.ActiveDataEventUtil;
import oracle.adfinternal.view.faces.activedata.JboActiveDataEventUtil;
public class Change {
public Change() {
}
public void triggerDataChange(MyActiveDataModel model) throws Exception {
for ( int i = 0 ; i < 10 ; i++) {
try {
Thread.sleep(4000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
model.bumpChangeCount();
ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
model.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Object[] { new Long(10) , new Integer(0) }),
null,
new String[] { "departmentName" },
new Object[] { "Administration" });
model.dataChanged(event);
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
model.bumpChangeCount();
event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
model.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Object[] { new Long(30), new Integer(0) }),
null,
new String[] { "departmentName" },
new Object[] { "Purchasing" });
model.dataChanged(event);
}
}
}
We need to add the DepartmentModel class as backing bean in the adfc-config or faces-config xml.
<managed-bean>
<managed-bean-name>DepartmentModel</managed-bean-name>
<managed-bean-class>nl.whitehorses.app.ads.DepartmentModel</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
And at last change the value attribute of the af:table to our managed bean
af:table value="#{DepartmentModel}" var="row"
Here you can download my demo workspace
Hi Edwin,
ReplyDeleteADS is nice stuff. I first worked with it with the GoogleTalk demo (http://www.oracle.com/technology/pub/articles/jellema-googletalk.html) However, what I never got to work - and perhaps you have - is the use of other components besides table, DVT and treetable for ADS - primarily the activeOutputText and activeOutputImage that you mention. Do you know how to use those? (I would also like to have a client side event listener for ADS events, to allow you to execute additional processing when a push event occurs).
best regards,
Lucas
Hi Lucas,
ReplyDeletethe activeOutputText and activeOutputImage will only work now with a data store that publishes events. like BAM. So they still have to do some work.
for the rest like table, tree, Graph, geo map, and gauge DVT components. You need to create a Java class that subclasses one of the following decorator classes:
ActiveCollectionModelDecorator ,
ActiveDataModelDecorator ,
ActiveGeoMapDataModelDecorator ,
ActiveGaugeDataModelDecorator
thanks Edwin
Thats very informative. Can you also throw some light on how to use a managed bean as a data source for my TreeTable with ADS?
ReplyDeleteHi
ReplyDeletemy example workspace contains an example of Matthias Wessendorf, this is a bean example.
thanks Edwin
Thanks, I have written code for ADS Treetable by extending the 'ActiveCollectionModelDecorator'. But nothing is getting rendered on the page. Do I need to extend a different class for TreeTable?
ReplyDeleteHi,
ReplyDeleteThat should work, for a treetable demo you can take a look at the rich fusion demo.
possible you got an unique key in the tree problem. are you using a row integer as key or do you have jbo key.
thanks Edwin
Hi Edwin,
ReplyDeleteNice Article. Thanks for the same.
Here you make use of the ActiveDataEventUtil.buildActiveDataUpdateEvent() method to create the Event.This event is passed to fireActiveDataUpdate() to update the UI Component.
Now, if the inserts/updates to the tables are made by a different process or person, I will not have a handle to the event. So, how do I create that event that can be used with fireActiveDataUpdate() ?
For ex: I want to enable ADS for DVT Graphs based on DB tables through Business components? But, I have no control over when or what changes happen to tables.
In such a case, how I enable ADS for my DVT Graphs ?
Best Regards,
Sivan
Hi,
ReplyDeleteyou can try push the insert & update to a jms topic and the ADS has to listen to this topic and when it receive a message it can update the ADS component.
Thanks
Hi Edwin,
ReplyDeleteyr article is nice.this one is based on bindings, collection etc.but how i implement this push if i m only using ADF components with managed beans. Since i ve a application which is only based on pure adf components & beans. i want to implement server side push on some of my af:table in my application.
Can u put any example related to this case?
Best Regards
Hi,
ReplyDeletethis should also work without adf bindings, Matthias and I made some pojo demo's
http://matthiaswessendorf.wordpress.com/2009/12/05/adf-faces-and-server-side-push/
take a look at the demo workspace.
thanks Edwin
Sorry Edwin, which one is demo for pojo. can u please specify heading & date of post.
ReplyDeleteGood luck for yr team in WC.
Best Regards
Hi,
ReplyDeletein my demo workspace you can take a look at the adstable.jspx and follow the matthias or oracle datapush tables.
thanks
Hi Edwin,
ReplyDeleteI followed the Matthias sample & changed it to connect database query.it is working nice for row updates in db table. but for new row insert,when i tried 'ActiveDataEntry.ChangeType.INSERT', the page is running similar to when polling happens.that is not a desired behaviour.
And i know that i m not doing properly when new row inserted in db table.
Edwin yr guidance is needed for new row insert.
Best Regards,
Hi Edwin,
ReplyDeleteHow i implement the data push for insert events. Can u specify what i need to adjust in Matthias sample.
Best Regards,
Hi,
ReplyDeletethis is how it works you need to make a insert method to the DepartmentManager bean which does the event stuff.
public void onDepartmentInsert(Integer rowKey, Department dept)
{
if (rowKey != null) {
System.out.println("insertEvent "+rowKey);
MyActiveDataModel asm = getMyActiveDataModel();
// start the preparation for the ADS insert
asm.prepareDataChange();
// create an ADS event, using an _internal_ util.....
// this class is not part of the API
ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(
ActiveDataEntry.ChangeType.INSERT_AFTER, // type
asm.getCurrentChangeCount(), // changeCount
new Object[] {rowKey}, // rowKey
new Object[] {null}, //insertKey (null as we don't insert stuff;
new String[] {"id","name"}, // attribue/property name that changes
new Object[] { dept.getId(), dept.getName()} // the payload for the above attribute
);
// deliver the new Event object to the ADS framework
asm.notifyDataChange(event);
}
}
then add insertData to the DepartmentBackend bean
public void insertData() {
// Add event code here...
System.out.println("insertData");
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(1000);
Long deptId = new Long(randomInt);
Department dept = new Department("dept "+randomInt , deptId);
Integer newRowKey = departments.size();
departments.add(dept);
listener.onDepartmentInsert(newRowKey, departments.get(newRowKey));
}
and put insertData to the thread and see the new records in the table
thanks
Thanks Edwin,
ReplyDeleteI attached my database query here, & it is working fine for new insert. sorry, but there is one task remains. that i think you can assist me.
I want to trigger a popup programmatically after ADS is inserting new record.
I tried this in onDepartmentInsert() method in DepartmentManager.java but failed.
How it should be implement?
Best Regards,
Hi,
ReplyDeleteI don't know, can you make a testcase then I can take a look. send it to biemond at gmail dot com
thanks
Hi Edwin,
ReplyDeleteI sent my testcase to your gmail id. Please find the ADSPojo.zip file with "Subject: ADSPojo - TestCase, Sender: ADSPojo".
Best Regards,
Hi Edwin,
ReplyDeleteI was wondering if you had the chance to write the ADS example with EJB and JMS.
I am new (very new) to ADF... I am trying to find out how can an ADF app can subscribe and listen to a JMS topic (in a remote machine) and then have the UI/page refreshed...
Thanks for any help!
Hi,
ReplyDeleteOracle is making an ADS JMS Implementation, so it is coming but I don't know when.
thanks
Hi Edwin,
ReplyDeleteI am the same who sent you ADSPojo.zip to yr gmail id.
In my real application af:table, one column contains af:commandLink.
On inserting new row by datapush,
this column remains blank but all others column populated.others column have af:outputtext. after replacing af:commandLink with af:outputtext this column populated.
but i cannot replace af:commandLink in my app. I replaced only for testing.
What i do so that af:commandLink also appears in column on new row insert?
Best Regards,
Hi Edwin,
ReplyDeletethanks for great information. I've been trying to make ADS push to UI which consists of table and summary label at the same time. I still can't make it work. I assume issue is in the fact that I am trying to INSERT_AFTER in table and UPDATE on the label. Do you have any hints or sample that does this?
Edwin,
ReplyDeleteThanks for the tremendous info and samples you leave on your blogs! In your/Matthias sample,I see that you are simulating database updates.Can you throw some light on how this is done real time.I tried to follow http://download.oracle.com/docs/cd/E14072_01/java.112/e10589/dbchgnf.htm
but I am not able to use a custom method like public void onDeptartmentUpdate(Integer rowKey, Department dept) in the listener, as in your example. Because of this I cant get the updated row info.
Kindly advise,
Krishna
Hi,
ReplyDeletethe easy way is to use a oracle 11g database, else it wont work and you can use a simple adf bc model project with a application modele in shared mode ( I think)
put this data in a table on a page
then you can add some records in the table.
else you need to implement your own ActiveDataModel which reads an AQ for the update or polls for changes.
thanks
Hi Edwin,
ReplyDeleteThanks for this useful post. We are trying something similar but using coherence to read the data in a POJO class that we are exposing as data control. We wants to display the BAM data object through coherence in our application using ADF DVT graphs.Can you please suggest some key changes that are required in order to achieve that.
Thanks.
Hi,
DeleteI don't know if this works with an ADF data control, try it first with a managed bean and direct on the jsf component. Don't know what property of the DVT component you should use and what to implement.
good luck.
Hi Edwin,
ReplyDeleteThanks for this post.
But, I am not able to get ADS for "UPDATE" while using a dynamic table.
The scenario is like, I am creating dynamic tables (based on List).
Now when an updated list comes, I need to refresh the corresponding dynamic table, I created.
Thanks.
Hi,
DeleteYou must implement DepartmentModel with ActiveDataModel and add this as value of af:table
value="#{DepartmentModel}"
that is the only way.
thanks
I guess I don't fully understand the process here.
ReplyDeleteI have a MySQL database which ADF Business Component is connected to.
I want to see the ADF table automtatically refreshed when there is a change to the database table (like insert/update/delate). I guess this is what ADS is for, right?
So I followed your example and it didn't work. Any extra thing?
I noticed you had fired some events in your Change object. Maybe MySQL database doesn't fire these events when there is a change?
Hi,
Deletethis works with oracle 11g r2 , oracle jdbc driver, add change logging to the table and use a shared adf bc application module.
else you need to program your own DepartmentModel ( which publish your changes ) with ActiveDataModel and add this as value of af:table value="#{DepartmentModel}"
thanks