• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.07
today-context/src/main/java/infra/cache/concurrent/ConcurrentMapCacheManager.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.cache.concurrent;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.util.Arrays;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.Map;
26
import java.util.concurrent.ConcurrentHashMap;
27
import java.util.concurrent.ConcurrentMap;
28
import java.util.function.Supplier;
29

30
import infra.beans.factory.BeanClassLoaderAware;
31
import infra.cache.Cache;
32
import infra.cache.CacheManager;
33
import infra.cache.support.CaffeineCacheManager;
34
import infra.core.serializer.support.SerializationDelegate;
35

36
/**
37
 * {@link CacheManager} implementation that lazily builds {@link ConcurrentMapCache}
38
 * instances for each {@link #getCache} request. Also supports a 'static' mode where
39
 * the set of cache names is pre-defined through {@link #setCacheNames}, with no
40
 * dynamic creation of further cache regions at runtime.
41
 *
42
 * <p>Supports the asynchronous {@link Cache#retrieve(Object)} and
43
 * {@link Cache#retrieve(Object, Supplier)} operations through basic
44
 * {@code CompletableFuture} adaptation, with early-determined cache misses.
45
 *
46
 * <p>Note: This is by no means a sophisticated CacheManager; it comes with no
47
 * cache configuration options. However, it may be useful for testing or simple
48
 * caching scenarios. For advanced local caching needs, consider
49
 * {@link CaffeineCacheManager} or
50
 * {@link infra.cache.jcache.JCacheCacheManager}.
51
 *
52
 * @author Juergen Hoeller
53
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
54
 * @see ConcurrentMapCache
55
 * @since 4.0
56
 */
57
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
58

59
  private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
12✔
60

61
  private boolean dynamic = true;
6✔
62

63
  private boolean allowNullValues = true;
6✔
64

65
  private boolean storeByValue = false;
6✔
66

67
  @Nullable
68
  private SerializationDelegate serialization;
69

70
  /**
71
   * Construct a dynamic ConcurrentMapCacheManager,
72
   * lazily creating cache instances as they are being requested.
73
   */
74
  public ConcurrentMapCacheManager() { }
3✔
75

76
  /**
77
   * Construct a static ConcurrentMapCacheManager,
78
   * managing caches for the specified cache names only.
79
   */
80
  public ConcurrentMapCacheManager(String... cacheNames) {
2✔
81
    setCacheNames(Arrays.asList(cacheNames));
4✔
82
  }
1✔
83

84
  /**
85
   * Specify the set of cache names for this CacheManager's 'static' mode.
86
   * <p>The number of caches and their names will be fixed after a call to this method,
87
   * with no creation of further cache regions at runtime.
88
   * <p>Calling this with a {@code null} collection argument resets the
89
   * mode to 'dynamic', allowing for further creation of caches again.
90
   */
91
  public void setCacheNames(@Nullable Collection<String> cacheNames) {
92
    if (cacheNames != null) {
2!
93
      for (String name : cacheNames) {
10✔
94
        this.cacheMap.put(name, createConcurrentMapCache(name));
8✔
95
      }
1✔
96
      this.dynamic = false;
4✔
97
    }
98
    else {
99
      this.dynamic = true;
×
100
    }
101
  }
1✔
102

103
  /**
104
   * Specify whether to accept and convert {@code null} values for all caches
105
   * in this cache manager.
106
   * <p>Default is "true", despite ConcurrentHashMap itself not supporting {@code null}
107
   * values. An internal holder object will be used to store user-level {@code null}s.
108
   * <p>Note: A change of the null-value setting will reset all existing caches,
109
   * if any, to reconfigure them with the new null-value requirement.
110
   */
111
  public void setAllowNullValues(boolean allowNullValues) {
112
    if (allowNullValues != this.allowNullValues) {
4!
113
      this.allowNullValues = allowNullValues;
3✔
114
      // Need to recreate all Cache instances with the new null-value configuration...
115
      recreateCaches();
2✔
116
    }
117
  }
1✔
118

119
  /**
120
   * Return whether this cache manager accepts and converts {@code null} values
121
   * for all of its caches.
122
   */
123
  public boolean isAllowNullValues() {
124
    return this.allowNullValues;
3✔
125
  }
126

127
  /**
128
   * Specify whether this cache manager stores a copy of each entry ({@code true}
129
   * or the reference ({@code false} for all of its caches.
130
   * <p>Default is "false" so that the value itself is stored and no serializable
131
   * contract is required on cached values.
132
   * <p>Note: A change of the store-by-value setting will reset all existing caches,
133
   * if any, to reconfigure them with the new store-by-value requirement.
134
   *
135
   * @since 4.0
136
   */
137
  public void setStoreByValue(boolean storeByValue) {
138
    if (storeByValue != this.storeByValue) {
4!
139
      this.storeByValue = storeByValue;
3✔
140
      // Need to recreate all Cache instances with the new store-by-value configuration...
141
      recreateCaches();
2✔
142
    }
143
  }
1✔
144

145
  /**
146
   * Return whether this cache manager stores a copy of each entry or
147
   * a reference for all its caches. If store by value is enabled, any
148
   * cache entry must be serializable.
149
   *
150
   * @since 4.0
151
   */
152
  public boolean isStoreByValue() {
153
    return this.storeByValue;
3✔
154
  }
155

156
  @Override
157
  public void setBeanClassLoader(ClassLoader classLoader) {
158
    this.serialization = new SerializationDelegate(classLoader);
6✔
159
    // Need to recreate all Cache instances with new ClassLoader in store-by-value mode...
160
    if (isStoreByValue()) {
3!
161
      recreateCaches();
×
162
    }
163
  }
1✔
164

165
  @Override
166
  public Collection<String> getCacheNames() {
167
    return Collections.unmodifiableSet(this.cacheMap.keySet());
×
168
  }
169

170
  @Override
171
  @Nullable
172
  public Cache getCache(String name) {
173
    Cache cache = this.cacheMap.get(name);
6✔
174
    if (cache == null && this.dynamic) {
5✔
175
      synchronized(this.cacheMap) {
5✔
176
        cache = this.cacheMap.get(name);
6✔
177
        if (cache == null) {
2!
178
          cache = createConcurrentMapCache(name);
4✔
179
          this.cacheMap.put(name, cache);
6✔
180
        }
181
      }
3✔
182
    }
183
    return cache;
2✔
184
  }
185

186
  /**
187
   * Remove the specified cache from this cache manager.
188
   *
189
   * @param name the name of the cache
190
   * @since 5.0
191
   */
192
  public void removeCache(String name) {
193
    this.cacheMap.remove(name);
×
194
  }
×
195

196
  private void recreateCaches() {
197
    for (Map.Entry<String, Cache> entry : this.cacheMap.entrySet()) {
12✔
198
      entry.setValue(createConcurrentMapCache(entry.getKey()));
8✔
199
    }
1✔
200
  }
1✔
201

202
  /**
203
   * Create a new ConcurrentMapCache instance for the specified cache name.
204
   *
205
   * @param name the name of the cache
206
   * @return the ConcurrentMapCache (or a decorator thereof)
207
   */
208
  protected Cache createConcurrentMapCache(String name) {
209
    SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
8✔
210
    return new ConcurrentMapCache(name, new ConcurrentHashMap<>(256), isAllowNullValues(), actualSerialization);
12✔
211
  }
212

213
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc