Pages

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.

5 comments:

  1. Hi
    This is very good example.

    I am actually looking for coherence training and couldnt find many.
    Please can you suggest any from your side?

    -Ram

    ReplyDelete
  2. Hi,

    Can we config global datasource in persistence.xml rather than use db connection string?

    Thanks
    Julia

    ReplyDelete
  3. Hi,

    it depends if coherence can run on the j2ee server. Coherence is now part of weblogic installation but I don't if it runs on the same jvm.

    maybe you can ask this in the oracle coherence forum.

    thanks

    ReplyDelete
  4. Hi,
    I want to build web service which calls coherence using JPA. how can it be possible.

    Thanks
    Dev

    ReplyDelete
    Replies
    1. Hi,

      sorry you need to call coherence which calls JPA. Maybe you can make your own db layer under JPA. but I Don't think it is possible cause JPA generates SQL and coherence can't handle this.

      thanks

      Delete