Skip to content

Commit 8545a75

Browse files
committed
Add resetCaches() method to Caffeine/ConcurrentMapCacheManager
Closes gh-35840 (cherry picked from commit bc3431f)
1 parent f94645d commit 8545a75

File tree

4 files changed

+108
-34
lines changed

4 files changed

+108
-34
lines changed

spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public class CaffeineCacheManager implements CacheManager {
7777

7878
private boolean allowNullValues = true;
7979

80-
private boolean dynamic = true;
80+
private volatile boolean dynamic = true;
8181

8282
private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
8383

@@ -102,10 +102,15 @@ public CaffeineCacheManager(String... cacheNames) {
102102

103103
/**
104104
* Specify the set of cache names for this CacheManager's 'static' mode.
105-
* <p>The number of caches and their names will be fixed after a call to this method,
106-
* with no creation of further cache regions at runtime.
107-
* <p>Calling this with a {@code null} collection argument resets the
108-
* mode to 'dynamic', allowing for further creation of caches again.
105+
* <p>The number of caches and their names will be fixed after a call
106+
* to this method, with no creation of further cache regions at runtime.
107+
* <p>Note that this method replaces existing caches of the given names
108+
* and prevents the creation of further cache regions from here on - but
109+
* does <i>not</i> remove unrelated existing caches. For a full reset,
110+
* consider calling {@link #resetCaches()} before calling this method.
111+
* <p>Calling this method with a {@code null} collection argument resets
112+
* the mode to 'dynamic', allowing for further creation of caches again.
113+
* @see #resetCaches()
109114
*/
110115
public void setCacheNames(@Nullable Collection<String> cacheNames) {
111116
if (cacheNames != null) {
@@ -245,11 +250,6 @@ public boolean isAllowNullValues() {
245250
}
246251

247252

248-
@Override
249-
public Collection<String> getCacheNames() {
250-
return Collections.unmodifiableSet(this.cacheMap.keySet());
251-
}
252-
253253
@Override
254254
@Nullable
255255
public Cache getCache(String name) {
@@ -260,6 +260,33 @@ public Cache getCache(String name) {
260260
return cache;
261261
}
262262

263+
@Override
264+
public Collection<String> getCacheNames() {
265+
return Collections.unmodifiableSet(this.cacheMap.keySet());
266+
}
267+
268+
/**
269+
* Reset this cache manager's caches, removing them completely for on-demand
270+
* re-creation in 'dynamic' mode, or simply clearing their entries otherwise.
271+
* @since 6.2.14
272+
*/
273+
public void resetCaches() {
274+
this.cacheMap.values().forEach(Cache::clear);
275+
if (this.dynamic) {
276+
this.cacheMap.keySet().retainAll(this.customCacheNames);
277+
}
278+
}
279+
280+
/**
281+
* Remove the specified cache from this cache manager, applying to
282+
* custom caches as well as dynamically registered caches at runtime.
283+
* @param name the name of the cache
284+
* @since 6.1.15
285+
*/
286+
public void removeCache(String name) {
287+
this.customCacheNames.remove(name);
288+
this.cacheMap.remove(name);
289+
}
263290

264291
/**
265292
* Register the given native Caffeine Cache instance with this cache manager,
@@ -303,16 +330,6 @@ public void registerCustomCache(String name, AsyncCache<Object, Object> cache) {
303330
this.cacheMap.put(name, adaptCaffeineCache(name, cache));
304331
}
305332

306-
/**
307-
* Remove the specified cache from this cache manager, applying to
308-
* custom caches as well as dynamically registered caches at runtime.
309-
* @param name the name of the cache
310-
* @since 6.1.15
311-
*/
312-
public void removeCache(String name) {
313-
this.customCacheNames.remove(name);
314-
this.cacheMap.remove(name);
315-
}
316333

317334
/**
318335
* Adapt the given new native Caffeine Cache instance to Spring's {@link Cache}

spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.junit.jupiter.api.Test;
2525

2626
import org.springframework.cache.Cache;
27-
import org.springframework.cache.CacheManager;
2827
import org.springframework.cache.support.SimpleValueWrapper;
2928

3029
import static org.assertj.core.api.Assertions.assertThat;
@@ -42,7 +41,7 @@ class CaffeineCacheManagerTests {
4241
@Test
4342
@SuppressWarnings("cast")
4443
void dynamicMode() {
45-
CacheManager cm = new CaffeineCacheManager();
44+
CaffeineCacheManager cm = new CaffeineCacheManager();
4645

4746
Cache cache1 = cm.getCache("c1");
4847
assertThat(cache1).isInstanceOf(CaffeineCache.class);
@@ -76,6 +75,14 @@ void dynamicMode() {
7675
cache1.evict("key3");
7776
assertThat(cache1.get("key3", () -> (String) null)).isNull();
7877
assertThat(cache1.get("key3", () -> (String) null)).isNull();
78+
79+
cm.removeCache("c1");
80+
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
81+
assertThat(cm.getCache("c2")).isSameAs(cache2);
82+
83+
cm.resetCaches();
84+
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
85+
assertThat(cm.getCache("c2")).isNotSameAs(cache2);
7986
}
8087

8188
@Test
@@ -131,11 +138,24 @@ void staticMode() {
131138

132139
cm.setAllowNullValues(true);
133140
Cache cache1y = cm.getCache("c1");
141+
Cache cache2y = cm.getCache("c2");
134142

135143
cache1y.put("key3", null);
136144
assertThat(cache1y.get("key3").get()).isNull();
137145
cache1y.evict("key3");
138146
assertThat(cache1y.get("key3")).isNull();
147+
cache2y.put("key4", "value4");
148+
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
149+
150+
cm.removeCache("c1");
151+
assertThat(cm.getCache("c1")).isNull();
152+
assertThat(cm.getCache("c2")).isSameAs(cache2y);
153+
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
154+
155+
cm.resetCaches();
156+
assertThat(cm.getCache("c1")).isNull();
157+
assertThat(cm.getCache("c2")).isSameAs(cache2y);
158+
assertThat(cache2y.get("key4")).isNull();
139159
}
140160

141161
@Test

spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCacheManager.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
5454

5555
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
5656

57-
private boolean dynamic = true;
57+
private volatile boolean dynamic = true;
5858

5959
private boolean allowNullValues = true;
6060

@@ -82,10 +82,15 @@ public ConcurrentMapCacheManager(String... cacheNames) {
8282

8383
/**
8484
* Specify the set of cache names for this CacheManager's 'static' mode.
85-
* <p>The number of caches and their names will be fixed after a call to this method,
86-
* with no creation of further cache regions at runtime.
87-
* <p>Calling this with a {@code null} collection argument resets the
88-
* mode to 'dynamic', allowing for further creation of caches again.
85+
* <p>The number of caches and their names will be fixed after a call
86+
* to this method, with no creation of further cache regions at runtime.
87+
* <p>Note that this method replaces existing caches of the given names
88+
* and prevents the creation of further cache regions from here on - but
89+
* does <i>not</i> remove unrelated existing caches. For a full reset,
90+
* consider calling {@link #resetCaches()} before calling this method.
91+
* <p>Calling this method with a {@code null} collection argument resets
92+
* the mode to 'dynamic', allowing for further creation of caches again.
93+
* @see #resetCaches()
8994
*/
9095
public void setCacheNames(@Nullable Collection<String> cacheNames) {
9196
if (cacheNames != null) {
@@ -160,11 +165,6 @@ public void setBeanClassLoader(ClassLoader classLoader) {
160165
}
161166

162167

163-
@Override
164-
public Collection<String> getCacheNames() {
165-
return Collections.unmodifiableSet(this.cacheMap.keySet());
166-
}
167-
168168
@Override
169169
@Nullable
170170
public Cache getCache(String name) {
@@ -175,6 +175,23 @@ public Cache getCache(String name) {
175175
return cache;
176176
}
177177

178+
@Override
179+
public Collection<String> getCacheNames() {
180+
return Collections.unmodifiableSet(this.cacheMap.keySet());
181+
}
182+
183+
/**
184+
* Reset this cache manager's caches, removing them completely for on-demand
185+
* re-creation in 'dynamic' mode, or simply clearing their entries otherwise.
186+
* @since 6.2.14
187+
*/
188+
public void resetCaches() {
189+
this.cacheMap.values().forEach(Cache::clear);
190+
if (this.dynamic) {
191+
this.cacheMap.clear();
192+
}
193+
}
194+
178195
/**
179196
* Remove the specified cache from this cache manager.
180197
* @param name the name of the cache

spring-context/src/test/java/org/springframework/cache/concurrent/ConcurrentMapCacheManagerTests.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.junit.jupiter.api.Test;
2020

2121
import org.springframework.cache.Cache;
22-
import org.springframework.cache.CacheManager;
2322

2423
import static org.assertj.core.api.Assertions.assertThat;
2524

@@ -31,7 +30,7 @@ class ConcurrentMapCacheManagerTests {
3130

3231
@Test
3332
void testDynamicMode() {
34-
CacheManager cm = new ConcurrentMapCacheManager();
33+
ConcurrentMapCacheManager cm = new ConcurrentMapCacheManager();
3534
Cache cache1 = cm.getCache("c1");
3635
assertThat(cache1).isInstanceOf(ConcurrentMapCache.class);
3736
Cache cache1again = cm.getCache("c1");
@@ -65,6 +64,14 @@ void testDynamicMode() {
6564
assertThat(cache1.get("key3").get()).isNull();
6665
cache1.evict("key3");
6766
assertThat(cache1.get("key3")).isNull();
67+
68+
cm.removeCache("c1");
69+
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
70+
assertThat(cm.getCache("c2")).isSameAs(cache2);
71+
72+
cm.resetCaches();
73+
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
74+
assertThat(cm.getCache("c2")).isNotSameAs(cache2);
6875
}
6976

7077
@Test
@@ -107,11 +114,24 @@ void testStaticMode() {
107114

108115
cm.setAllowNullValues(true);
109116
Cache cache1y = cm.getCache("c1");
117+
Cache cache2y = cm.getCache("c2");
110118

111119
cache1y.put("key3", null);
112120
assertThat(cache1y.get("key3").get()).isNull();
113121
cache1y.evict("key3");
114122
assertThat(cache1y.get("key3")).isNull();
123+
cache2y.put("key4", "value4");
124+
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
125+
126+
cm.removeCache("c1");
127+
assertThat(cm.getCache("c1")).isNull();
128+
assertThat(cm.getCache("c2")).isSameAs(cache2y);
129+
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
130+
131+
cm.resetCaches();
132+
assertThat(cm.getCache("c1")).isNull();
133+
assertThat(cm.getCache("c2")).isSameAs(cache2y);
134+
assertThat(cache2y.get("key4")).isNull();
115135
}
116136

117137
@Test

0 commit comments

Comments
 (0)