9/11

# 3 Integrating Oracle Coherence with Spring

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 implementatons 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:

- It can instantiate its own ApplicationContext instance with a provided configuration file. This is useful for cache servers that require beans from a Spring container.

- A `BeanFactory` instance can be provided to it at run time. This is useful for Coherence applications running in a container that already has an existing `BeanFactory` instance.

## 3.1 Configuring Coherence for a Spring-Aware Cache Factory

To configure Coherence to use the `SpringAwareCacheFactory` instance, the XML code in Example 3-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 3-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 3-2:

Example 3-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 3-3:

Example 3-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 3-4:

Example 3-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 3-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 3-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 3-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 3-6 Configuring Setter Injection to Set Properties on the Bean

    <?xml version="1.0"?>

    <!DOCTYPE cache-config SYSTEM "cache-config.dtd">

    <cache-config>
      <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>

## 3.2 SpringAwareCacheFactory API

Example 3-7 lists the Java source code for `SpringAwareCacheFactory`. It requires Coherence 3.4.n and Spring 2.n.

Example 3-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:";
        }