Pages

Showing posts with label Coherence. Show all posts
Showing posts with label Coherence. Show all posts

Tuesday, February 4, 2014

Configure Coherence HotCache

Coherence can really accelerate and improve your application because it's fast, high available, easy to setup and it's scalable. But when you even use it together with the JCache framework of Java 8 or the new Coherence Adapter in Oracle SOA Suite and OSB 12c it will even be more easier to use Coherence as your main HA Cache. 
Before Coherence 12.1.2 when you want to use Coherence together with JPA for the database connectivity, you must make sure that there is no batch job or application doing modifications directly in the database. This will lead to an out of sync Coherence Cache. But with Coherence 12.1.2 together with GoldenGate you can capture these database changes and send updates to the Coherence Cache. This is called Coherence HotCache.

Here you can see how it basically works.


And how it works in GoldenGate.  First GoldenGate will capture all the database changes and a datapump process will send the trails to a remote GoldenGate for Java client which will update the Coherence Cache.


In this blogpost you can follow all the steps to setup your own Coherence HotCache Cluster

I also made Vagrant / VirtualBox environment which uses Puppet to create a WebLogic 12.1.2 cluster together with GoldenGate for Java 11.2.1 and also an Oracle 11.2.0.4 Database with GoldenGate 12.1.2. You only need to download your licensed software and start it up.

In this example the Coherence HotCache uses the Oracle Database HR demo schema and I also made with OEPE 12.1.2 a Coherence JPA application which we will deploy to the Dynamic WebLogic 12.1.2 Coherence Cluster.

First we need to have a working database with a WebLogic Cluster. After this you can configure GoldenGate 12.1.2 on the database server and GoldenGate Java Adapters version 11.2.1 ( V38714-01.zip downloaded from EDelivery ) on the WebLogic Admin Server machine.

Database Configuration

Next step is to configure GoldenGate on the database server.

We will create a new goldengate admin user, enable database archiving and unlock HR schema user

Make a new archive folder for the TEST database
su - oracle
mkdir -p /oracle/archives/test

Allow the oracle user to generate spool files in the GoldenGate home
su - ggate
chmod 775 /oracle/product/12.1.2/ggate

Log in as oracle and go the goldengate home
su - oracle

export ORAENV_ASK=NO;
export ORACLE_SID=test;
. oraenv

cd /oracle/product/12.1.2/ggate/

sqlplus /nolog
connect / as sysdba
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;

alter system set log_archive_dest_1='LOCATION=/oracle/archives/test' scope=both;
alter system set ENABLE_GOLDENGATE_REPLICATION=true scope=both; 
alter system set undo_retention=86400 scope=both; 

the ENABLE_GOLDENGATE_REPLICATION parameter is only for Oracle Database 11.2.0.4 or higher, this allows me to use ADD SCHEMATRANDATA in GoldenGate.

Unlock the HR demo schema
alter user hr account unlock;
alter user hr identified by hr;

Add supplemental log data
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER DATABASE FORCE LOGGING;
ALTER SYSTEM SWITCH LOGFILE;

Check if everything is Ok for GoldenGate
SELECT supplemental_log_data_min, force_logging FROM v$database;

Create the goldengate admin user and grant him the necessary rights
create tablespace ggate
  logging
  datafile '/oracle/oradata/test/ggate01.dbf' 
  size 32m 
  autoextend on 
  next 32m maxsize 2048m
  extent management local;

CREATE USER GGATE_ADMIN identified by GGATE_ADMIN
DEFAULT TABLESPACE ggate
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON GGATE;

GRANT CREATE SESSION, ALTER SESSION to GGATE_ADMIN;
GRANT ALTER SYSTEM TO GGATE_ADMIN;
GRANT CONNECT, RESOURCE to GGATE_ADMIN;
GRANT SELECT ANY DICTIONARY to GGATE_ADMIN;
GRANT FLASHBACK ANY TABLE to GGATE_ADMIN;
GRANT SELECT ON DBA_CLUSTERS TO GGATE_ADMIN;
GRANT EXECUTE ON DBMS_FLASHBACK TO GGATE_ADMIN;
GRANT SELECT ANY TRANSACTION To GGATE_ADMIN;
GRANT SELECT ON SYS.V_$DATABASE TO GGATE_ADMIN;
GRANT FLASHBACK ANY TABLE TO GGATE_ADMIN;
EXEC DBMS_GOLDENGATE_AUTH.GRANT_ADMIN_PRIVILEGE('GGATE_ADMIN');
/

Add GoldenGate repository tables and enable the capture of the DDL changes.
Give GGATE_ADMIN as input to the following GoldenGate scripts ( marker_setup, ddl_setup & role_setup )

@marker_setup.sql
@ddl_setup.sql
@role_setup.sql
GRANT GGS_GGSUSER_ROLE TO GGATE_ADMIN;
@ddl_enable

Grant select on all the HR tables to the GoldenGate schema user
GRANT SELECT ON HR.REGIONS to GGATE_ADMIN;
GRANT SELECT ON HR.DEPARTMENTS to GGATE_ADMIN;
GRANT SELECT ON HR.JOBS to GGATE_ADMIN;
GRANT SELECT ON HR.EMPLOYEES to GGATE_ADMIN;
GRANT SELECT ON HR.JOB_HISTORY to GGATE_ADMIN;
GRANT SELECT ON HR.COUNTRIES to GGATE_ADMIN;
GRANT SELECT ON HR.LOCATIONS to GGATE_ADMIN;

This finishes our database part

Coherence Application

When the WebLogic Dynamic Cluster is active we can deploy the HR Coherence demo application to the WebLogic Cluster and test if it works.

In my case the cluster nodes are running on the following IP addresses 10.10.10.100 and 10.10.10.200 and I have a Employee and Department Cache which are connected with JPA to the employees and departments tables of the HR demo schema.

You can find the Eclipse workspace project here. This workspace contains the following projects

  • HrClient, The client test project which uses wls12_remote_cache.xml to connect to the coherence cluster nodes. 
  • HrModel, JPA project with the entities and the HrModel persistence unit. 
  • HrHotCache, which is the Coherence project and will be added as a Grid Archive (GAR)
  • HrHotCacheWeb, a dummy Web Application project.
  • HrHotCacheEAR, Generates an Ear which the GAR, WAR and JPA jar 

The persistence.xml in the HrModel project has a resource entry called HrModel which uses test.oracle.com as service name and connects to 10.10.10.5 ( location of the Database server )

To test Coherence we need to have a Coherence client file which defines the cache entries and all the addresses of the Coherence nodes. We will also use this Coherence client file in GoldenGate



  
    
      Employee
      CustomRemoteCacheScheme
    
    
      Department
      CustomRemoteCacheScheme
    
  

  
    
      CustomRemoteCacheScheme 
   CustomExtendTcpCacheService
      
        10s
        
          
            
              
10.10.10.200
9099
10.10.10.100
9099
5s 500ms 5s
a Java class to retrieve department with 10 as id.

package test;

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

public class Department {

 public static void main(String[] args) {
        NamedCache department = CacheFactory.getCache("Department");
        model.Department dept =  (model.Department) department.get(10L);
        System.out.println("Department: "+dept.getDepartmentName());
 }
}

Run the Department class with an Oracle Coherence run profile ( use this coherence client file as input) and test if it works.

GoldenGate for Java configuration

Next step is to configure GoldenGate 11.2.1 for Java on the WebLogic 12.1.2 AdminServer.

Log in as the goldengate user, change directory to the goldengate home

su - ggate
cd /opt/oracle/ggate_java

export JAVA_HOME=/usr/java/jdk1.7.0_45
export PATH=${JAVA_HOME}/bin:${PATH}
export LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/amd64/server:${LD_LIBRARY_PATH}

For the GoldenGate java delivery we need to have a property file called hr-cgga.properties and add this to the dirprm folder of your GoldenGate home ( /opt/oracle/ggate_java/dirprm/hr-cgga.properties )

In the jvm.bootoptions I made the following changes:

  • My WebLogic 12.1.2 Middleware home is /opt/oracle/middleware12c, you probably need to change this to your own middleware home
  • The jar which contains the entities and the persistence.xml is and this is an export of the Eclipse HrModel project.
  • Log4j property file, Here is a link to my Log4j properties file
  • HrModel as the toplink.goldengate.persistence-unit value
  • The Coherence client file, which is the same as we used to test the Coherence Cache, Here is a link to my coherence client file


# ==================================================================== 
# List of active event handlers. Handlers not in the list are ignored. 
# ==================================================================== 
gg.handlerlist=cgga
# ==================================================================== 
# Coherence cache updater
# ==================================================================== 
gg.handler.cgga.type=oracle.toplink.goldengate.CoherenceAdapter
# ==================================================================== 
# Native JNI library properties
# ==================================================================== 
goldengate.userexit.nochkpt=true
goldengate.userexit.writers=jvm
# ======================================
# Java boot options
# ====================================== 
jvm.bootoptions=-Djava.class.path=dirprm:ggjava/ggjava.jar:/vagrant/hr/bin/hrmodel.jar:/opt/oracle/middleware12c/oracle_common/modules/oracle.jdbc_11.2.0/ojdbc6.jar:/opt/oracle/middleware12c/coherence/lib/coherence.jar:/opt/oracle/middleware12c/oracle_common/modules/javax.persistence_2.0.0.0_2-0.jar:/opt/oracle/middleware12c/oracle_common/modules/oracle.toplink_12.1.2/eclipselink.jar:/opt/oracle/middleware12c/oracle_common/modules/oracle.toplink_12.1.2/toplink-grid.jar -Xmx32M -Xms32M -Dtoplink.goldengate.persistence-unit=HrModel -Dlog4j.configuration=/vagrant/hr/log4j-default.properties -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.cacheconfig=/vagrant/hr/client-cache-config.xml -Dtangosol.coherence.ttl=0

Next we can configure GoldenGate on the WebLogic AdminServer, this GoldenGate will receive the trail from the GoldenGate client located at the Database Server.

./ggsci

CREATE SUBDIRS

Configure the manager.

status mgr
stop mgr


EDIT PARAMS MGR

Add the following content

PORT 16100
DYNAMICPORTLIST 16110-16120, 16130
AUTOSTART ER *
AUTORESTART ER *, RETRIES 4, WAITMINUTES 4

Start the manager

start mgr
status mgr

Configure the Java delivery process which will connect to the Coherence Cache

EDIT PARAMS HR-CGGA

Add the following content

EXTRACT HR-CGGA
SETENV ( GGS_USEREXIT_CONF     = "dirprm/hr-cgga.properties" )
SETENV ( GGS_JAVAUSEREXIT_CONF = "dirprm/hr-cgga.properties")
SOURCEDEFS dirdef/hr.def
CUserExit libggjava_ue.so CUSEREXIT PassThru IncludeUpdateBefores
GETUPDATEBEFORES
NoTcpSourceTimer
Table hr.*;

We will generate and copy the hr.def definition at a later time This process will listen to the jj trail which will be deliverd by the GoldenGate client on the database server

DELETE EXTRACT HR-CGGA
ADD EXTRACT HR-CGGA, EXTTRAILSOURCE dirdat/jj
exit

We will start the Java delivery process after the database GoldenGate configuration.

GoldenGate 12.1.2 configuration on the Database server

log in as ggate and change directory to the GoldenGate home
su - ggate
cd /oracle/product/12.1.2/ggate/

export ORAENV_ASK=NO;
export ORACLE_SID=test;
. oraenv

Initial GoldenGate configuration
./ggsci

ADD CREDENTIALSTORE
ALTER CREDENTIALSTORE ADD USER GGATE_ADMIN, PASSWORD GGATE_ADMIN, ALIAS gg1 
ADD MASTERKEY  gg1 
CREATE WALLET
OPEN WALLET
ADD MASTERKEY
INFO MASTERKEY ALL

exit

I did a silent install of GoldenGate 12.1.2 and this install will also start the manager plus create all the required GoldenGate directories
./ggsci
# mgr
status mgr
stop mgr

EDIT PARAMS MGR

Add the following content
PORT 16000
DYNAMICPORTLIST 16010-16020, 16030
AUTOSTART ER *
AUTORESTART ER *, RETRIES 4, WAITMINUTES 4
STARTUPVALIDATIONDELAY 5
USERIDALIAS gg1
PURGEOLDEXTRACTS dirdat/*, USECHECKPOINTS, MINKEEPHOURS 2

Start the manager
start mgr
status mgr

Next step is to configure a Classic Capture Extract
EDIT PARAMS HRTEST

Here I also do something extra like adding DDL capture and because the other GoldenGate client is not a 12.1.2 client I need to set the format release to 11.2

Add the following content
EXTRACT HRTEST
USERIDALIAS gg1
LOGALLSUPCOLS
DDL INCLUDE MAPPED
EXTTRAIL dirdat/st, FORMAT RELEASE 11.2
SEQUENCE hr.*;
TABLE hr.*;
BR BROFF
getUpdateBefores
TranLogOptions excludeUser hr

Add the extract and remove some old configuration or trails because I use an Oracle 11.2.0.4 Database and enabled the ENABLE_GOLDENGATE_REPLICATION init parameter so I can use ADD SCHEMATRANDATA hr.

START MGR
DBLOGIN USERIDALIAS gg1
STOP EXTRACT HRTEST
DELETE EXTRACT HRTEST
ADD SCHEMATRANDATA hr
ADD EXTRACT HRTEST, TRANLOG, BEGIN NOW
SHELL rm -f dirdat/st*
ADD EXTTRAIL dirdat/st, EXTRACT HRTEST
start HRTEST
You can check the output in dirrpt/HRTEST.rpt or do
./ggsci
info all

Now we can add a datapump which will send the trail to the GoldenGate Java adpater
EDIT PARAMS PJAVA

Here I will connect to adminwls.example.com and also need to set the expected format to 11.2

Add the following content
EXTRACT PJAVA
USERIDALIAS gg1
RMTHOST adminwls.example.com, MGRPORT 16100
RMTTRAIL dirdat/jj, FORMAT RELEASE 11.2
PASSTHRU
GETUPDATEBEFORES
TABLE hr.*;

Configure the datapump
DBLOGIN USERIDALIAS gg1
STOP EXTRACT PJAVA
DELETE EXTRACT PJAVA
ADD EXTRACT PJAVA, EXTTRAILSOURCE dirdat/st
ADD RMTTRAIL dirdat/jj, EXTRACT PJAVA, megabytes 100
START EXTRACT PJAVA

You can check the output in dirrpt/PJAVA.rpt or do
./ggsci
info all

Generate the HR defintions and copy this definition to the GoldenGate Java Adapter configuration

Make a new hrdefgen.prm file and add the following content
DEFSFILE ./dirdef/hr.def PURGE FORMAT RELEASE 11.2
USERIDALIAS gg1
TABLE hr.*;

Generate the HR table definitions
./defgen paramfile /vagrant/hrdefgen.prm

Copy /oracle/product/12.1.2/ggate/dirdef/hr.def to /opt/oracle/ggate_java/dirdef/hr.def ( located on the WebLogic AdminServer)

Start the Java delivery process

Go the GoldenGate Java Adapter home

Look at the output, it should load the dirdat/jj trail and connect to coherence cluster
su - ggate
cd /opt/oracle/ggate_java

export JAVA_HOME=/usr/java/jdk1.7.0_45
export PATH=${JAVA_HOME}/bin:${PATH}
export LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/amd64/server:${LD_LIBRARY_PATH}

./extract pf dirprm/hr-cgga.prm

Next time you can do it from ggsci and use start HR-CGGA

Test the HotCache configuration

Use the Coherence Test client to retrieve department 10 again.

Connect to the Oracle database and don't use the HR schema user ( very important, changes made by HR will be ignored cause coherence will also use this user to connect to the database). Update the department name of department 10.

Wait a few seconds and use the Coherence Test client again to retrieve department 10 and look if the department name has changed.

Publish the HR Database changes to a JMS Queue

As an extra we can also publish the HR database changes to a JMS Queue by creating a new property file called hr-oggq.properties and add this to /opt/oracle/ggate_java/dirprm


### oggq.properties ###
gg.handlerlist=oggjms
### Path to WebLogic jars ###
gg.classpath=/usr/java/jdk1.7.0_45/lib/tools.jar:/opt/oracle/middleware12c/wlserver/server/lib/weblogic_sp.jar:/opt/oracle/middleware12c/wlserver/server/lib/weblogic.jar:/opt/oracle/middleware12c/wlserver/server/lib/webservices.jar:
### JNDI properties
java.naming.provider.url=t3://adminwls.example.com:7001
java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
java.naming.security.principal=weblogic
java.naming.security.credentials=weblogic1
### JMS Handler
gg.handler=oggjms
gg.handler.oggjms.type=jms
gg.handler.oggjms.format=xml2
gg.handler.oggjms.format.mode=op
gg.handler.oggjms.destinationType=queue
gg.handler.oggjms.destination=HR-Queue
gg.handler.oggjms.connectionFactoryJndiName=HR-CF
### native library config ###
goldengate.userexit.nochkpt=TRUE
goldengate.userexit.timestamp=utc
goldengate.log.logname=cuserexit
goldengate.log.level=DEBUG
goldengate.log.tofile=TRUE
goldengate.userexit.writers=javawriter
javawriter.stats.display=TRUE
javawriter.stats.full=TRUE

Next we go back to ./ggsci and add this JMS extract and this will watch the same audit trail.

EDIT PARAMS HR-OGGQ

add the following content
EXTRACT HR-OGGQ
SOURCEDEFS dirdef/hr.def
CUserExit libggjava_ue.so CUSEREXIT PassThru IncludeUpdateBefores
GETUPDATEBEFORES
Table hr.*;

Add the extract
DELETE EXTRACT HR-OGGQ
ADD EXTRACT HR-OGGQ, EXTTRAILSOURCE dirdat/jj

Exit ggsci and start this JMS extract outside ggsci, so we can see all the log output
./extract pf dirprm/hr-oggq.prm

Sunday, August 11, 2013

Coherence 12.1.2 Rest application build with OEPE

With WebLogic 12.1.2 Oracle also released a new version of Coherence and OEPE. The 12.1.2 release contains many new Coherence features like WebLogic Managed Coherence Servers and Coherence Grid Archive ( GAR ) which can be included in an normal EAR. Coherence also has some nice new REST features like direct & named queries,  Custom Query engines and new Security options.
Plus with OEPE you can develop Coherence applications in Eclipse and it has Coherence editors for all the Coherence configuration files.

In this blogpost we will test these tools and features in a demo application which uses the HR Oracle demo schema, JPA and expose these entities as Coherence REST Services.

We start by downloading OEPE Eclipse runtime bundle with WebLogic 12.1.2, Coherence and ADF http://www.oracle.com/technetwork/developer-tools/eclipse/downloads/index.html

Start OEPE and define a new workspace.

Create an Oracle Coherence Application


Provide a project name and make sure you define a target runtime.  Plus enable Add project to EAR.


Use the default Coherence options.


The Coherence application will also add a dynamic Web project.


JPA Project
For this demo I will use JPA so we can use these entities in Coherence.


Also target this to WebLogic 12.1.2 and add this project to the already existing EAR project.


Define an connection to the database this will also add a persistence unit to the JPA project.


Next choose JPA entities from tables, where I select the Departments and Employees tables for this demo


Change the JPA mapping relations between the Department and Employee entities and add for REST the XML annotations.
Like @XmlRootElement(name="Department")  and @XmlTransient on the getters to break the loop of loading the Department and Employee objects.

Coherence Project
Next step is to configure the Coherence.
We can use the OEPE Coherence Editors or go directly to the source tab.


First we change coherence-cache-config.xml file where we will define the Department and Employee cache and connect this to EclipseLink.

HrJPA is the name of the Persistence Unit ( Resource Local )

Create a new file called coherence-rest-config.xml, this contains our REST entity definitions where we add some coherence named queries and enable the direct query option.

<key-class>java.lang.Integer</key-class> must match with the primary Java Data type of the entity


The last file we need to change is the pof-config.xml and add <include>coherence-rest-pof-config.xml</include> to the user-type-list

Also upload the coherence-rest.jar to the lib folder of the CoherenceJPA project.

Web project
Last step is to enable the Web project for Coherence REST. We need to enable the Oracle Coherence Facet on this Web Project.


Remove all the Coherence files located in the src folder, we don't need this.

Add the following Coherence REST Servlet.


Also we need to add the following Jersey and Jackson jars files to the WEB-INF lib folder. ( Located  in the module folder of the oracle_common )



Also create your own servlet, so we can fill the Department and Employee Coherence cache ( else the cache will be empty )



In the Eclipse Servers tab we need to add an WebLogic Domain with a Managed Coherence Server ( maybe use right click to select an WebLogic target other than the default AdminServer.


For we Coherence REST we need to change the default EclipseLink JAXB provider.

Add these parameters to Server startup arguments to the Managed Server
-Dcom.sun.xml.ws.spi.db.BindingContextFactory=com.sun.xml.ws.db.glassfish.JAXBRIContextFactory 
-Djavax.xml.bind.JAXBContext=com.sun.xml.bind.v2.ContextFactory

Publish the EAR from OEPE which also contains the Grid Archive (GAR) to the Coherence Managed Server

Finally we can test the Rest service
Start by invoking the servlet http://wls12:7201/CoherenceJPAWeb/CoherenceServlet

Next we can use a Rest Client to test all the Rest operations.

Get all the department entries
http://wls12:7201/CoherenceJPAWeb/rest/Department



Get Deparment 100
http://wls12:7201/CoherenceJPAWeb/rest/Department/100


Add a new Department
 Delete a department

Direct query ( enabled in the coherence rest config xml )
http://wls12:7201/CoherenceJPAWeb/rest/Department?q=departmentName='Finance'


Location1700 Named query also defined in the coherence rest config xml


Location Named query with a integer parameter


Here you can download or look at the github demo project.

Monday, December 29, 2008

Coherence with ADF BC ( BC4J)

Inspired by an article of Clemens I decided to make my own very fast and easy ADF BC Coherence example project ( I use in this project the HR schema tables ). This Jdeveloper 11g project is a bit different then that of Clemens. I will use not a local cache and I will use a JPA provider to fill the Coherence cache. Clemens uses an entity to fill the cache. In my case the JPA Toplink ( eclipselink) provider fills the cache. The ADF BC viewobjects reads this cache and the transactions are handled by the ADF BC entities ( my own EntitiyImpl updates or add the cache entries).
It is now very easy to use coherence with ADF BC, you don't need to program java or know much about coherence. There are only five steps to make it work.
Step 1, create an EJB entity
Step 2, change the coherence configuration xml where we will add the new entity and start Coherence on 1 or more servers
Step 3, Create an ADF BC entity ( same attributes and java types as the EJB entity ) and override the row entity class.
Step 4, Create on the just created entity a new default view and override the viewobject object.
Step 5, Fill the cache and start the ADF BC Web Application
That's all.
Here is an picture of an ADF page where I use the coherence viewobjects and I also support ADF BC master detail relation ( viewlinks)

We start by adding a new EJB entity.


Create a new persistence unit, This name must match with the coherence configuration xml

Select only one table a time, else the foreign key attributes will be replaced by relation classes

We need to change the persistence xml and add some extra jdbc properties. Coherence won't use the datasource so we need to add eclipselink.jdbc properties. Change my values with your own database values.
<?xml version="1.0" encoding="windows-1252" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
         version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="Model">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>java:/app/jdbc/jdbc/hrDS</jta-data-source>
<class>nl.whitehorses.coherence.model.Departments</class>
<class>nl.whitehorses.coherence.model.Employees</class>
<properties>
  <property name="eclipselink.target-server" value="WebLogic_10"/>
  <property name="javax.persistence.jtaDataSource"
            value="java:/app/jdbc/jdbc/hrDS"/>
  <property name="eclipselink.jdbc.driver"
            value="oracle.jdbc.OracleDriver"/>
  <property name="eclipselink.jdbc.url"
            value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
  <property name="eclipselink.jdbc.user" value="hr"/>
  <property name="eclipselink.jdbc.password" value="hr"/>
</properties>
</persistence-unit>
</persistence>

Download coherence from Oracle and put my start script and my coherence configuration file in the bin folder of coherence home
here is my configuration xml, Where I add for every ejb entity a new cache-mapping. If I add the ejb entities under the same package name and use the same persistence unit I can use the same distributed-scheme.
<?xml version="1.0" encoding="windows-1252" ?>
<cache-config>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>Employees</cache-name>
<scheme-name>jpa-distributed</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>Departments</cache-name>
<scheme-name>jpa-distributed</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>jpa-distributed</scheme-name>
<service-name>JpaDistributedCache</service-name>
<backing-map-scheme>
  <read-write-backing-map-scheme>
    <internal-cache-scheme>
      <local-scheme/>
    </internal-cache-scheme>
    <cachestore-scheme>
      <class-scheme>
        <class-name>com.tangosol.coherence.jpa.JpaCacheStore</class-name>
        <init-params>
          <init-param>
            <param-type>java.lang.String</param-type>
            <param-value>{cache-name}</param-value>
          </init-param>
          <init-param>
            <param-type>java.lang.String</param-type>
            <param-value>nl.whitehorses.coherence.model.{cache-name}</param-value>
          </init-param>
          <init-param>
            <param-type>java.lang.String</param-type>
            <param-value>Model</param-value>
          </init-param>
        </init-params>
      </class-scheme>
    </cachestore-scheme>
  </read-write-backing-map-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
</caching-schemes>
</cache-config>

Add these parameters to run options of the model and viewcontroller project.
-Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.log.level=3 -Dtangosol.coherence.cacheconfig=d:\oracle\coherence\bin\jpa-cache-config-web.xml
Now JDeveloper knows where to find the coherence the cache.

Create an new entity with the same name as the ejb entity and don't select a schema object. We will add our own attributes to this ADF BC entity.


Add at least all the mandatory attributes to this entity. These attributes needs to have the same name and java type as the ejb entity.

When we are finished we can create a new default view



I created a new EntityImpl ( Inspired by the great work of Steve and Clemens). This EntityImpl has it's own doDML. In this method I use reflection to dynamically update or add entries to the Coherence Cache.
package nl.whitehorses.adfbc.model.base;

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

import java.lang.reflect.Method;

import oracle.jbo.AttributeDef;
import oracle.jbo.server.EntityImpl;
import oracle.jbo.server.TransactionEvent;

public class CoherenceEntityImpl extends EntityImpl {


    protected void doSelect(boolean lock) {
    }

    protected void doDML(int operation, TransactionEvent e) {

        NamedCache cache =
            CacheFactory.getCache(this.getEntityDef().getName());

        if (operation == DML_INSERT || operation == DML_UPDATE) {

            try {
                Class clazz =
                    Class.forName("nl.whitehorses.coherence.model." + this.getEntityDef().getName());
                Object clazzInst = clazz.newInstance();
                AttributeDef[] allDefs =
                    this.getEntityDef().getAttributeDefs();
                for (int iAtts = 0; iAtts < allDefs.length; iAtts++) {
                    AttributeDef single = allDefs[iAtts];
                    Method m =
                        clazz.getMethod("set" + single.getName(), new Class[] { single.getJavaType() });
                    m.invoke(clazzInst,
                             new Object[] { getAttribute(single.getName()) });
                }
                cache.put(getAttribute(0), clazzInst);
            } catch (Exception ee) {
                ee.printStackTrace();
            }
        } else if (operation == DML_DELETE) {
            cache.remove(getAttribute(0));
        }
    }

}

Override the just create ADF BC Entity with this EntityImpl

Here is the ViewObjectImpl I use to override the Viewobjects
package nl.whitehorses.adfbc.model.base;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.util.ConverterCollections;
import com.tangosol.util.Filter;
import com.tangosol.util.filter.AllFilter;
import com.tangosol.util.filter.EqualsFilter;

import com.tangosol.util.filter.IsNotNullFilter;

import java.lang.reflect.Method;

import java.sql.ResultSet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import oracle.jbo.AttributeDef;
import oracle.jbo.Row;
import oracle.jbo.ViewCriteria;
import oracle.jbo.common.Diagnostic;
import oracle.jbo.server.AttributeDefImpl;
import oracle.jbo.server.ViewObjectImpl;
import oracle.jbo.server.ViewRowImpl;
import oracle.jbo.server.ViewRowSetImpl;

public class CoherenceViewObjectImpl extends ViewObjectImpl {


    private NamedCache cache;
    private Iterator foundRowsIterator;

    /**
     * executeQueryForCollection - overridden for custom java data source support.
     */
    protected void executeQueryForCollection(Object qc, Object[] params,
                                             int noUserParams) {

        cache = CacheFactory.getCache(this.getViewDef().getName());

        List filterList = new ArrayList();
        Set foundRows = null;
        
        // get the currently set view criteria
        ViewCriteria vc = getViewCriteria();
        if (vc != null) {
            Row vcr = vc.first();
            // get all attributes and check which ones are filled
            for (AttributeDef attr : getAttributeDefs()) {
                Object s = vcr.getAttribute(attr.getName());
                if (s != null && s != "") {
                    // construct an EqualsFilter
                    EqualsFilter filter =
                        new EqualsFilter("get" + attr.getName(), s);
                    // add it to the list
                    filterList.add(filter);
                }
            }
        } else if  (params != null && params.length > 0) {
            for ( int i = 0 ; i < params.length ; i++  ) {
                Object[] s = (Object[])params[i];
                // construct an EqualsFilter
                String attribute = s[0].toString();
                attribute = attribute.substring(5);
                EqualsFilter filter = new EqualsFilter("get" + attribute, s[1]);
                // add it to the list
                filterList.add(filter);
                   
            }
        }
        if (filterList.size() > 0) {
          // create the final filter set, with
          // enclosing the single filters wit an AllFilter
          Filter finalEqualsFilterArray[] =
                    (EqualsFilter[])filterList.toArray(new EqualsFilter[] { });
          AllFilter filter = new AllFilter(finalEqualsFilterArray);
          foundRows = cache.entrySet(filter);
        } else {
              IsNotNullFilter filter =  new IsNotNullFilter("get"+this.getAttributeDef(0).getName());
              foundRows = cache.entrySet(filter);
        }
        setUserDataForCollection(qc, foundRows);
        
        ConverterCollections.ConverterEntrySet resultEntrySet =
            (ConverterCollections.ConverterEntrySet)getUserDataForCollection(qc);
        foundRowsIterator = resultEntrySet.iterator();

        super.executeQueryForCollection(qc, params, noUserParams);
    }

    protected boolean hasNextForCollection(Object qc) {

        boolean retVal = foundRowsIterator.hasNext();
        if (retVal == false) {
            setFetchCompleteForCollection(qc, true);
        }
        return retVal;
    }


    /**
     * createRowFromResultSet - overridden for custom java data source support.
     */
    protected ViewRowImpl createRowFromResultSet(Object qc,
                                                 ResultSet resultSet) {

        ViewRowImpl r = createNewRowForCollection(qc);
        AttributeDefImpl[] allDefs = (AttributeDefImpl[])getAttributeDefs();
        Map.Entry entry = (Map.Entry)foundRowsIterator.next();
        // loop through all attrs and fill them from the righ entity usage
        Object dynamicPojoFromCache = entry.getValue();
        Class dynamicPojoClass = dynamicPojoFromCache.getClass();
        for (int iAtts = 0; iAtts < allDefs.length; iAtts++) {
            AttributeDefImpl single = allDefs[iAtts];
            try {
                Method m =
                    dynamicPojoClass.getMethod("get" + single.getName(), new Class[] { });
                Object result = result = m.invoke(dynamicPojoFromCache, null);
                // populate the attributes
                super.populateAttributeForRow(r, iAtts, result);
            } catch (Exception e) {
               e.printStackTrace(); 
            }
        }
        return r;
    }


    public long getQueryHitCount(ViewRowSetImpl viewRowSet) {
       if (viewRowSet.isFetchComplete()) {
          return viewRowSet.getFetchedRowCount();
       }
       Long result;
       if ( cache != null) {
           result = Long.valueOf(cache.size());
       } else {
           cache = CacheFactory.getCache(this.getViewDef().getName());
           result = Long.valueOf(cache.size());
       }
       return result;
    }


}

Override the viewobject with this ViewObjectImpl

That's all.
Here is the JDeveloper 11gR2 example on github
https://github.com/biemond/jdev11gR2_examples/tree/master/Coherence_ADF_BC

Important,

download coherence at otn and put the jars in the coherence\lib folder.
Rename the internal coherence folder of jdeveloper else you can't connect to the cache.

Update the project options with your own coherence settings, also the weblogic run options of the application.

And Start Coherence and fill the coherence cache before running.


Thursday, May 22, 2008

Employee search jsf page on Coherence with JPA

This I had this week a great coherence training for Oracle partners, where I learned a lot of interesting things about Coherence . This product is from tangosol ( Now owned byOracle ) and is a memory distributed data grid solution for clustered applications and application servers.

You may think coherence is a very complex product but it is a very easy to handle. It does everything automatically like backing up the cache on different nodes, distribute the load and recover from lost cache servers or adding new coherence servers to the cluster.
In this blog I will show how you can make a jsf employee search page on the coherence cache which get its data from a JPA datasource. Coherence runs above JPA. In this blog I will also update the cache entries with a salary raise and coherence will take care to update the right records in the employee table.
If you want to do it yourself you have to download coherence from Oracle. Add coherence.jar and tangosol.jar to the project libraries. Then we can create a entity bean ( entities from tables ( JPA / EJB3.0) on the employee table ( HR schema). Jdeveloper create a persistence.xml which we have to change with the right class and database parameters. persistence-unit name must match with the property of JpaCacheStore in the coherence jpa xml.

<?xml version="1.0" encoding="windows-1252" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="JPA">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<class>nl.ordina.coherence.model.Employees</class>
<properties>
<property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
<property name="toplink.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
<property name="toplink.jdbc.user" value="hr"/>
<property name="toplink.jdbc.password" value="hr"/>
</properties>
</persistence-unit>
</persistence>
Next I created jpa-cache-config-web.xml

<?xml version="1.0" encoding="windows-1252" ?>
<cache-config>
<caching-scheme-mapping>
<cache-mapping>
<!-- Set the name of the cache to be the entity name -->
<cache-name>Employees</cache-name>
<!-- Configure this cache to use the scheme defined below -->
<scheme-name>jpa-distributed</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>jpa-distributed</scheme-name>
<service-name>JpaDistributedCache</service-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<internal-cache-scheme>
<local-scheme/>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>com.tangosol.coherence.jpa.JpaCacheStore</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>{cache-name}</param-value>
</init-param>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>nl.ordina.coherence.model.{cache-name}</param-value>
</init-param>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>JPA</param-value>
</init-param>
</init-params>
</class-scheme>
</cachestore-scheme>
</read-write-backing-map-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
</caching-schemes>
</cache-config>

Add this file to the run options of the cache-server.cmd and the jdeveloper web project runtime options. -Dtangosol.coherence.cacheconfig=D:\oracle\coherence\bin\jpa-cache-config-web.xml
Now we can load the Employees cache. Do this with the following code
private NamedCache employees = CacheFactory.getCache("Employees");
For searching through the cache for the right employees I use a filter.

public void search(ActionEvent actionEvent) {

Filter filter = null;
if ( firstName.getValue() != null && lastName.getValue() != null ) {
filter = new OrFilter( new LikeFilter("getFirstName", firstName.getValue().toString())
, new LikeFilter("getLastName" , lastName.getValue().toString()));
} else if ( firstName.getValue() != null && lastName.getValue() == null ) {
filter = new LikeFilter("getFirstName", firstName.getValue().toString());
} else if ( firstName.getValue() == null && lastName.getValue() != null ) {
filter = new LikeFilter("getLastName", lastName.getValue().toString());
} else {
seeAll(actionEvent);
return;
}

Set empSet = employees.entrySet(filter);

int size = empSet.size();
List list = new ArrayList();

for (Iterator it = empSet.iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry)it.next();
Employees empl = (Employees)entry.getValue();
list.add(empl);
}
model = new ArrayDataModel(list.toArray());
}

To update the employee records. I first need to have a class which I can invoke on the cache

package nl.ordina.coherence.backing;

import com.tangosol.util.processor.AbstractProcessor;
import com.tangosol.util.InvocableMap.Entry;
import nl.ordina.coherence.model.Employees;

public class RaiseSalary extends AbstractProcessor {
public RaiseSalary() {
}

public Object process(Entry entry ) {
Employees emp = (Employees)entry.getValue();
emp.setSalary(emp.getSalary() * 1.10);
entry.setValue(emp);
return null;
}
}


We invoke this by the following statement employees.invokeAll(AlwaysFilter.INSTANCE, new RaiseSalary());. Coherence updates the affected records.
Here you can download the example project. You have to start 1 cache server on your network and you can start the webapp on different clients. Coherence connects automatically to the cache server. You can search the cache immediately. Start for example more cache servers and look at the timing. And update the cache by doing a salary raise on 1 webapp and query the changed employees in the other webapp.

To run this example you have to change some files.
Change the path parameters with the right folders of jpa-cache-server-web2.cmd file (this included in the zip) and the classpath of the employee bean
:launch
set java_opts="-Xms%memory% -Xmx%memory% -Dtangosol.coherence.cacheconfig=D:\oracle\coherence\bin\jpa-cache-config-web.xml"
"%java_exec%" -server -showversion "%java_opts%" -cp "%coherence_home%\lib\coherence.jar;%coherence_home%\lib\coherence-jpa.jar;D:\oracle\jdevstudio10133\jdbc\lib\ojdbc14.jar;D:\oracle\jdevstudio10133\toplink\jlib\toplink-essentials.jar;D:\projecten\workspace\10.1.3.3\coherence\web\public_html\WEB-INF\classes" com.tangosol.net.DefaultCacheServer %1
Change the run options and the libraries of the project and off course the persistence.xml for the database parmaters.