Added "transactionAware" bean property to EhCacheCacheManager and JCacheCacheManager

In the course of this enhancement, the "cache.ehcache" and "cache.jcache" packages moved from spring-context to the spring-context-support module, expecting further transaction-related functionality. Also aligns with the presence of Spring's Quartz support in the spring-context-support module, since Quartz and EHCache are sort of sister projects at Terracotta now.

Issue: SPR-9966
This commit is contained in:
Juergen Hoeller
2012-11-25 21:58:35 +01:00
parent 7d7758bfc9
commit e2f418ab4c
20 changed files with 406 additions and 103 deletions

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.ehcache;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.Assert;
/**
* {@link Cache} implementation on top of an {@link Ehcache} instance.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
*/
public class EhCacheCache implements Cache {
private final Ehcache cache;
/**
* Create an {@link EhCacheCache} instance.
* @param ehcache backing Ehcache instance
*/
public EhCacheCache(Ehcache ehcache) {
Assert.notNull(ehcache, "Ehcache must not be null");
Status status = ehcache.getStatus();
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
"An 'alive' Ehcache is required - current cache is " + status.toString());
this.cache = ehcache;
}
public String getName() {
return this.cache.getName();
}
public Ehcache getNativeCache() {
return this.cache;
}
public ValueWrapper get(Object key) {
Element element = this.cache.get(key);
return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
}
public void put(Object key, Object value) {
this.cache.put(new Element(key, value));
}
public void evict(Object key) {
this.cache.remove(key);
}
public void clear() {
this.cache.removeAll();
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.ehcache;
import java.util.Collection;
import java.util.LinkedHashSet;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Status;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.util.Assert;
/**
* CacheManager backed by an EhCache {@link net.sf.ehcache.CacheManager}.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
*/
public class EhCacheCacheManager extends AbstractTransactionSupportingCacheManager {
private net.sf.ehcache.CacheManager cacheManager;
/**
* Create a new EhCacheCacheManager, setting the target EhCache CacheManager
* through the {@link #setCacheManager} bean property.
*/
public EhCacheCacheManager() {
}
/**
* Create a new EhCacheCacheManager for the given backing EhCache.
* @param cacheManager the backing EhCache {@link net.sf.ehcache.CacheManager}
*/
public EhCacheCacheManager(net.sf.ehcache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Set the backing EhCache {@link net.sf.ehcache.CacheManager}.
*/
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Return the backing EhCache {@link net.sf.ehcache.CacheManager}.
*/
public net.sf.ehcache.CacheManager getCacheManager() {
return this.cacheManager;
}
@Override
protected Collection<Cache> loadCaches() {
Assert.notNull(this.cacheManager, "A backing EhCache CacheManager is required");
Status status = this.cacheManager.getStatus();
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
"An 'alive' EhCache CacheManager is required - current cache is " + status.toString());
String[] names = this.cacheManager.getCacheNames();
Collection<Cache> caches = new LinkedHashSet<Cache>(names.length);
for (String name : names) {
caches.add(new EhCacheCache(this.cacheManager.getEhcache(name)));
}
return caches;
}
@Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
if (cache == null) {
// check the EhCache cache again
// (in case the cache was added at runtime)
Ehcache ehcache = this.cacheManager.getEhcache(name);
if (ehcache != null) {
cache = new EhCacheCache(ehcache);
addCache(cache);
}
}
return cache;
}
}

View File

@@ -0,0 +1,427 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.ehcache;
import java.io.IOException;
import java.util.Set;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.constructs.blocking.BlockingCache;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
import net.sf.ehcache.event.CacheEventListener;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* {@link FactoryBean} that creates a named EHCache {@link net.sf.ehcache.Cache} instance
* (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface),
* representing a cache region within an EHCache {@link net.sf.ehcache.CacheManager}.
*
* <p>If the specified named cache is not configured in the cache configuration descriptor,
* this FactoryBean will construct an instance of a Cache with the provided name and the
* specified cache properties and add it to the CacheManager for later retrieval. If some
* or all properties are not set at configuration time, this FactoryBean will use defaults.
*
* <p>Note: If the named Cache instance is found, the properties will be ignored and the
* Cache instance will be retrieved from the CacheManager.
*
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @since 1.1.1
* @see #setCacheManager
* @see EhCacheManagerFactoryBean
* @see net.sf.ehcache.Cache
*/
public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware, InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private CacheManager cacheManager;
private String cacheName;
private int maxElementsInMemory = 10000;
private int maxElementsOnDisk = 10000000;
private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
private boolean overflowToDisk = true;
private boolean eternal = false;
private int timeToLive = 120;
private int timeToIdle = 120;
private boolean diskPersistent = false;
private int diskExpiryThreadIntervalSeconds = 120;
private int diskSpoolBufferSize = 0;
private boolean clearOnFlush = true;
private boolean blocking = false;
private CacheEntryFactory cacheEntryFactory;
private BootstrapCacheLoader bootstrapCacheLoader;
private Set<CacheEventListener> cacheEventListeners;
private boolean statisticsEnabled = false;
private boolean sampledStatisticsEnabled = false;
private boolean disabled = false;
private String beanName;
private Ehcache cache;
/**
* Set a CacheManager from which to retrieve a named Cache instance.
* By default, <code>CacheManager.getInstance()</code> will be called.
* <p>Note that in particular for persistent caches, it is advisable to
* properly handle the shutdown of the CacheManager: Set up a separate
* EhCacheManagerFactoryBean and pass a reference to this bean property.
* <p>A separate EhCacheManagerFactoryBean is also necessary for loading
* EHCache configuration from a non-default config location.
* @see EhCacheManagerFactoryBean
* @see net.sf.ehcache.CacheManager#getInstance
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Set a name for which to retrieve or create a cache instance.
* Default is the bean name of this EhCacheFactoryBean.
*/
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
/**
* Specify the maximum number of cached objects in memory.
* Default is 10000 elements.
*/
public void setMaxElementsInMemory(int maxElementsInMemory) {
this.maxElementsInMemory = maxElementsInMemory;
}
/**
* Specify the maximum number of cached objects on disk.
* Default is 10000000 elements.
*/
public void setMaxElementsOnDisk(int maxElementsOnDisk) {
this.maxElementsOnDisk = maxElementsOnDisk;
}
/**
* Set the memory style eviction policy for this cache.
* <p>Supported values are "LRU", "LFU" and "FIFO", according to the
* constants defined in EHCache's MemoryStoreEvictionPolicy class.
* Default is "LRU".
*/
public void setMemoryStoreEvictionPolicy(MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
Assert.notNull(memoryStoreEvictionPolicy, "memoryStoreEvictionPolicy must not be null");
this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
}
/**
* Set whether elements can overflow to disk when the in-memory cache
* has reached the maximum size limit. Default is "true".
*/
public void setOverflowToDisk(boolean overflowToDisk) {
this.overflowToDisk = overflowToDisk;
}
/**
* Set whether elements are considered as eternal. If "true", timeouts
* are ignored and the element is never expired. Default is "false".
*/
public void setEternal(boolean eternal) {
this.eternal = eternal;
}
/**
* Set the time in seconds to live for an element before it expires,
* i.e. the maximum time between creation time and when an element expires.
* <p>This is only used if the element is not eternal. Default is 120 seconds.
*/
public void setTimeToLive(int timeToLive) {
this.timeToLive = timeToLive;
}
/**
* Set the time in seconds to idle for an element before it expires, that is,
* the maximum amount of time between accesses before an element expires.
* <p>This is only used if the element is not eternal. Default is 120 seconds.
*/
public void setTimeToIdle(int timeToIdle) {
this.timeToIdle = timeToIdle;
}
/**
* Set whether the disk store persists between restarts of the Virtual Machine.
* Default is "false".
*/
public void setDiskPersistent(boolean diskPersistent) {
this.diskPersistent = diskPersistent;
}
/**
* Set the number of seconds between runs of the disk expiry thread.
* Default is 120 seconds.
*/
public void setDiskExpiryThreadIntervalSeconds(int diskExpiryThreadIntervalSeconds) {
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
}
/**
* Set the amount of memory to allocate the write buffer for puts to the disk store.
* Default is 0.
*/
public void setDiskSpoolBufferSize(int diskSpoolBufferSize) {
this.diskSpoolBufferSize = diskSpoolBufferSize;
}
/**
* Set whether the memory store should be cleared when flush is called on the cache.
* Default is "true".
*/
public void setClearOnFlush(boolean clearOnFlush) {
this.clearOnFlush = clearOnFlush;
}
/**
* Set whether to use a blocking cache that lets read attempts block
* until the requested element is created.
* <p>If you intend to build a self-populating blocking cache,
* consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
* @see net.sf.ehcache.constructs.blocking.BlockingCache
* @see #setCacheEntryFactory
*/
public void setBlocking(boolean blocking) {
this.blocking = blocking;
}
/**
* Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
* to use for a self-populating cache. If such a factory is specified,
* the cache will be decorated with EHCache's
* {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
* <p>The specified factory can be of type
* {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
* which will lead to the use of an
* {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
* <p>Note: Any such self-populating cache is automatically a blocking cache.
* @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
* @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
* @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
*/
public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
this.cacheEntryFactory = cacheEntryFactory;
}
/**
* Set an EHCache {@link net.sf.ehcache.bootstrap.BootstrapCacheLoader}
* for this cache, if any.
*/
public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) {
this.bootstrapCacheLoader = bootstrapCacheLoader;
}
/**
* Specify EHCache {@link net.sf.ehcache.event.CacheEventListener cache event listeners}
* to registered with this cache.
*/
public void setCacheEventListeners(Set<CacheEventListener> cacheEventListeners) {
this.cacheEventListeners = cacheEventListeners;
}
/**
* Set whether to enable EhCache statistics on this cache.
* @see net.sf.ehcache.Cache#setStatisticsEnabled
*/
public void setStatisticsEnabled(boolean statisticsEnabled) {
this.statisticsEnabled = statisticsEnabled;
}
/**
* Set whether to enable EhCache's sampled statistics on this cache.
* @see net.sf.ehcache.Cache#setSampledStatisticsEnabled
*/
public void setSampledStatisticsEnabled(boolean sampledStatisticsEnabled) {
this.sampledStatisticsEnabled = sampledStatisticsEnabled;
}
/**
* Set whether this cache should be marked as disabled.
* @see net.sf.ehcache.Cache#setDisabled
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public void setBeanName(String name) {
this.beanName = name;
}
public void afterPropertiesSet() throws CacheException, IOException {
// If no CacheManager given, fetch the default.
if (this.cacheManager == null) {
if (logger.isDebugEnabled()) {
logger.debug("Using default EHCache CacheManager for cache region '" + this.cacheName + "'");
}
this.cacheManager = CacheManager.getInstance();
}
// If no cache name given, use bean name as cache name.
if (this.cacheName == null) {
this.cacheName = this.beanName;
}
// Fetch cache region: If none with the given name exists,
// create one on the fly.
Ehcache rawCache;
if (this.cacheManager.cacheExists(this.cacheName)) {
if (logger.isDebugEnabled()) {
logger.debug("Using existing EHCache cache region '" + this.cacheName + "'");
}
rawCache = this.cacheManager.getEhcache(this.cacheName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Creating new EHCache cache region '" + this.cacheName + "'");
}
rawCache = createCache();
this.cacheManager.addCache(rawCache);
}
if (this.cacheEventListeners != null) {
for (CacheEventListener listener : this.cacheEventListeners) {
rawCache.getCacheEventNotificationService().registerListener(listener);
}
}
if (this.statisticsEnabled) {
rawCache.setStatisticsEnabled(true);
}
if (this.sampledStatisticsEnabled) {
rawCache.setSampledStatisticsEnabled(true);
}
if (this.disabled) {
rawCache.setDisabled(true);
}
// Decorate cache if necessary.
Ehcache decoratedCache = decorateCache(rawCache);
if (decoratedCache != rawCache) {
this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache);
}
this.cache = decoratedCache;
}
/**
* Create a raw Cache object based on the configuration of this FactoryBean.
*/
protected Cache createCache() {
// Only call EHCache 1.6 constructor if actually necessary (for compatibility with EHCache 1.3+)
return (!this.clearOnFlush) ?
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize,
this.clearOnFlush) :
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize);
}
/**
* Decorate the given Cache, if necessary.
* @param cache the raw Cache object, based on the configuration of this FactoryBean
* @return the (potentially decorated) cache object to be registered with the CacheManager
*/
protected Ehcache decorateCache(Ehcache cache) {
if (this.cacheEntryFactory != null) {
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
return new UpdatingSelfPopulatingCache(cache, (UpdatingCacheEntryFactory) this.cacheEntryFactory);
}
else {
return new SelfPopulatingCache(cache, this.cacheEntryFactory);
}
}
if (this.blocking) {
return new BlockingCache(cache);
}
return cache;
}
public Ehcache getObject() {
return this.cache;
}
/**
* Predict the particular {@code Ehcache} implementation that will be returned from
* {@link #getObject()} based on logic in {@link #createCache()} and
* {@link #decorateCache(Ehcache)} as orchestrated by {@link #afterPropertiesSet()}.
*/
public Class<? extends Ehcache> getObjectType() {
if (this.cache != null) {
return this.cache.getClass();
}
if (this.cacheEntryFactory != null) {
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
return UpdatingSelfPopulatingCache.class;
}
else {
return SelfPopulatingCache.class;
}
}
if (this.blocking) {
return BlockingCache.class;
}
return Cache.class;
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.ehcache;
import java.io.IOException;
import java.io.InputStream;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
/**
* {@link FactoryBean} that exposes an EHCache {@link net.sf.ehcache.CacheManager}
* instance (independent or shared), configured from a specified config location.
*
* <p>If no config location is specified, a CacheManager will be configured from
* "ehcache.xml" in the root of the class path (that is, default EHCache initialization
* - as defined in the EHCache docs - will apply).
*
* <p>Setting up a separate EhCacheManagerFactoryBean is also advisable when using
* EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
* also necessary for loading EHCache configuration from a non-default config location.
*
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @since 1.1.1
* @see #setConfigLocation
* @see #setShared
* @see EhCacheFactoryBean
* @see net.sf.ehcache.CacheManager
*/
public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private Resource configLocation;
private boolean shared = false;
private String cacheManagerName;
private CacheManager cacheManager;
/**
* Set the location of the EHCache config file. A typical value is "/WEB-INF/ehcache.xml".
* <p>Default is "ehcache.xml" in the root of the class path, or if not found,
* "ehcache-failsafe.xml" in the EHCache jar (default EHCache initialization).
* @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
* @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
* Set whether the EHCache CacheManager should be shared (as a singleton at the VM level)
* or independent (typically local within the application). Default is "false", creating
* an independent instance.
* @see net.sf.ehcache.CacheManager#create()
* @see net.sf.ehcache.CacheManager#CacheManager()
*/
public void setShared(boolean shared) {
this.shared = shared;
}
/**
* Set the name of the EHCache CacheManager (if a specific name is desired).
* @see net.sf.ehcache.CacheManager#setName(String)
*/
public void setCacheManagerName(String cacheManagerName) {
this.cacheManagerName = cacheManagerName;
}
public void afterPropertiesSet() throws IOException, CacheException {
logger.info("Initializing EHCache CacheManager");
if (this.configLocation != null) {
InputStream is = this.configLocation.getInputStream();
try {
this.cacheManager = (this.shared ? CacheManager.create(is) : new CacheManager(is));
}
finally {
is.close();
}
}
else {
this.cacheManager = (this.shared ? CacheManager.create() : new CacheManager());
}
if (this.cacheManagerName != null) {
this.cacheManager.setName(this.cacheManagerName);
}
}
public CacheManager getObject() {
return this.cacheManager;
}
public Class<? extends CacheManager> getObjectType() {
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}
public boolean isSingleton() {
return true;
}
public void destroy() {
logger.info("Shutting down EHCache CacheManager");
this.cacheManager.shutdown();
}
}

View File

@@ -0,0 +1,7 @@
/**
* Support classes for the open source cache
* <a href="http://ehcache.sourceforge.net">EhCache</a>,
* allowing to set up an EhCache CacheManager and Caches
* as beans in a Spring context.
*/
package org.springframework.cache.ehcache;

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.jcache;
import java.io.Serializable;
import javax.cache.Status;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.Assert;
/**
* {@link org.springframework.cache.Cache} implementation on top of a
* {@link javax.cache.Cache} instance.
*
* @author Juergen Hoeller
* @since 3.2
*/
public class JCacheCache implements Cache {
private static final Object NULL_HOLDER = new NullHolder();
@SuppressWarnings("rawtypes")
private final javax.cache.Cache cache;
private final boolean allowNullValues;
/**
* Create an {@link org.springframework.cache.jcache.JCacheCache} instance.
* @param jcache backing JCache Cache instance
*/
public JCacheCache(javax.cache.Cache<?,?> jcache) {
this(jcache, true);
}
/**
* Create an {@link org.springframework.cache.jcache.JCacheCache} instance.
* @param jcache backing JCache Cache instance
* @param allowNullValues whether to accept and convert null values for this cache
*/
public JCacheCache(javax.cache.Cache<?,?> jcache, boolean allowNullValues) {
Assert.notNull(jcache, "Cache must not be null");
Status status = jcache.getStatus();
Assert.isTrue(Status.STARTED.equals(status),
"A 'started' cache is required - current cache is " + status.toString());
this.cache = jcache;
this.allowNullValues = allowNullValues;
}
public String getName() {
return this.cache.getName();
}
public javax.cache.Cache<?,?> getNativeCache() {
return this.cache;
}
public boolean isAllowNullValues() {
return this.allowNullValues;
}
@SuppressWarnings("unchecked")
public ValueWrapper get(Object key) {
Object value = this.cache.get(key);
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
}
@SuppressWarnings("unchecked")
public void put(Object key, Object value) {
this.cache.put(key, toStoreValue(value));
}
@SuppressWarnings("unchecked")
public void evict(Object key) {
this.cache.remove(key);
}
public void clear() {
this.cache.removeAll();
}
/**
* Convert the given value from the internal store to a user value
* returned from the get method (adapting <code>null</code>).
* @param storeValue the store value
* @return the value to return to the user
*/
protected Object fromStoreValue(Object storeValue) {
if (this.allowNullValues && storeValue == NULL_HOLDER) {
return null;
}
return storeValue;
}
/**
* Convert the given user value, as passed into the put method,
* to a value in the internal store (adapting <code>null</code>).
* @param userValue the given user value
* @return the value to store
*/
protected Object toStoreValue(Object userValue) {
if (this.allowNullValues && userValue == null) {
return NULL_HOLDER;
}
return userValue;
}
@SuppressWarnings("serial")
private static class NullHolder implements Serializable {
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.jcache;
import java.util.Collection;
import java.util.LinkedHashSet;
import javax.cache.CacheManager;
import javax.cache.Status;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.util.Assert;
/**
* {@link org.springframework.cache.CacheManager} implementation
* backed by a JCache {@link javax.cache.CacheManager}.
*
* @author Juergen Hoeller
* @since 3.2
*/
public class JCacheCacheManager extends AbstractTransactionSupportingCacheManager {
private javax.cache.CacheManager cacheManager;
private boolean allowNullValues = true;
/**
* Create a new JCacheCacheManager, setting the target JCache CacheManager
* through the {@link #setCacheManager} bean property.
*/
public JCacheCacheManager() {
}
/**
* Create a new JCacheCacheManager for the given backing JCache.
* @param cacheManager the backing JCache {@link javax.cache.CacheManager}
*/
public JCacheCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Set the backing JCache {@link javax.cache.CacheManager}.
*/
public void setCacheManager(javax.cache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Return the backing JCache {@link javax.cache.CacheManager}.
*/
public javax.cache.CacheManager getCacheManager() {
return this.cacheManager;
}
/**
* Specify whether to accept and convert null values for all caches
* in this cache manager.
* <p>Default is "true", despite JSR-107 itself not supporting null values.
* An internal holder object will be used to store user-level null values.
*/
public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
}
/**
* Return whether this cache manager accepts and converts null values
* for all of its caches.
*/
public boolean isAllowNullValues() {
return this.allowNullValues;
}
@Override
protected Collection<Cache> loadCaches() {
Assert.notNull(this.cacheManager, "A backing CacheManager is required");
Status status = this.cacheManager.getStatus();
Assert.isTrue(Status.STARTED.equals(status),
"A 'started' JCache CacheManager is required - current cache is " + status.toString());
Collection<Cache> caches = new LinkedHashSet<Cache>();
for (javax.cache.Cache<?,?> jcache : this.cacheManager.getCaches()) {
caches.add(new JCacheCache(jcache, this.allowNullValues));
}
return caches;
}
@Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
if (cache == null) {
// check the JCache cache again
// (in case the cache was added at runtime)
javax.cache.Cache<?,?> jcache = this.cacheManager.getCache(name);
if (jcache != null) {
cache = new JCacheCache(jcache, this.allowNullValues);
addCache(cache);
}
}
return cache;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.jcache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* {@link FactoryBean} for a JCache {@link javax.cache.CacheManager},
* obtaining a pre-defined CacheManager by name through the standard
* JCache {@link javax.cache.Caching} class.
*
* @author Juergen Hoeller
* @since 3.2
* @see javax.cache.Caching#getCacheManager()
* @see javax.cache.Caching#getCacheManager(String)
*/
public class JCacheManagerFactoryBean
implements FactoryBean<CacheManager>, BeanClassLoaderAware, InitializingBean, DisposableBean {
private String cacheManagerName = Caching.DEFAULT_CACHE_MANAGER_NAME;
private ClassLoader beanClassLoader;
private CacheManager cacheManager;
/**
* Specify the name of the desired CacheManager.
* Default is JCache's default.
* @see Caching#DEFAULT_CACHE_MANAGER_NAME
*/
public void setCacheManagerName(String cacheManagerName) {
this.cacheManagerName = cacheManagerName;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public void afterPropertiesSet() {
this.cacheManager = (this.beanClassLoader != null ?
Caching.getCacheManager(this.beanClassLoader, this.cacheManagerName) :
Caching.getCacheManager(this.cacheManagerName));
}
public CacheManager getObject() {
return this.cacheManager;
}
public Class<?> getObjectType() {
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}
public boolean isSingleton() {
return true;
}
public void destroy() {
this.cacheManager.shutdown();
}
}

View File

@@ -0,0 +1,7 @@
/**
* Implementation package for JSR-107 (javax.cache aka "JCache") based caches.
* Provides a {@link org.springframework.cache.CacheManager CacheManager}
* and {@link org.springframework.cache.Cache Cache} implementation for
* use in a Spring context, using a JSR-107 compliant cache provider.
*/
package org.springframework.cache.jcache;

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.transaction;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
/**
* Base class for CacheManager implementations that want to support built-in
* awareness of Spring-managed transactions. This usually needs to be switched
* on explicitly through the {@link #setTransactionAware} bean property.
*
* @author Juergen Hoeller
* @since 3.2
* @see #setTransactionAware
* @see TransactionAwareCacheDecorator
* @see TransactionAwareCacheManagerProxy
*/
public abstract class AbstractTransactionSupportingCacheManager extends AbstractCacheManager {
private boolean transactionAware = false;
/**
* Set whether this CacheManager should expose transaction-aware Cache objects.
* <p>Default is "false". Set this to "true" to synchronize cache put/evict
* operations with ongoing Spring-managed transactions, performing the actual cache
* put/evict operation only in the after-commit phase of a successful transaction.
*/
public void setTransactionAware(boolean transactionAware) {
this.transactionAware = transactionAware;
}
/**
* Return whether this CacheManager has been configured to be transaction-aware.
*/
public boolean isTransactionAware() {
return this.transactionAware;
}
@Override
protected Cache decorateCache(Cache cache) {
return (isTransactionAware() ? new TransactionAwareCacheDecorator(cache) : cache);
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.transaction;
import org.springframework.cache.Cache;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Cache decorator which synchronizes its {@link #put} and {@link #evict} operations with
* Spring-managed transactions (through Spring's {@link TransactionSynchronizationManager},
* performing the actual cache put/evict operation only in the after-commit phase of a
* successful transaction. If no transaction is active, {@link #put} and {@link #evict}
* operations will be performed immediately, as usual.
*
* @author Juergen Hoeller
* @since 3.2
* @see TransactionAwareCacheManagerProxy
*/
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;
/**
* Create a new TransactionAwareCache for the given target Cache.
* @param targetCache the target Cache to decorate
*/
public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
public String getName() {
return this.targetCache.getName();
}
public Object getNativeCache() {
return this.targetCache.getNativeCache();
}
public ValueWrapper get(Object key) {
return this.targetCache.get(key);
}
public void put(final Object key, final Object value) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
targetCache.put(key, value);
}
});
}
else {
this.targetCache.put(key, value);
}
}
public void evict(final Object key) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
targetCache.evict(key);
}
});
}
else {
this.targetCache.evict(key);
}
}
public void clear() {
this.targetCache.clear();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.transaction;
import java.util.Collection;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.Assert;
/**
* Proxy for a target {@link CacheManager}, exposing transaction-aware {@link Cache} objects
* which synchronize their {@link Cache#put} operations with Spring-managed transactions
* (through Spring's {@link org.springframework.transaction.support.TransactionSynchronizationManager},
* performing the actual cache put operation only in the after-commit phase of a successful transaction.
* If no transaction is active, {@link Cache#put} operations will be performed immediately, as usual.
*
* @author Juergen Hoeller
* @since 3.2
* @see #setTargetCacheManager
* @see TransactionAwareCacheDecorator
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class TransactionAwareCacheManagerProxy implements CacheManager, InitializingBean {
private CacheManager targetCacheManager;
/**
* Create a new TransactionAwareCacheManagerProxy, setting the target CacheManager
* through the {@link #setTargetCacheManager} bean property.
*/
public TransactionAwareCacheManagerProxy() {
}
/**
* Create a new TransactionAwareCacheManagerProxy for the given target CacheManager.
* @param targetCacheManager the target CacheManager to proxy
*/
public TransactionAwareCacheManagerProxy(CacheManager targetCacheManager) {
Assert.notNull(targetCacheManager, "Target CacheManager must not be null");
this.targetCacheManager = targetCacheManager;
}
/**
* Set the target CacheManager to proxy.
*/
public void setTargetCacheManager(CacheManager targetCacheManager) {
this.targetCacheManager = targetCacheManager;
}
public void afterPropertiesSet() {
if (this.targetCacheManager == null) {
throw new IllegalStateException("'targetCacheManager' is required");
}
}
public Cache getCache(String name) {
return new TransactionAwareCacheDecorator(this.targetCacheManager.getCache(name));
}
public Collection<String> getCacheNames() {
return this.targetCacheManager.getCacheNames();
}
}

View File

@@ -0,0 +1,5 @@
/**
* Transaction-aware decorators for the the org.springframework.cache package.
* Provides synchronization of put operations with Spring-managed transactions.
*/
package org.springframework.cache.transaction;