Skip Headers
Oracle® Coherence Integration Guide for Oracle Coherence
Release 3.7.1

Part Number E22621-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

4 Integrating Spring with Coherence

Spring is a platform for building and running Java EE applications. This chapter describes how to configure the Oracle Coherence cache to be available to applications that run on the Spring platform.

Coherence CacheFactory static factory methods allow you to access all Coherence caches and services. These methods, (such as getCache), delegate to a ConfigurableCacheFactory interface, which can be plugged in by using the CacheFactory.setConfigurableCacheFactory method or the operational override file (tangosol-coherence-override.xml).

The Coherence cache configuration file (coherence-cache-config.xml), provides the class-scheme element, where you can specify your own implementations of Coherence interfaces, such as CacheStore and MapListener. Coherence can instantiate your implementations in either of two ways: it can create a new instance by using the new operator, or it can invoke a user-provided factory method.

For some applications, it may be useful for Coherence to retrieve objects configured in a class-scheme element from a Spring BeanFactory instance instead of creating its own instance. This is especially true for cache servers configured with CacheStore objects running in a standalone JVM, because these CacheStore objects typically must be configured with data sources, connection pools, and so on. Spring provides easy configuration of data sources for plain Java objects.

The SpringAwareCacheFactory interface is a custom ConfigurableCacheFactory that can delegate class scheme bean instantiations to a Spring BeanFactory instance. It has two modes of operation:

4.1 Configuring Coherence for a Spring-Aware Cache Factory

To configure Coherence to use the SpringAwareCacheFactory instance, the XML code in Example 4-1 should be placed in the operational override file (tangosol-coherence-override.xml). By default, this specifies coherence-cache-config.xml as the cache configuration file and application-context.xml as the Spring configuration file.

Example 4-1 Configuring the Cache to Use SpringAwareCacheFactory

...
<configurable-cache-factory-config>
  <class-name system-property="tangosol.coherence.cachefactory">
    com.tangosol.coherence.spring.SpringAwareCacheFactory
  </class-name>
  <init-params>
    <init-param>
      <param-type>java.lang.String</param-type>
      <param-value system-property="tangosol.coherence.cacheconfig">
         coherence-cache-config.xml
      </param-value>
    </init-param>
    <init-param id="1">
      <param-type>java.lang.String</param-type>
      <param-value system-property="tangosol.coherence.springconfig">
         application-context.xml
      </param-value>
    </init-param>
  </init-params>
</configurable-cache-factory-config>
...

As an alternative to using the configuration file, the SpringAwareCacheFactory instance can be configured programmatically as illustrated in Example 4-2:

Example 4-2 Configuring SpringAwareCacheFactory Programmatically

BeanFactory             bf  = ...
SpringAwareCacheFactory scf = new SpringAwareCacheFactory();
        
scf.setBeanFactory(bf);
CacheFactory.setConfigurableCacheFactory(scf);

Because the SpringAwareCacheFactory instance is BeanFactoryAware, it can also be defined in an application context, as shown in Example 4-3:

Example 4-3 Defining a SpringAwareCacheFactory Instance in an Application Context

<bean id="cacheFactory"
      class="com.tangosol.coherence.spring.SpringAwareCacheFactory">
</bean>

Taking this a step further, the Coherence CacheFactory instance can be configured inside of the application context, as shown in Example 4-4:

Example 4-4 Configuring a CacheFactory Instance in an Application Context

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetClass" value="com.tangosol.net.CacheFactory"/>
  <property name="targetMethod" value="setConfigurableCacheFactory"/>
  <property name="arguments" ref="cacheFactory"/>
</bean>

The application context can have a CacheStore instance configured as in Example 4-5. Note that the EntityCacheStore instance is scoped as prototype. This value is specified because Coherence will manage the life cycle of the bean when it is retrieved from Spring, just as if Coherence had instantiated the object using the new operator.

Example 4-5 Configuring a CacheStore Instance in an Application Context

<bean id="dataSource" class="...">
...
</bean>

<bean id="sessionFactory" class="...">
  <property name="dataSource" ref="dataSource"/>
  ...
</bean>
  
<bean id="entityCacheStore" 
      class="com.company.app.EntityCacheStore" scope="prototype">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

Coherence can use the entityCacheStore bean as illustrated in Example 4-6. By specifying the init-param element, setter injection can be used to set properties on the bean retrieved from Spring. The bean will have the method setEntityName invoked with the cache name before it is used by Coherence.

Example 4-6 Configuring Setter Injection to Set Properties on the Bean

<?xml version="1.0"?>

<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
              xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
  <caching-scheme-mapping>
    <cache-mapping>
      <cache-name>com.company.app.domain.*</cache-name>
      <scheme-name>distributed-domain</scheme-name>
    </cache-mapping>
  </caching-scheme-mapping>

  <caching-schemes>
    <distributed-scheme>
      <scheme-name>distributed-domain</scheme-name>
      <backing-map-scheme>
        <read-write-backing-map-scheme>
          <internal-cache-scheme>
            <local-scheme />
          </internal-cache-scheme>
          <cachestore-scheme>
            <class-scheme>
              <class-name>spring-bean:entityCacheStore</class-name>
              <init-params>
                <init-param>
                  <param-name>setEntityName</param-name>
                  <param-value>{cache-name}</param-value>
                </init-param>
              </init-params>
            </class-scheme>
          </cachestore-scheme>
          <write-delay>5s</write-delay>
        </read-write-backing-map-scheme>
      </backing-map-scheme>
      <autostart>true</autostart>
    </distributed-scheme>
  </caching-schemes>
</cache-config>

4.2 SpringAwareCacheFactory API

Example 4-7 lists the Java source code for SpringAwareCacheFactory. At a minimum, it requires Coherence 3.4.n and Spring 2.n.

Example 4-7 Updated Sample for a Spring-Aware Cache Factory

package com.tangosol.coherence.spring;
 
import com.tangosol.net.BackingMapManagerContext;
import com.tangosol.net.DefaultConfigurableCacheFactory;
 
import com.tangosol.run.xml.SimpleElement;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;
 
import com.tangosol.util.ClassHelper;
 
import java.util.Iterator;
 
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
 
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
 
 
/**
* SpringAwareCacheFactory provides a facility to access caches declared
* in a "cache-config.dtd" compliant configuration file, similar to its super
* class {@link DefaultConfigurableCacheFactory}.  In addition, this factory
* provides the ability to reference beans in a Spring application context
* through the use of a class-scheme element.
*
* This factory can be configured to start its own Spring application
* context from which to retrieve these beans.  This can be useful for standalone
* JVMs such as cache servers.  It can also be configured at run time with a
* preconfigured Spring bean factory.  This can be useful for Coherence
* applications running in an environment that is itself responsible for starting
* the Spring bean factory, such as a web container.
*
* @see #instantiateAny(CacheInfo, XmlElement,
        BackingMapManagerContext, ClassLoader)
*
*/
public class SpringAwareCacheFactory
        extends DefaultConfigurableCacheFactory
        implements BeanFactoryAware
    {
    // ----- constructors -------------------------------------------------
 
    /**
    * Construct a default DefaultConfigurableCacheFactory using the
    * default configuration file name.
    */
    public SpringAwareCacheFactory()
        {
        super();
        }
 
    /**
    * Construct a SpringAwareCacheFactory using the specified path to
    * a "cache-config.dtd" compliant configuration file or resource.  This
    * will also create a Spring ApplicationContext based on the supplied
    * path to a Spring compliant configuration file or resource.
    *
    * @param sCacheConfig location of a cache configuration
    * @param sAppContext  location of a Spring application context
    */
    public SpringAwareCacheFactory(String sCacheConfig, String sAppContext)
        {
        super(sCacheConfig);
 
        azzert(sAppContext != null && sAppContext.length() > 0,
                "Application context location required");
 
        m_beanFactory = sCacheConfig.startsWith("file:") ? (BeanFactory)
            new FileSystemXmlApplicationContext(sAppContext) :
            new ClassPathXmlApplicationContext(sAppContext);
 
        // register a shutdown hook so the bean factory cleans up
        // upon JVM exit
        ((AbstractApplicationContext) m_beanFactory).registerShutdownHook();
        }
 
    /**
    * Construct a SpringAwareCacheFactory using the specified path to
    * a "cache-config.dtd" compliant configuration file or resource and
    * the supplied Spring BeanFactory.
    *
    * @param sPath       the configuration resource name or file path
    * @param beanFactory Spring BeanFactory used to load Spring beans
    */
    public SpringAwareCacheFactory(String sPath, BeanFactory beanFactory)
        {
        super(sPath);
 
        m_beanFactory = beanFactory;
        }
 
 
    // ----- extended methods -----------------------------------------------
 
    /**
    * 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:
    * 
    * &lt;class-name&gt;spring-bean:sampleCacheStore&lt;/class-name&gt;
    * 
    *
    * Parameters may be passed to these beans through setter injection as well:
    * 
    *   &lt;init-params&gt;
    *     &lt;init-param&gt;
    *       &lt;param-name&gt;setEntityName&lt;/param-name&gt;
    *       &lt;param-value&gt;{cache-name}&lt;/param-value&gt;
    *     &lt;/init-param&gt;
    *   &lt;/init-params&gt;
    * 
    *
    * Note that Coherence will manage the lifecycle of the instantiated Spring
    * bean, therefore any beans that are retrieved using this method should be
    * scoped as a prototype in the Spring configuration file, for example:
    * 
    *   &lt;bean id="sampleCacheStore"
    *         class="com.company.SampleCacheStore"
    *         scope="prototype"/&gt;
    * 
    *
    * @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) 
    */
    public 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);
            }
        }
 
    /**
    * Get the Spring BeanFactory used by this CacheFactory.
    * @return the Spring {@link BeanFactory} used by this CacheFactory
    */
    public BeanFactory getBeanFactory()
        {
        azzert(m_beanFactory != null, "Spring BeanFactory == null");
        return m_beanFactory;
        }
 
    /**
    * Set the Spring BeanFactory used by this CacheFactory.
    * @param beanFactory the Spring {@link BeanFactory} used by this CacheFactory
    */
    public void setBeanFactory(BeanFactory beanFactory)
        {
        m_beanFactory = beanFactory;
        }
 
 
    // ----- data fields ----------------------------------------------------
 
    /**
    * Spring BeanFactory used by this CacheFactory
    */
    private BeanFactory m_beanFactory;
 
    /**
    * Prefix used in cache configuration "class-name" element to indicate
    * this bean is in Spring.
    */
    private static final String SPRING_BEAN_PREFIX = "spring-bean:";
    }