/*
* SpringAwareCacheFactory.java
*
* Copyright 2001-2007 by Oracle. All rights reserved.
*
* Oracle is a registered trademarks of Oracle Corporation and/or its affiliates.
*
* This software is the confidential and proprietary information of
* Oracle Corporation. You shall not disclose such confidential and
* proprietary information and shall use it only in accordance with the
* terms of the license agreement you entered into with Oracle.
*
* This notice may not be removed or altered.
*/
package com.bagri.xdm.process.coherence.factory;
import com.bagri.common.bean.SpringBeanProvider;
import com.bagri.xdm.access.coherence.impl.CoherenceXDMFactory;
import com.bagri.xdm.common.XDMHelper;
import com.oracle.coherence.environment.extensible.ExtensibleEnvironment;
import com.tangosol.net.BackingMapManagerContext;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.ConfigurableCacheFactory;
import com.tangosol.run.xml.SimpleElement;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;
import com.tangosol.util.ClassHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
//import javax.annotation.Nonnull;
/**
* 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.
* <p/>
* 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 pre-configured
* 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 ExtensibleEnvironment implements ApplicationContextAware, SpringBeanProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringAwareCacheFactory.class);
// ----- constructors -------------------------------------------------
/**
* Construct a default DefaultConfigurableCacheFactory using the
* default configuration file name.
*/
public SpringAwareCacheFactory() {
super();
instance = this;
//initXDMFactory();
LOG.info("<init 1>");
}
/**
* Construct a default DefaultConfigurableCacheFactory using the
* default configuration file name.
*
* @param sCacheConfig location of a cache configuration
*/
public SpringAwareCacheFactory(String sCacheConfig) {
super(sCacheConfig);
instance = this;
//initXDMFactory();
LOG.info("<init 2>. config: {}", sCacheConfig);
}
/**
* 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);
//initXDMFactory();
instance = this;
LOG.info("<init 3>. config: {}; context: {}", sCacheConfig, sAppContext);
initializeContext(sCacheConfig, sAppContext);
}
// ----- extended methods -----------------------------------------------
/**
* Create an Object using the "class-scheme" element.
* <p/>
* In addition to the functionality provided by the super class, this will retrieve an object from the configured
* Spring BeanFactory for class names that use the following format:
* <p/>
* <pre>
* <class-name>spring-bean:sampleCacheStore</class-name>
* </pre>
* <p/>
* Parameters may be passed to these beans through setter injection as well:
* <p/>
* <pre>
* <init-params>
* <init-param>
* <param-name>setEntityName</param-name>
* <param-value>{cache-name}</param-value>
* </init-param>
* </init-params>
* </pre>
* <p/>
* 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:
* <p/>
* <pre>
* <bean id="sampleCacheStore"
* class="com.company.SampleCacheStore"
* scope="prototype"/>
* </pre>
*
* @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
*/
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());
LOG.trace("instantiateAny. going to instantiate: {}", sBeanName);
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 = appContext.getBean(sBeanName);
if (xmlConfig != null) {
for (Object o : xmlConfig.getElementList()) {
XmlElement xmlElement = (XmlElement) o;
String sMethod = xmlElement.getName();
String sParam = xmlElement.getString();
try {
LOG.trace("instantiateAny. about to invoke {}.{}()...", oBean, sMethod);
ClassHelper.invoke(oBean, sMethod, new Object[]{sParam});
LOG.trace("instantiateAny. {}.{}() invoked successfully", oBean, sMethod);
} catch (Exception e) {
throw ensureRuntimeException(e, "Could not invoke " + sMethod + "(" + sParam + ") on bean " + oBean);
}
}
}
LOG.trace("instantiateAny. returning: {}", oBean);
return oBean;
} else {
return super.instantiateAny(info, xmlClass, context, loader);
}
}
/**
* @param beanId Bean ID
* @param cl Class
* @param <T> Type class
* @return SpringBean
*/
public <T> T getSpringBean(String beanId, Class<T> cls) {
LOG.trace("getSpringBean. looking for: {}", beanId);
return appContext.getBean(beanId, cls);
}
/**
* Creates bean instance from static application context
*
* @param beanId id of bean
* @return instance
*/
public static Object instantiateSpringBean(String beanId) {
LOG.trace("instantiateSpringBean. instantiating: {}", beanId);
return appContext.getBean(beanId);
}
/**
* /**
* Creates bean instance from static application context
*
* @param beanId id of bean
* @param cl Class
* @param <T> Type class
* @return instance
*/
public static <T> T instantiateSpringBean(String beanId, Class<T> cl) {
LOG.trace("instantiateSpringBean. instantiating: {}", beanId);
return appContext.getBean(beanId, cl);
}
//public Service ensureService(String serviceName) {
// LOG.trace("ensureService. serviceName: {}", serviceName);
// Service result = super.ensureService(serviceName);
// LOG.trace("ensureService. returning: {}", result);
// return result;
//}
/**
* Returns static application context
*
* @return static application context
*/
public static ApplicationContext getApplicationContext() {
LOG.trace("getApplicationContext. returning: {}", appContext);
return appContext;
}
/**
* Setter for static application context
*
* @param applicationContext application context
* @throws BeansException in case of error
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// Assign the ApplicationContext into a static variable
LOG.info("setApplicationContext. old: {} new: {}", appContext, applicationContext);
appContext = applicationContext;
}
// ----- data fields ----------------------------------------------------
private static ConfigurableCacheFactory instance = null;
public static ConfigurableCacheFactory getFactory() {
return instance;
}
/**
* Spring ApplicationContext used by this CacheFactory
*/
private static ApplicationContext appContext = null;
private static synchronized void initializeContext(String sCacheConfig, String sAppContext) {
if (appContext == null) {
azzert(sAppContext != null && sAppContext.length() > 0, "Application context location required");
appContext = sCacheConfig.startsWith("file:") ?
new FileSystemXmlApplicationContext(sAppContext) :
new ClassPathXmlApplicationContext(sAppContext);
// register a shutdown hook so the bean factory cleans up
// upon JVM exit
((AbstractApplicationContext) appContext).registerShutdownHook();
}
}
/**
* Prefix used in cache configuration "class-name" element to indicate
* this bean is in Spring
*/
private static final String SPRING_BEAN_PREFIX = "spring-bean:";
private static final Logger LOG = LoggerFactory.getLogger(SpringAwareCacheFactory.class);
/**
* return non-null bean or throws IllegalStateException
*
* @param beanId bean's id
* @param cl bean's class
* @return IllegalStateException if fails to retrieve bean
*/
//@Nonnull
public static <T> T getBeanOrThrowException(String beanId, Class<T> cl) {
final ConfigurableCacheFactory factory = CacheFactory.getConfigurableCacheFactory();
if (factory instanceof SpringBeanProvider) {
final T bean = ((SpringBeanProvider) factory).getSpringBean(beanId, cl);
if (bean != null) {
return bean;
}
}
throw new IllegalStateException();
}
//private void initXDMFactory() {
//XDMHelper.registerXDMFactory(new CoherenceXDMFactory());
//LOG.debug("initXDMFactory. registered: {}", XDMHelper.getXDMFactory());
//}
}