package org.infinispan.spring.support.embedded;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/**
* <p>
* A {@link org.springframework.beans.factory.FactoryBean <code>FactoryBean</code>} for creating a
* native {@link #setCacheName(String) named} Infinispan {@link org.infinispan.Cache
* <code>org.infinispan.Cache</code>}, delegating to a
* {@link #setInfinispanEmbeddedCacheManager(EmbeddedCacheManager) <code>configurable</code>}
* {@link org.infinispan.manager.EmbeddedCacheManager
* <code>org.infinispan.manager.EmbeddedCacheManager</code>}. If no cache name is explicitly set,
* this <code>FactoryBean</code>'s {@link #setBeanName(String) <code>beanName</code>} will be used
* instead.
* </p>
* <p>
* Beyond merely creating named <code>Cache</code> instances, this <code>FactoryBean</code> offers
* great flexibility in configuring those <code>Caches</code>. It has setters for all non-global
* configuration settings, i.e. all settings that are specific to a single <code>Cache</code>. The
* configuration settings thus defined override those settings obtained from the
* <code>EmbeddedCacheManager</code>.
* </p>
* <p>
* There are different configuration {@link #setConfigurationTemplateMode(String)
* <code>modes</code>} that control with what <code>Configuration</code> to start before further
* customizing it as described above:
* <ul>
* <li>
* <code>NONE</code>: Configuration starts with a new <code>Configuration</code> instance.
* Note that this mode may only be used if no named configuration having the same name as the <code>Cache</code>
* to be created already exists. It is therefore illegal to use this mode to create a <code>Cache</code> named, say,
* "cacheName" if the configuration file used to configure the
* <code>EmbeddedCacheManager</code> contains a configuration section named
* "cacheName".</li>
* <li>
* <code>DEFAULT</code>: Configuration starts with the <code>EmbeddedCacheManager</code>'s
* <em>default</em> <code>Configuration</code> instance, i.e. the configuration settings defined
* in its configuration file's default section. Note that this mode may only be used if no named configuration
* having the same name as the <code>Cache</code> to be created already exists. It is therefore illegal to use
* this mode to create a <code>Cache</code> named, say, "cacheName" if the configuration file used
* to configure the <code>EmbeddedCacheManager</code> contains a configuration section named
* "cacheName".</li>
* <li>
* <code>CUSTOM</code>: This is where a user will provide a custom-built <code>ConfigurationBuilder</code>
* object which will be used to configure a <code>Cache</code> instance. If a {@link #setCacheName(String)} has
* already been called, then that name will be used.
* </li>
* <li>
* <code>NAMED</code>: Configuration starts with the <code>EmbeddedCacheManager</code>'s
* <code>Configuration</code> instance having the same name as the <code>Cache</code> to be
* created. For a <code>Cache</code> named, say, "cacheName" this is the configuration
* section named "cacheName" as defined in the <code>EmbeddedCacheManager</code>'s
* configuration file. Note that this mode is only useful if such a named configuration section does indeed exist.
* Otherwise, it is equivalent to using
* <code>DEFAULT</code>.</li>
* </ul>
* </p>
* <p>
* In addition to creating a named <code>Cache</code> this <code>FactoryBean</code> does also
* control that <code>Cache</code>' {@link org.infinispan.commons.api.Lifecycle lifecycle} by shutting
* it down when the enclosing Spring application context is closed. It is therefore advisable to
* <em>always</em> use this <code>FactoryBean</code> when creating a named <code>Cache</code>.
* </p>
*
* @author <a href="mailto:olaf DOT bergner AT gmx DOT de">Olaf Bergner</a>
* @author Marius Bogoevici
*
*/
public class InfinispanNamedEmbeddedCacheFactoryBean<K, V> implements FactoryBean<Cache<K, V>>,
BeanNameAware, InitializingBean, DisposableBean {
/**
* <p>
* Defines how to configure a new named cache produced by this <code>FactoryBean</code>:
* <ul>
* <li>
* <code>NONE</code>: Configuration starts with a new <code>Configuration</code> instance.
* Note that this mode may only be used if no named configuration having the same name as the <code>Cache</code>
* to be created already exists. It is therefore illegal to use this mode to create a <code>Cache</code> named, say,
* "cacheName" if the configuration file used to configure the
* <code>EmbeddedCacheManager</code> contains a configuration section named
* "cacheName".</li>
* <li>
* <code>DEFAULT</code>: Configuration starts with the <code>EmbeddedCacheManager</code>'s
* <em>default</em> <code>Configuration</code> instance, i.e. the configuration settings defined
* in its configuration file's default section. Note that this mode may only be used if no named configuration
* having the same name as the <code>Cache</code> to be created already exists. It is therefore illegal to use
* this mode to create a <code>Cache</code> named, say, "cacheName" if the configuration file used
* to configure the <code>EmbeddedCacheManager</code> contains a configuration section named
* "cacheName".</li>
* <li>
* <code>CUSTOM</code>: This is where a user will provide a custom-built <code>ConfigurationBuilder</code>
* object which will be used to configure a <code>Cache</code> instance. If a {@link #setCacheName(String)} has
* already been called, then that name will be used.
* </li>
* <li>
* <code>NAMED</code>: Configuration starts with the <code>EmbeddedCacheManager</code>'s
* <code>Configuration</code> instance having the same name as the <code>Cache</code> to be
* created. For a <code>Cache</code> named, say, "cacheName" this is the configuration
* section named "cacheName" as defined in the <code>EmbeddedCacheManager</code>'s
* configuration file. Note that this mode is only useful if such a named configuration section does indeed exist.
* Otherwise, it is equivalent to using
* <code>DEFAULT</code>.</li>
* </ul>
* </p>
*
* @author <a href="mailto:olaf DOT bergner AT gmx DOT de">Olaf Bergner</a>
*
*/
enum ConfigurationTemplateMode {
NONE,
DEFAULT,
CUSTOM,
NAMED
}
private final Log logger = LogFactory.getLog(getClass());
private EmbeddedCacheManager infinispanEmbeddedCacheManager;
private ConfigurationTemplateMode configurationTemplateMode = ConfigurationTemplateMode.NAMED;
private String cacheName;
private String beanName;
private Cache<K, V> infinispanCache;
private ConfigurationBuilder builder = null;
// ------------------------------------------------------------------------
// org.springframework.beans.factory.InitializingBean
// ------------------------------------------------------------------------
/**
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
if (this.infinispanEmbeddedCacheManager == null) {
throw new IllegalStateException("No Infinispan EmbeddedCacheManager has been set");
}
this.logger.info("Initializing named Infinispan embedded cache ...");
final String effectiveCacheName = obtainEffectiveCacheName();
this.infinispanCache = configureAndCreateNamedCache(effectiveCacheName);
this.logger.info("New Infinispan embedded cache [" + this.infinispanCache + "] initialized");
}
private Cache<K, V> configureAndCreateNamedCache(final String cacheName) {
switch (this.configurationTemplateMode) {
case NONE:
this.logger.debug("ConfigurationTemplateMode is NONE: using a fresh Configuration");
if (this.infinispanEmbeddedCacheManager.getCacheNames().contains(cacheName)) {
throw new IllegalStateException("Cannot use ConfigurationTemplateMode NONE: a cache named [" + cacheName + "] has already been defined.");
}
builder = new ConfigurationBuilder();
this.infinispanEmbeddedCacheManager.defineConfiguration(cacheName, builder.build());
break;
case NAMED:
this.logger.debug("ConfigurationTemplateMode is NAMED: using a named Configuration [" + cacheName + "]");
break;
case DEFAULT:
this.logger.debug("ConfigurationTemplateMode is DEFAULT.");
if (this.infinispanEmbeddedCacheManager.getCacheNames().contains(cacheName)) {
throw new IllegalStateException("Cannot use ConfigurationTemplateMode DEFAULT: a cache named [" + cacheName + "] has already been defined.");
}
break;
case CUSTOM:
this.logger.debug("ConfigurationTemplateMode is CUSTOM. Using the provided ConfigurationBuilder.");
if (this.builder == null) {
throw new IllegalStateException("There has not been a ConfigurationBuilder provided. There has to be one " +
"provided for ConfigurationTemplateMode CUSTOM.");
}
this.infinispanEmbeddedCacheManager.defineConfiguration(cacheName, builder.build());
break;
default:
throw new IllegalStateException("Unknown ConfigurationTemplateMode: " + this.configurationTemplateMode);
}
return this.infinispanEmbeddedCacheManager.getCache(cacheName);
}
private String obtainEffectiveCacheName() {
if (StringUtils.hasText(this.cacheName)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using custom cache name [" + this.cacheName + "]");
}
return this.cacheName;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using bean name [" + this.beanName + "] as cache name");
}
return this.beanName;
}
}
// ------------------------------------------------------------------------
// org.springframework.beans.factory.FactoryBean
// ------------------------------------------------------------------------
/**
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public Cache<K, V> getObject() throws Exception {
return this.infinispanCache;
}
/**
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<? extends Cache> getObjectType() {
return this.infinispanCache != null ? this.infinispanCache.getClass() : Cache.class;
}
/**
* Always returns <code>true</code>.
*
* @return Always <code>true</code>
*
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
// ------------------------------------------------------------------------
// org.springframework.beans.factory.BeanNameAware
// ------------------------------------------------------------------------
/**
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
@Override
public void setBeanName(final String name) {
this.beanName = name;
}
// ------------------------------------------------------------------------
// org.springframework.beans.factory.DisposableBean
// ------------------------------------------------------------------------
/**
* Shuts down the <code>org.infinispan.Cache</code> created by this <code>FactoryBean</code>.
*
* @see org.springframework.beans.factory.DisposableBean#destroy()
* @see org.infinispan.Cache#stop()
*/
@Override
public void destroy() throws Exception {
// Probably being paranoid here ...
if (this.infinispanCache != null) {
this.infinispanCache.stop();
}
}
// ------------------------------------------------------------------------
// Properties
// ------------------------------------------------------------------------
/**
* <p>
* Sets the {@link org.infinispan.Cache#getName() name} of the {@link org.infinispan.Cache
* <code>org.infinispan.Cache</code>} to be created. If no explicit <code>cacheName</code> is
* set, this <code>FactoryBean</code> will use its {@link #setBeanName(String)
* <code>beanName</code>} as the <code>cacheName</code>.
* </p>
*
* @param cacheName
* The {@link org.infinispan.Cache#getName() name} of the {@link org.infinispan.Cache
* <code>org.infinispan.Cache</code>} to be created
*/
public void setCacheName(final String cacheName) {
this.cacheName = cacheName;
}
/**
* <p>
* Sets the {@link org.infinispan.manager.EmbeddedCacheManager
* <code>org.infinispan.manager.EmbeddedCacheManager</code>} to be used for creating our
* {@link org.infinispan.Cache <code>Cache</code>} instance. Note that this is a
* <strong>mandatory</strong> property.
* </p>
*
* @param infinispanEmbeddedCacheManager
* The {@link org.infinispan.manager.EmbeddedCacheManager
* <code>org.infinispan.manager.EmbeddedCacheManager</code>} to be used for creating
* our {@link org.infinispan.Cache <code>Cache</code>} instance
*/
public void setInfinispanEmbeddedCacheManager(
final EmbeddedCacheManager infinispanEmbeddedCacheManager) {
this.infinispanEmbeddedCacheManager = infinispanEmbeddedCacheManager;
}
/**
* @param configurationTemplateMode
* @throws IllegalArgumentException
*/
public void setConfigurationTemplateMode(final String configurationTemplateMode)
throws IllegalArgumentException {
this.configurationTemplateMode = ConfigurationTemplateMode.valueOf(configurationTemplateMode);
}
/**
* API to introduce a customised {@link ConfigurationBuilder} that will override the default configurations
* which are already available on this class. This can <strong>only</strong> be used if {@link
* #setConfigurationTemplateMode(String)} has been set to <code>CUSTOM</code>.
*
* @param builder
*/
public void addCustomConfiguration(final ConfigurationBuilder builder) {
this.builder = builder;
}
}