diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java index 212f5d6e0d..ec7dabdddc 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java @@ -31,6 +31,7 @@ import org.springframework.util.Assert; * * @author Costin Leau * @author Juergen Hoeller + * @author Stephane Nicoll * @since 3.1 */ public class EhCacheCacheManager extends AbstractTransactionSupportingCacheManager { @@ -86,17 +87,14 @@ public class EhCacheCacheManager extends AbstractTransactionSupportingCacheManag } @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 = getCacheManager().getEhcache(name); - if (ehcache != null) { - addCache(new EhCacheCache(ehcache)); - cache = super.getCache(name); // potentially decorated - } + protected Cache getMissingCache(String name) { + // check the EhCache cache again + // (in case the cache was added at runtime) + Ehcache ehcache = getCacheManager().getEhcache(name); + if (ehcache != null) { + return new EhCacheCache(ehcache); } - return cache; + return null; } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java index 453f87828b..9c324efcdd 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java @@ -31,6 +31,7 @@ import org.springframework.cache.transaction.AbstractTransactionSupportingCacheM *

Note: This class has been updated for JCache 1.0, as of Spring 4.0. * * @author Juergen Hoeller + * @author Stephane Nicoll * @since 3.2 */ public class JCacheCacheManager extends AbstractTransactionSupportingCacheManager { @@ -108,17 +109,13 @@ public class JCacheCacheManager extends AbstractTransactionSupportingCacheManage } @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 = getCacheManager().getCache(name); - if (jcache != null) { - addCache(new JCacheCache(jcache, isAllowNullValues())); - cache = super.getCache(name); // potentially decorated - } + protected Cache getMissingCache(String name) { + // Check the JCache cache again (in case the cache was added at runtime) + javax.cache.Cache jcache = getCacheManager().getCache(name); + if (jcache != null) { + return new JCacheCache(jcache, isAllowNullValues()); } - return cache; + return null; } } diff --git a/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheManagerTests.java b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheManagerTests.java new file mode 100644 index 0000000000..eb75558d45 --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheManagerTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2014 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.CacheManager; +import net.sf.ehcache.config.CacheConfiguration; +import net.sf.ehcache.config.Configuration; +import org.junit.After; +import org.junit.Before; +import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManagerTests; + +/** + * @author Stephane Nicoll + */ +public class EhCacheCacheManagerTests extends AbstractTransactionSupportingCacheManagerTests { + + private CacheManager nativeCacheManager; + private EhCacheCacheManager cacheManager; + private EhCacheCacheManager transactionalCacheManager; + + @Before + public void setup() { + nativeCacheManager = new CacheManager(new Configuration().name("EhCacheCacheManagerTests") + .defaultCache(new CacheConfiguration("default", 100))); + addNativeCache(CACHE_NAME); + + cacheManager = new EhCacheCacheManager(nativeCacheManager); + cacheManager.setTransactionAware(false); + cacheManager.afterPropertiesSet(); + + transactionalCacheManager = new EhCacheCacheManager(nativeCacheManager); + transactionalCacheManager.setTransactionAware(true); + transactionalCacheManager.afterPropertiesSet(); + } + + @After + public void tearDown() { + nativeCacheManager.shutdown(); + } + + + @Override + protected EhCacheCacheManager getCacheManager(boolean transactionAware) { + if (transactionAware) { + return transactionalCacheManager; + } else { + return cacheManager; + } + } + + @Override + protected Class getCacheType() { + return EhCacheCache.class; + } + + @Override + protected void addNativeCache(String cacheName) { + nativeCacheManager.addCache(cacheName); + } + + @Override + protected void removeNativeCache(String cacheName) { + nativeCacheManager.removeCache(cacheName); + } +} diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheCacheManagerTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheCacheManagerTests.java new file mode 100644 index 0000000000..c60798b88c --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheCacheManagerTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2002-2014 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 static org.mockito.BDDMockito.*; +import static org.mockito.Mockito.mock; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManagerTests; + +/** + * @author Stephane Nicoll + */ +public class JCacheCacheManagerTests extends AbstractTransactionSupportingCacheManagerTests { + + private CacheManagerMock cacheManagerMock; + private JCacheCacheManager cacheManager; + private JCacheCacheManager transactionalCacheManager; + + @Before + public void setupOnce() { + cacheManagerMock = new CacheManagerMock(); + cacheManagerMock.addCache(CACHE_NAME); + + cacheManager = new JCacheCacheManager(cacheManagerMock.getCacheManager()); + cacheManager.setTransactionAware(false); + cacheManager.afterPropertiesSet(); + + transactionalCacheManager = new JCacheCacheManager(cacheManagerMock.getCacheManager()); + transactionalCacheManager.setTransactionAware(true); + transactionalCacheManager.afterPropertiesSet(); + } + + + @Override + protected JCacheCacheManager getCacheManager(boolean transactionAware) { + if (transactionAware) { + return transactionalCacheManager; + } else { + return cacheManager; + } + } + + @Override + protected Class getCacheType() { + return JCacheCache.class; + } + + @Override + protected void addNativeCache(String cacheName) { + cacheManagerMock.addCache(cacheName); + } + + @Override + protected void removeNativeCache(String cacheName) { + cacheManagerMock.removeCache(cacheName); + } + + private static class CacheManagerMock { + + private final List cacheNames; + private final CacheManager cacheManager; + + private CacheManagerMock() { + this.cacheNames = new ArrayList(); + this.cacheManager = mock(CacheManager.class); + given(cacheManager.getCacheNames()).willReturn(cacheNames); + } + + private CacheManager getCacheManager() { + return cacheManager; + } + + @SuppressWarnings("unchecked") + public void addCache(String name) { + cacheNames.add(name); + Cache cache = mock(Cache.class); + given(cache.getName()).willReturn(name); + given(cacheManager.getCache(name)).willReturn(cache); + } + + public void removeCache(String name) { + cacheNames.remove(name); + given(cacheManager.getCache(name)).willReturn(null); + } + } +} diff --git a/spring-context-support/src/test/java/org/springframework/cache/transaction/AbstractTransactionSupportingCacheManagerTests.java b/spring-context-support/src/test/java/org/springframework/cache/transaction/AbstractTransactionSupportingCacheManagerTests.java new file mode 100644 index 0000000000..39fb0db58d --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/cache/transaction/AbstractTransactionSupportingCacheManagerTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2014 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 static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +/** + * Shared tests for {@link CacheManager} that inherit from + * {@link AbstractTransactionSupportingCacheManager}. + * + * @author Stephane Nicoll + */ +public abstract class AbstractTransactionSupportingCacheManagerTests { + + public static final String CACHE_NAME = "testCacheManager"; + + @Rule + public final TestName name = new TestName(); + + + /** + * Returns the {@link CacheManager} to use. + * + * @param transactionAware if the requested cache manager should be aware + * of the transaction + * @return the cache manager to use + * @see org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager#setTransactionAware(boolean) + */ + protected abstract T getCacheManager(boolean transactionAware); + + /** + * Returns the expected concrete type of the cache. + */ + protected abstract Class getCacheType(); + + /** + * Adds a cache with the specified name to the native manager. + */ + protected abstract void addNativeCache(String cacheName); + + /** + * Removes the cache with the specified name from the native manager. + */ + protected abstract void removeNativeCache(String cacheName); + + @Test + public void getOnExistingCache() { + assertThat(getCacheManager(false).getCache(CACHE_NAME), is(instanceOf(getCacheType()))); + } + + @Test + public void getOnNewCache() { + T cacheManager = getCacheManager(false); + String cacheName = name.getMethodName(); + addNativeCache(cacheName); + assertFalse(cacheManager.getCacheNames().contains(cacheName)); + try { + assertThat(cacheManager.getCache(cacheName), + is(instanceOf(getCacheType()))); + assertTrue(cacheManager.getCacheNames().contains(cacheName)); + } finally { + removeNativeCache(cacheName); + } + } + + @Test + public void getOnUnknownCache() { + T cacheManager = getCacheManager(false); + String cacheName = name.getMethodName(); + assertFalse(cacheManager.getCacheNames().contains(cacheName)); + assertThat(cacheManager.getCache(cacheName), nullValue()); + } + + @Test + public void getTransactionalOnExistingCache() { + assertThat(getCacheManager(true).getCache(CACHE_NAME), + is(instanceOf(TransactionAwareCacheDecorator.class))); + } + + @Test + public void getTransactionalOnNewCache() { + String cacheName = name.getMethodName(); + T cacheManager = getCacheManager(true); + assertFalse(cacheManager.getCacheNames().contains(cacheName)); + addNativeCache(cacheName); + try { + assertThat(cacheManager.getCache(cacheName), + is(instanceOf(TransactionAwareCacheDecorator.class))); + assertTrue(cacheManager.getCacheNames().contains(cacheName)); + } finally { + removeNativeCache(cacheName); + } + } +} diff --git a/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java b/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java index 364472409b..677f7fe3ac 100644 --- a/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java +++ b/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -33,6 +33,7 @@ import org.springframework.cache.CacheManager; * * @author Costin Leau * @author Juergen Hoeller + * @author Stephane Nicoll * @since 3.1 */ public abstract class AbstractCacheManager implements CacheManager, InitializingBean { @@ -70,10 +71,36 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing return cache; } + /** + * Return a missing cache with the specified {@code name} or {@code null} if + * such cache does not exist or could not be created on the fly. + *

Some caches may be created at runtime in the native provided. If a lookup + * by name does not yield any result, a subclass gets a chance to register + * such a cache at runtime. The returned cache will be automatically added to + * this instance. + * @param name the name of the cache to retrieve + * @return the missing cache or {@code null} if no such cache exists or could be + * created + * @see #getCache(String) + */ + protected Cache getMissingCache(String name) { + return null; + } @Override public Cache getCache(String name) { - return this.cacheMap.get(name); + Cache cache = lookupCache(name); + if (cache != null) { + return cache; + } + else { + Cache missingCache = getMissingCache(name); + if (missingCache != null) { + addCache(missingCache); + return lookupCache(name); // May be decorated + } + return null; + } } @Override @@ -81,6 +108,9 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing return Collections.unmodifiableSet(this.cacheNames); } + private Cache lookupCache(String name) { + return this.cacheMap.get(name); + } /** * Load the caches for this cache manager. Occurs at startup.