Monday, November 12, 2012

Oracle Coherence with Active Data Service

Hi ,

Use Case :Need to show latest records into af:table with use of oracle coherence.

Class Description :

The following class which i used to achieve my requirement .

1-TableActiveCollectionModelDecorator
    This class used for providing the model data for af:table
    Extends ActiveCollectionModelDecorator 

2-TableActiveModelClass
   This class extends BaseActiveDataModel

3-CoherenceClass
   This class use to put the data into oracle coherence

4-Interface TableInterface for listen Map listener 

The class and interface relationship is depicted in following picture

UI description :

Here i used af:table as UI component to show the latest data.

I have been working on a project where i need to show the latest data on UI .For this we are using following technology.

1-Active data service
2-And oracle coherence

Since this bit tricky and tough to understand so for this i have divided my post under following  parts

Step 1-In first part will explain active data service configuration into the application.
Step 2-In second part will explain  oracle coherence for maintaining data in memory cache.
Step 3-In third part will explain communication between between  oracle coherence and Active data Service.
Step 4-Registration of the bean into adfc-config.xml 
Step5-Doing following operation into oracle coherence cache.
 1-Adding new record into cache
 2-update records into cache
 3-delete records into cahce 

So let me explain each individual point in details

Step 1-In first part will explain active data service configuration into the application.

1-You require to configure Active Data Service  in the adf-config.xml.In adf-config.xml file need to register communication transport details and  need to create new property file name as adf-config.properties file .

i)ADD following code 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>long-polling</transport>
    <latency-threshold>10000</latency-threshold>
    <keep-alive-interval>10000</keep-alive-interval>
    <polling-interval>3000</polling-interval>
    <max-reconnect-attempt-time>1500000</max-reconnect-attempt-time>
    <reconnect-wait-time>10000</reconnect-wait-time>
  </ads:adf-activedata-config>
</adf-config>


ii)First create services folder under .adf/META-INF directory and then create property file name  adf-config.properties add following code in adf-config.properties file

http\://xmlns.oracle.com/adf/activedata/config=oracle.adfinternal.view.faces.activedata.ActiveDataConfiguration$ActiveDataConfigCallback


*Be careful all the above line should be in  one single line.

2-Since here i am using af:table component for Active data service so first class which we do require to extends is ActiveCollectionModelDecorator.
Here i am using  ActiveCollectionModelDecorator class however you can also use ActiveDataModel class .
 Code is following

package com.backing.bean.ads;

import com.backing.bean.coherence.CoherenceClass;
import com.backing.bean.coherence.Example;
import com.backing.bean.data.TableData;
import com.backing.bean.interfacee.TableInterface;
import com.backing.bean.model.TableActiveModelClass;

import java.util.List;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import javax.faces.context.FacesContext;

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;

public class TableActiveCollectionModelDecorator extends ActiveCollectionModelDecorator  {

    private TableActiveModelClass _activeDataModel =
        new TableActiveModelClass();
    List<TableData> getTableDataList = null;
    private CollectionModel _model = null;

    public TableActiveCollectionModelDecorator() {
        super();
    }

    public ActiveDataModel getActiveDataModel() {
        return _activeDataModel;
    }

    protected CollectionModel getCollectionModel() {
        if (_model == null) {
            FacesContext ctx = FacesContext.getCurrentInstance();
            ExpressionFactory ef = ctx.getApplication().getExpressionFactory();
            ValueExpression ve =
                ef.createValueExpression(ctx.getELContext(), "#{coherenceClass}",
                                         CoherenceClass.class);
            CoherenceClass context =
                (CoherenceClass)ve.getValue(ctx.getELContext());
            getTableDataList = context.getTableDataList();
            _model = new SortableModel(getTableDataList);
        }
        return _model;
    }
 }

*Here coherence class is responsible to fetch data from coherence and return into this class.When the page if load first time getCollectionModel() method will called and return data back to UI.

Here following method are override

1-public ActiveDataModel getActiveDataModel() {}
2- public CollectionModel getCollectionModel(){}

3-Second class which is require to extends  BaseActiveDataModel class moreover  this class is also very important class.Code is following

package com.backing.bean.model;

import java.util.Collection;

import java.util.concurrent.atomic.AtomicInteger;

import oracle.adf.view.rich.activedata.BaseActiveDataModel;
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.BaseMenuModel;
import org.apache.myfaces.trinidad.model.CollectionModel;

public class TableActiveModelClass 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();
    }
    public void prepareDataChange() {
        changeCounter.incrementAndGet();
    }
    public void notifyDataChange(ActiveDataUpdateEvent event) {

        fireActiveDataUpdate(event);
    }
    private final AtomicInteger changeCounter = new AtomicInteger();
}


Although here i am not doing any thing in the startActiveData and stopActiveData but it is used to seed and remove the resource.

Above are steps to implements Active data service and following are for oracle coherence

Step 2-In second part will explain  oracle coherence for maintaining data in memory cache.

1-First need to add the coherence.jar jar file into application path. coherence.jar is already available in jdeveloper installation folder
 Location is following
 oracle installation  folder name\oracle_common\modules\oracle.coherence 

2-Created class name as CoherenceClass where i have done initialization for Name  Cache as following in static block


    static {
        nameCache = CacheFactory.getCache(CACHE_NAME);
    }


3-In constructor of  CoherenceClass load the data form method  and add in the oracle coherence as following


    public CoherenceClass() {
        CacheFactory.ensureCluster();
        NamedCache nc = CacheFactory.getCache(CoherenceClass.CACHE_NAME);
        nc.addMapListener(this);
        if (nc.size() == 0) {
            nc.putAll(loadDataFromCoherence());
        }
    }

    private static Map loadDataFromCoherence() {
        //This are my friends name
        Map loadDataMap = new HashMap();
        loadDataMap.put("C0", new TableData(0L, "Prateek"));
        loadDataMap.put("C1", new TableData(1L, "Abhishek"));
        loadDataMap.put("C2", new TableData(2L, "Satyendra"));
        loadDataMap.put("C3", new TableData(3L, "Ramesh"));
        loadDataMap.put("C4", new TableData(4L, "Anoop"));
        loadDataMap.put("C5", new TableData(5L, "Jamal"));
        loadDataMap.put("C6", new TableData(6L, "Laljeet"));
        loadDataMap.put("C7", new TableData(7L, "Hariom"));
        loadDataMap.put("C8", new TableData(8L, "Rajesh"));
        return loadDataMap;
    }


4-In next step i required to implements interface name MapListener which has following method  to catch the event.


    public void entryInserted(MapEvent mapEvent) {  }

    public void entryUpdated(MapEvent mapEvent) }

    public void entryDeleted(MapEvent mapEvent) {}


If we add/remove/delete any things from same cache it will automatically trigger this method accordingly 

5-So in the same class i have implemented MapListener and override above three method .

Step 3-In third part will explain communication between between  oracle coherence and Active data Service. 

Here i done with Active data service and oracle coherence.Although we do require communication between coherence and ads .

For providing communication between these two i have created one interface and step are following

1-Created an interface name as TableInterface which has following method

    public void entryInCoherence(Integer rowKey, TableData tableData);

    public void updateInCoherence(Integer rowKey, TableData tableData);

    public void deleteInCoherence(Integer rowKey, TableData tableData);


2-implemetated this interface with same class which extends  ActiveCollectionModelDecorator class and override all the method code are following


      public void entryInCoherence(Integer rowKey, TableData tableData) {
        if (rowKey != null) {
            TableActiveModelClass asm = _activeDataModel;
            asm.prepareDataChange();
            ActiveDataUpdateEvent event =
                ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.INSERT,
                                                               asm.getCurrentChangeCount(),
                                                               new Object[] { rowKey },
                                                               new Object[] { null },
                                                               new String[] { "id",
                                                                              "name" },
                                                               new Object[] { tableData.getId(),
                                                                              tableData.getName() });
            asm.notifyDataChange(event);
        }
    }

    public void updateInCoherence(Integer rowKey, TableData tableData) {
        if (rowKey != null) {
            TableActiveModelClass asm = _activeDataModel;
            asm.prepareDataChange();
            ActiveDataUpdateEvent event =
                ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
                                                               asm.getCurrentChangeCount(),
                                                               new Object[] { rowKey },
                                                               new Object[] { null },
                                                               new String[] { "id",
                                                                              "name" },
                                                               new Object[] { tableData.getId(),
                                                                              tableData.getName() });
            asm.notifyDataChange(event);
        }
    }

    public void deleteInCoherence(Integer rowKey, TableData tableData) {
        TableActiveModelClass asm = _activeDataModel;
        asm.prepareDataChange();
        ActiveDataUpdateEvent event =
            ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.REMOVE,
                                                           asm.getCurrentChangeCount(),
                                                           new Object[] { rowKey },
                                                           new Object[] { null },
                                                           new String[] { "id",
                                                                          "name" },
                                                           new Object[] { tableData.getId(),
                                                                          tableData.getName() });

        asm.notifyDataChange(event);
    }




Step 4-Registration of the bean into adfc-config.xml 



<managed-bean id="__2">
    <managed-bean-name id="__1">tableActive</managed-bean-name>
    <managed-bean-class id="__4">com.backing.bean.ads.TableActiveCollectionModelDecorator</managed-bean-class>
    <managed-bean-scope id="__3">session</managed-bean-scope>
  </managed-bean>
  <managed-bean id="__26">
    <managed-bean-name id="__25">coherenceClass</managed-bean-name>
    <managed-bean-class id="__23">com.backing.bean.coherence.CoherenceClass</managed-bean-class>
    <managed-bean-scope id="__24">session</managed-bean-scope>
    <managed-property id="__28">
      <property-name id="__27">listener</property-name>
      <value id="__29">#{tableActive}</value>
    </managed-property>
  </managed-bean>



Step5-Doing following operation into oracle coherence cache.

I have created TestingClass class for doing add/delete/update into cache i have created one main class which has following code.
 package com.backing.bean.coherence;

import com.backing.bean.data.TableData;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;

public class TestingClass {
    public TestingClass() {
        super();
    }

    public static void main(String[] args) {
        //here only add new record line is commented
        //following checking the update and delete please remove the comment
        NamedCache nc = CacheFactory.getCache(CoherenceClass.CACHE_NAME);
        //adding  new record
        nc.put("PK1", new TableData(1001L, "PrateekShaw"));
        //updatring record
        // nc.put("PK1", new TableData(1001L, "PrateekKumarShaw"));
        //deleting record
        // nc.remove("PK1");//Pass the key
    }
}





So in above code if you run after running the  page since only add line is not commented  so it will add the new record into cache .
1-Aftering adding records in cache


2-After  updating records


3-After deleting records




Code is present at following location

https://docs.google.com/open?id=0B8cP4jZuxLlXbWcyU0RVejc5b28

I have to explain more in details which i know but due to less time i have to stop here but  later sure i will update this post if possible.

However this is just POC to understand communication between oracle coherenernce and ADS.


Reference :

1-http://biemond.blogspot.in/2009/12/adf-data-push-with-active-data-service.html
2-http://matthiaswessendorf.wordpress.com/2010/01/07/adf%E2%80%99s-active-data-service-and-scalar-data-like-activeoutputtext/
3-Oracle Fusion Developer Guide

Thanks
Prateek

6 comments:

  1. Prateek, this is very useful. But Why all the managed bean is registered in session scope?
    Is there any limitation?

    ReplyDelete
    Replies
    1. Hi,

      No it's depend on your requirement.
      But scope should be more than request and backing bean

      Thanks
      Prateek

      Delete
  2. Many thanks for sharing Prateek
    I hope it would work the same if I link Coherence and Programmatic BC and bind programmatic VO behind the UI table. Please share your views on that.

    ReplyDelete
    Replies
    1. Hi Ponraj.

      Yes you can get the data from coherence and programmatic create VO.But i things it does not support ADS.
      For supporting ADS you need to create new model which support ADS.

      Delete
  3. Hi Prateek, Thanks for your blog really helpful.
    One problem i am finding in using this is the data that is being loaded at the start ( the names of your friends in the tables), if i try to modify the data in those rows or delete them altogether, its not reflecting in real time in ADF-ADS. The Coherence cache is updated and i if i open a new browser instance, it reflects the change. The example you have where you create a new one, then update/delete that record works just fine.

    Thanks
    Anirban

    ReplyDelete
    Replies
    1. Hi Anirban,

      It should work the only things which you should need to remember that is row key.While create the event for Update/Delete you should need to pass correct row key.Might be you are passing wrong row key therefore it is not working for existing rows.

      Delete