Sunday, September 7, 2008

Eclipse Link and Oracle Coherence integration using Spring

For the last few days I have an intresting journey into Oracle Coherence. Spring being one of the most powerful and widely accepted framework, I thought of playing with it. The belwo article is an outocome of my journey ....

EclipseLink and Coherence can be used together in several combinations. This blog discusses, how to integrate using Spring framework.These options including using Coherence as a Eclipse Link plug-in, using Eclipse Link as a Coherence plug-in via the CacheStore interface and bulk-loading Coherence caches from a Eclipse Link query. Most applications that use Coherence and EclipseLink would like to use a mixture of these approaches. The Eclipse Link API features powerful management of entities and relationships, and the Coherence API delivers maximum performance and scalability.

Aim:

1. To make Oracle Coherence as the L2 shared cache using spring framework.








CacheStore/CachLoader:

Orache Coherence provides above two interfaces based on the Jcache specifications. Jcache specifies API and semantics for temporary, in memory caching of Java objects, including object creation, shared access, spooling, invalidation, and consistency across JVM's.

In order to make Oracle Coherence as the distributed cache, as a first step it is important to make the CacheStore and CacheLoader which will do

· Store/Update/Delete the java object in the oracle coherence

· Retrieve object from the cache.

So lets make two classes EclipseLinkCacheStore which implements CacheStore and EclipseLinkCacheLoder which implements the CacheStore, as shown below:-

public class OhiEclipseLinkEntityCacheLoader extends Base implements CacheLoader {

// Will be injected using spring

private EntityManagerFactory emf;

private EntityManager em;

protected String m_sEntityName = "";

protected Class m_sEntityClass;

public void setEntityName(String entityName) {

this.m_sEntityName = entityName;

}

public void setEntityClass(Class entityClass) {

this.m_sEntityClass = entityClass;

}

public OhiEclipseLinkEntityCacheLoader(EntityManagerFactory emf) {

this.emf = emf;

// Create a new Entity manager to avoid any

// conflicts with the existing Enity manager

// which is using L1 cache

this.em = emf.createEntityManager();

}

- - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - -

// Implement other inteface methods

}

public class OhiEclipseLinkEntityCacheStore extends OhiEclipseLinkEntityCacheLoader implements CacheStore {

private EntityManagerFactory emf;

private String name="";

public OhiEclipseLinkEntityCacheStore(EntityManagerFactory emf) {

super(emf);

- - - - - - - -

- - - - - - -

// Implement other inteface methods

}

Spring can inject the Enity Manager as shown below: -

class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">

value="org.eclipse.persistence.platform.database.oracle.OraclePlatform" />

class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" />

class="org.springframework.jdbc.datasource.DriverManagerDataSource">

value="oracle.jdbc.pool.OracleDataSource" />

value="jdbc:oracle:thin:@xxxx:1521:XE" />

class="org.springframework.orm.jpa.JpaTransactionManager">

ref="entityManagerFactory" />

class="com.oracle.healthinsurance.eclipselink.cachestore.OhiEclipseLinkEntityCacheStore"

scope="prototype">

What’s next?

Lets create spring aware cache store which can pick up the oracle coherence configure files (in a more declrative way rather than passing it using –D options as specified in the oracle coherence documentation)

SpringAwareCacheStore provides a facility to access caches declared in a "cache-config.dtd" compliant configuration file, similar to its super class (DefaultConfigurableCacheFactory). In addition, this factory provides the ability to reference beans in a Spring application context via the use of a class-scheme element. This factor is made ApplicationContextAware so that it will very trivial to get can access to BeanFactory. This can be useful for stand-alone JVMs such as cache servers. It can also be configured at runtime with a pre-configured Spring bean factory. This can be useful for Coherence applications running in an environment that is itself responsible for tarting the Spring bean factory, such as a web container.

The following the code snippet which further extends the spring aware cache factory mentioned in the Oracle Coherence documentations

public class SpringAwareCacheStore extends DefaultConfigurableCacheFactory

implements BeanFactoryAware, ApplicationContextAware, InitializingBean

/**

* This constructor automatically figure out the absolute path to the spring

* configuraion file using DiscoveryUtils. Hence as result cache config file

* can be appeneded at runtime.

*

* If this construtor is used the bean factory is set in the afterProperties

* set method.

*

* @param sPath

*/

public SpringAwareCacheStore(String sPath) {

super(DiscoverUtils.getPathToResouce(sPath));

}

/**

* Appliction context. This application context will be required to get the

* BeanFactory in case this class is instantiated with the constructor which

* take the cache file name.

*/

private ApplicationContext appContext;

public void setApplicationContext(ApplicationContext applicationContext)

throws BeansException {

this.appContext = applicationContext;

}

public void afterPropertiesSet() throws Exception {

if (m_beanFactory == null) {

m_beanFactory = appContext.getAutowireCapableBeanFactory();

// register a shutdown hook so the bean factory cleans up

// upon JVM exit

((AbstractApplicationContext) m_beanFactory).registerShutdownHook();

}

}

// ----- extended methods -----------------------------------------------

// Picked up from the Oracle Coherence documentation

/**

* Create an Object using the "class-scheme" element.

In addition to

* the functionality provided by the super class, this will retreive an

* object from the configured Spring BeanFactory for class names that use

* the following format:

*

*

* <class-name>spring-bean:sampleCacheStore</class-name>

*

*

* Parameters may be passed to these beans via setter injection as well:

*

*

* <init-params>

* <init-param>

* <param-name>setEntityName</param-name>

* <param-value>{cache-name}</param-value>

* </init-param>

* </init-params>

*

*

* Note that Coherence will manage the lifecycle of the instantiated Spring

* bean, therefore any beans that are retreived using this method should be

* scoped as a prototype in the Spring configuration file, for example:

*

*

* <bean id="sampleCacheStore"

* class="com.company.SampleCacheStore"

* scope="prototype"/>

*

*

* @param info

* the cache info

* @param xmlClass

* "class-scheme" element.

* @param context

* BackingMapManagerContext to be used

* @param loader

* the ClassLoader to instantiate necessary classes

*

* @return a newly instantiated Object

*

* @see DefaultConfigurableCacheFactory#instantiateAny( CacheInfo,

* XmlElement, BackingMapManagerContext, ClassLoader)

*/

protected Object instantiateAny(CacheInfo info, XmlElement xmlClass,

BackingMapManagerContext context, ClassLoader loader) {

if (translateSchemeType(xmlClass.getName()) != SCHEME_CLASS) {

throw new IllegalArgumentException("Invalid class definition: "

+ xmlClass);

}

String sClass = xmlClass.getSafeElement("class-name").getString();

if (sClass.startsWith(SPRING_BEAN_PREFIX)) {

String sBeanName = sClass.substring(SPRING_BEAN_PREFIX.length());

azzert(sBeanName != null && sBeanName.length() > 0,

"Bean name required");

XmlElement xmlParams = xmlClass.getElement("init-params");

XmlElement xmlConfig = null;

if (xmlParams != null) {

xmlConfig = new SimpleElement("config");

XmlHelper.transformInitParams(xmlConfig, xmlParams);

}

Object oBean = getBeanFactory().getBean(sBeanName);

if (xmlConfig != null) {

for (Iterator iter = xmlConfig.getElementList().iterator(); iter

.hasNext();) {

XmlElement xmlElement = (XmlElement) iter.next();

String sMethod = xmlElement.getName();

String sParam = xmlElement.getString();

try {

ClassHelper.invoke(oBean, sMethod,

new Object[] { sParam });

} catch (Exception e) {

ensureRuntimeException(e, "Could not invoke " + sMethod

+ "(" + sParam + ") on bean " + oBean);

}

}

}

return oBean;

} else {

return super.instantiateAny(info, xmlClass, context, loader);

}

}

How my Oracle Coherene config file looks like?

Using the above factory the cache config file can be put in the class path and the above factory class will automatically pick it and lauch the Oracle Cohernece.

Here is the sample config file??

*

ohi-cache-distributed

ohi-cache-distributed

spring-bean:entityCacheStore

setEntityName

{cache-name}

5s

true

Below is the spring magic which lanches the above oracle coherence

class="com.oracle.healthinsurance.cache.factory.SpringAwareCacheStore">

value="/META-INF/spring/coherence-cache-config.xml" />

class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">

value="com.tangosol.net.CacheFactory" />

value="setConfigurableCacheFactory" />

This is equivalent to the code

BeanFactory bf = applicationContext.getBeanFactory();

SpringAwareCacheStore scf = new SpringAwareCacheStore /META-INF/spring/",bf);

scf.setBeanFactory(applicationContext.getBeanFactory());

CacheFactory.setConfigurableCacheFactory(scf);

Here my sample Unit test case (based on AbstractJpaTests provided by spring)

NamedCache cache = CacheFactory.getCache("CodFlexCodeSystemsB");

Assert.assertNotNull(cache);

CodFlexCodeSystemsB obj = (CodFlexCodeSystemsB) cache.get(new Long(10001));

System.err.println(">>>" + obj.getSubtype());

Assert.assertNotNull(obj);

obj.setSubtype("SIFC");

cache.put(new Long(10001), obj);

try {

Thread.sleep(8000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

CodFlexCodeSystemsB obj1 = (CodFlexCodeSystemsB) cache.get(new Long(10001));

System.err.println(obj1.getId());

System.err.println(obj1.getSubtype());

System.err.println("size " + obj1.getCodFlexCodeSystemsTlList().size());

Conclusion

Using spring its very simple to launch oracle coherence as L2 cache and make it enity store. But care should be taken not put all the entity in oracle coherence as it will be negative affect on the performance. After this integration its very simple to exploit the oracle coherence powerful feature like Filters,Query etc.