"acceptExisting" flag allows for sharing per cacheManagerName on EhCache 2.5+

As a benefit over the "shared" flag, this allows for sharing a specific CacheManager in a system with potentially multiple CacheManagers involved, and it supports controlled shutdown by the EhCacheManagerFactoryBean that actually created the CacheManager.

Issue: SPR-11178
This commit is contained in:
Juergen Hoeller
2013-12-11 17:25:02 +01:00
parent 9c157ea073
commit a9605a11e9
3 changed files with 145 additions and 23 deletions

View File

@@ -52,9 +52,10 @@ import org.springframework.beans.factory.InitializingBean;
* Cache instance will be retrieved from the CacheManager.
*
* <p>Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher.
* We recommend the use of EhCache 2.5 or higher.
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @author Dmitriy Kopylenko
* @since 1.1.1
* @see #setCacheManager
* @see EhCacheManagerFactoryBean
@@ -87,12 +88,14 @@ public class EhCacheFactoryBean extends CacheConfiguration implements FactoryBea
@SuppressWarnings("deprecation")
public EhCacheFactoryBean() {
// Using deprecated setMaxElementsInMemory method for EhCache 2.1-2.4 compatibility
setMaxElementsInMemory(10000);
setMaxElementsOnDisk(10000000);
setTimeToLiveSeconds(120);
setTimeToIdleSeconds(120);
}
/**
* Set a CacheManager from which to retrieve a named Cache instance.
* By default, {@code CacheManager.getInstance()} will be called.

View File

@@ -45,9 +45,10 @@ import org.springframework.core.io.Resource;
* also necessary for loading EhCache configuration from a non-default config location.
*
* <p>Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher.
* We recommend the use of EhCache 2.5 or higher.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @author Dmitriy Kopylenko
* @since 1.1.1
* @see #setConfigLocation
* @see #setShared
@@ -60,12 +61,16 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
private Resource configLocation;
private boolean shared = false;
private String cacheManagerName;
private boolean acceptExisting = false;
private boolean shared = false;
private CacheManager cacheManager;
private boolean locallyManaged = true;
/**
* Set the location of the EhCache config file. A typical value is "/WEB-INF/ehcache.xml".
@@ -78,17 +83,6 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
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)
@@ -97,6 +91,45 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
this.cacheManagerName = cacheManagerName;
}
/**
* Set whether an existing EhCache CacheManager of the same name will be accepted
* for this EhCacheManagerFactoryBean setup. Default is "false".
* <p>Typically used in combination with {@link #setCacheManagerName "cacheManagerName"}
* but will simply work with the default CacheManager name if none specified.
* All references to the same CacheManager name (or the same default) in the
* same ClassLoader space will share the specified CacheManager then.
* <p><b>NOTE:</b> This feature requires EhCache 2.5 or higher. In contrast to
* the {@link #setShared "shared"} flag, it supports controlled shutdown of the
* CacheManager by the EhCacheManagerFactoryBean that actually created it.
* @see #setCacheManagerName
* #see #setShared
* @see net.sf.ehcache.CacheManager#getCacheManager(String)
* @see net.sf.ehcache.CacheManager#CacheManager()
*/
public void setAcceptExisting(boolean acceptExisting) {
this.acceptExisting = acceptExisting;
}
/**
* Set whether the EhCache CacheManager should be shared (as a singleton at the
* ClassLoader level) or independent (typically local within the application).
* Default is "false", creating an independent local instance.
* <p><b>NOTE:</b> This feature allows for sharing this EhCacheManagerFactoryBean's
* CacheManager with any code calling <code>CacheManager.create()</code> in the same
* ClassLoader space, with no need to agree on a specific CacheManager name.
* However, it only supports a single EhCacheManagerFactoryBean involved which will
* control the lifecycle of the underlying CacheManager (in particular, its shutdown).
* <p>This flag overrides {@link #setAcceptExisting "acceptExisting"} if both are set,
* since it indicates the 'stronger' mode of sharing.
* @see #setCacheManagerName
* @see #setAcceptExisting
* @see net.sf.ehcache.CacheManager#create()
* @see net.sf.ehcache.CacheManager#CacheManager()
*/
public void setShared(boolean shared) {
this.shared = shared;
}
@Override
public void afterPropertiesSet() throws IOException, CacheException {
@@ -108,7 +141,30 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
if (this.cacheManagerName != null) {
configuration.setName(this.cacheManagerName);
}
this.cacheManager = (this.shared ? CacheManager.create(configuration) : new CacheManager(configuration));
if (this.shared) {
// Old-school EhCache singleton sharing...
// No way to find out whether we actually created a new CacheManager
// or just received an existing singleton reference.
this.cacheManager = CacheManager.create(configuration);
}
else if (this.acceptExisting) {
// EhCache 2.5+: Reusing an existing CacheManager of the same name.
// Basically the same code as in CacheManager.getInstance(String),
// just storing whether we're dealing with an existing instance.
synchronized (CacheManager.class) {
this.cacheManager = CacheManager.getCacheManager(this.cacheManagerName);
if (this.cacheManager == null) {
this.cacheManager = new CacheManager(configuration);
}
else {
this.locallyManaged = false;
}
}
}
else {
// Throwing an exception if a CacheManager of the same name exists already...
this.cacheManager = new CacheManager(configuration);
}
}
finally {
if (is != null) {
@@ -136,8 +192,10 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
@Override
public void destroy() {
logger.info("Shutting down EhCache CacheManager");
this.cacheManager.shutdown();
if (this.locallyManaged) {
logger.info("Shutting down EhCache CacheManager");
this.cacheManager.shutdown();
}
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.cache.ehcache;
import junit.framework.TestCase;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.config.CacheConfiguration;
@@ -26,17 +26,21 @@ 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 org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import static org.junit.Assert.*;
/**
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @author Dmitriy Kopylenko
* @since 27.09.2004
*/
public class EhCacheSupportTests extends TestCase {
public class EhCacheSupportTests {
public void testLoadingBlankCacheManager() throws Exception {
@Test
public void testBlankCacheManager() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.setCacheManagerName("myCacheManager");
assertEquals(CacheManager.class, cacheManagerFb.getObjectType());
@@ -53,7 +57,59 @@ public class EhCacheSupportTests extends TestCase {
}
}
public void testLoadingCacheManagerFromConfigFile() throws Exception {
@Test
public void testCacheManagerConflict() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.setCacheManagerName("myCacheManager");
assertEquals(CacheManager.class, cacheManagerFb.getObjectType());
assertTrue("Singleton property", cacheManagerFb.isSingleton());
cacheManagerFb.afterPropertiesSet();
try {
CacheManager cm = cacheManagerFb.getObject();
assertTrue("Loaded CacheManager with no caches", cm.getCacheNames().length == 0);
Cache myCache1 = cm.getCache("myCache1");
assertTrue("No myCache1 defined", myCache1 == null);
EhCacheManagerFactoryBean cacheManagerFb2 = new EhCacheManagerFactoryBean();
cacheManagerFb2.setCacheManagerName("myCacheManager");
cacheManagerFb2.afterPropertiesSet();
fail("Should have thrown CacheException because of naming conflict");
}
catch (CacheException ex) {
// expected
}
finally {
cacheManagerFb.destroy();
}
}
@Test
public void testAcceptExistingCacheManager() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.setCacheManagerName("myCacheManager");
assertEquals(CacheManager.class, cacheManagerFb.getObjectType());
assertTrue("Singleton property", cacheManagerFb.isSingleton());
cacheManagerFb.afterPropertiesSet();
try {
CacheManager cm = cacheManagerFb.getObject();
assertTrue("Loaded CacheManager with no caches", cm.getCacheNames().length == 0);
Cache myCache1 = cm.getCache("myCache1");
assertTrue("No myCache1 defined", myCache1 == null);
EhCacheManagerFactoryBean cacheManagerFb2 = new EhCacheManagerFactoryBean();
cacheManagerFb2.setCacheManagerName("myCacheManager");
cacheManagerFb2.setAcceptExisting(true);
cacheManagerFb2.afterPropertiesSet();
CacheManager cm2 = cacheManagerFb2.getObject();
assertSame(cm, cm2);
cacheManagerFb2.destroy();
}
finally {
cacheManagerFb.destroy();
}
}
public void testCacheManagerFromConfigFile() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.setConfigLocation(new ClassPathResource("testEhcache.xml", getClass()));
cacheManagerFb.setCacheManagerName("myCacheManager");
@@ -70,10 +126,12 @@ public class EhCacheSupportTests extends TestCase {
}
}
@Test
public void testEhCacheFactoryBeanWithDefaultCacheManager() throws Exception {
doTestEhCacheFactoryBean(false);
}
@Test
public void testEhCacheFactoryBeanWithExplicitCacheManager() throws Exception {
doTestEhCacheFactoryBean(true);
}
@@ -156,6 +214,7 @@ public class EhCacheSupportTests extends TestCase {
}
}
@Test
public void testEhCacheFactoryBeanWithBlockingCache() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.afterPropertiesSet();
@@ -175,6 +234,7 @@ public class EhCacheSupportTests extends TestCase {
}
}
@Test
public void testEhCacheFactoryBeanWithSelfPopulatingCache() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.afterPropertiesSet();
@@ -200,6 +260,7 @@ public class EhCacheSupportTests extends TestCase {
}
}
@Test
public void testEhCacheFactoryBeanWithUpdatingSelfPopulatingCache() throws Exception {
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean();
cacheManagerFb.afterPropertiesSet();