From 08fc4f9015fff74077d430b0c13e8a465261e0b0 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 30 Jun 2011 18:17:39 +0000 Subject: [PATCH] SPR-8477 + add no-op cache implementations suitable for cache declarations w/o a backing store git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4641 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../cache/support/CompositeCacheManager.java | 31 +++++- .../cache/support/NoOpCacheManager.java | 99 +++++++++++++++++++ .../cache/NoOpCacheManagerTest.java | 61 ++++++++++++ spring-framework-reference/src/cache.xml | 22 +++++ 4 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java create mode 100644 org.springframework.context/src/test/java/org/springframework/cache/NoOpCacheManagerTest.java diff --git a/org.springframework.context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java b/org.springframework.context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java index 00dee1fa90f..c0fa4a1e111 100644 --- a/org.springframework.context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java +++ b/org.springframework.context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import org.springframework.beans.factory.InitializingBean; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.util.Assert; @@ -29,11 +30,21 @@ import org.springframework.util.Assert; * Composite {@link CacheManager} implementation that iterates * over a given collection of {@link CacheManager} instances. * + * Allows {@link NoOpCacheManager} to be automatically added to the list for handling + * the cache declarations without a backing store. + * * @author Costin Leau */ -public class CompositeCacheManager implements CacheManager { +public class CompositeCacheManager implements InitializingBean, CacheManager { - private CacheManager[] cacheManagers; + private List cacheManagers; + private boolean noOpManager = false; + + public void afterPropertiesSet() { + if (noOpManager) { + cacheManagers.add(new NoOpCacheManager()); + } + } public Cache getCache(String name) { Cache cache = null; @@ -55,8 +66,20 @@ public class CompositeCacheManager implements CacheManager { return Collections.unmodifiableCollection(names); } - public void setCacheManagers(CacheManager[] cacheManagers) { + public void setCacheManagers(Collection cacheManagers) { Assert.notEmpty(cacheManagers, "non-null/empty array required"); - this.cacheManagers = cacheManagers.clone(); + this.cacheManagers = new ArrayList(); + this.cacheManagers.addAll(cacheManagers); + } + + /** + * Indicates whether a {@link NoOpCacheManager} will be added at the end of the manager lists. + * Any cache requests not handled by the configured cache managers will be automatically handled + * by the {@link NoOpCacheManager}. + * + * @param add whether a {@link NoOpCacheManager} instance will be added or not + */ + public void setAddNoOpCache(boolean add) { + this.noOpManager = add; } } \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java b/org.springframework.context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java new file mode 100644 index 00000000000..aa3a3230bc6 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java @@ -0,0 +1,99 @@ +/* + * Copyright 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.support; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +/** + * A basic, no operation {@link CacheManager} implementation suitable for disabling caching, + * typically used for backing cache declarations without an actual backing store. + * + * Will simply accept any items into the cache not actually storing them. + * + * @see CompositeCacheManager + * @author Costin Leau + */ +public class NoOpCacheManager implements CacheManager { + + private final ConcurrentMap caches = new ConcurrentHashMap(); + private Set names = new LinkedHashSet(); + + private static class NoOpCache implements Cache { + + private final String name; + + public NoOpCache(String name) { + this.name = name; + } + + public void clear() { + } + + public void evict(Object key) { + } + + public ValueWrapper get(Object key) { + return null; + } + + public String getName() { + return name; + } + + public Object getNativeCache() { + return null; + } + + public void put(Object key, Object value) { + } + } + + /** + * {@inheritDoc} + * + * This implementation always returns a {@link Cache} implementation that will not + * store items. Additionally, the request cache will be remembered by the manager for consistency. + */ + public Cache getCache(String name) { + Cache cache = caches.get(name); + if (cache == null) { + caches.putIfAbsent(name, new NoOpCache(name)); + synchronized (names) { + names.add(name); + } + } + + return caches.get(name); + } + + /** + * {@inheritDoc} + * + * This implementation returns the name of the caches previously requested. + */ + public Collection getCacheNames() { + return Collections.unmodifiableSet(names); + } +} \ No newline at end of file diff --git a/org.springframework.context/src/test/java/org/springframework/cache/NoOpCacheManagerTest.java b/org.springframework.context/src/test/java/org/springframework/cache/NoOpCacheManagerTest.java new file mode 100644 index 00000000000..ae1572e7c1f --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/cache/NoOpCacheManagerTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 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; + +import static org.junit.Assert.*; + +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cache.support.NoOpCacheManager; + +public class NoOpCacheManagerTest { + + private CacheManager manager; + + @Before + public void setup() { + manager = new NoOpCacheManager(); + } + + @Test + public void testGetCache() throws Exception { + Cache cache = manager.getCache("bucket"); + assertNotNull(cache); + assertSame(cache, manager.getCache("bucket")); + } + + @Test + public void testNoOpCache() throws Exception { + String name = UUID.randomUUID().toString(); + Cache cache = manager.getCache(name); + assertEquals(name, cache.getName()); + Object key = new Object(); + cache.put(key, new Object()); + assertNull(cache.get(key)); + assertNull(cache.getNativeCache()); + } + + @Test + public void testCacheName() throws Exception { + String name = "bucket"; + assertFalse(manager.getCacheNames().contains(name)); + manager.getCache(name); + assertTrue(manager.getCacheNames().contains(name)); + } +} diff --git a/spring-framework-reference/src/cache.xml b/spring-framework-reference/src/cache.xml index df174693d46..26523d6fa79 100644 --- a/spring-framework-reference/src/cache.xml +++ b/spring-framework-reference/src/cache.xml @@ -383,6 +383,7 @@ public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)]]>< +
Configuring the cache storage @@ -429,6 +430,27 @@ public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)]]>< implementation. Note the entire ehcache-specific configuration is read from the resource ehcache.xml.
+
+ Dealing with caches without a backing store + + Sometimes when switching environments or doing testing, one might have cache declarations without an actual backing cache configured. As this is an invalid configuration, at runtime an + exception will be through since the caching infrastructure is unable to find a suitable store. In situations like this, rather then removing the cache declarations (which can prove tedious), + one can wire in a simple, dummy cache that performs no caching - that is, forces the cached methods to be executed every time: + + + + + + + +]]> + + The CompositeCacheManager above chains multiple CacheManagers and aditionally, through the addNoOpManager flag, adds a + no op cache that for all the definitions not handled by the configured cache managers. That is, every cache definition not found in either jdkCache + or gemfireCache (configured above) will be handled by the no op cache, which will not store any information causing the target method to be executed every time. + +
+