• 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

16.67
today-context/src/main/java/infra/cache/Cache.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;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.util.concurrent.CompletableFuture;
23
import java.util.function.Supplier;
24

25
import infra.cache.annotation.Cacheable;
26
import infra.util.function.ThrowingFunction;
27

28
/**
29
 * Interface that defines common cache operations.
30
 *
31
 * <p>Serves primarily as an SPI for Infra annotation-based caching
32
 * model ({@link Cacheable} and co)
33
 * and secondarily as an API for direct usage in applications.
34
 *
35
 * <p><b>Note:</b> Due to the generic use of caching, it is recommended
36
 * that implementations allow storage of {@code null} values
37
 * (for example to cache methods that return {@code null}).
38
 *
39
 * @author Costin Leau
40
 * @author Juergen Hoeller
41
 * @author Stephane Nicoll
42
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
43
 * @see CacheManager
44
 * @see Cacheable
45
 * @since 2019-02-27 17:11
46
 */
47
public interface Cache {
48

49
  /**
50
   * Return the cache name.
51
   */
52
  String getName();
53

54
  /**
55
   * Return the underlying native cache provider.
56
   */
57
  Object getNativeCache();
58

59
  /**
60
   * Return the value to which this cache maps the specified key.
61
   * <p>Returns {@code null} if the cache contains no mapping for this key;
62
   * otherwise, the cached value (which may be {@code null} itself) will
63
   * be returned in a {@link ValueWrapper}.
64
   *
65
   * @param key the key whose associated value is to be returned
66
   * @return the value to which this cache maps the specified key,
67
   * contained within a {@link ValueWrapper} which may also hold
68
   * a cached {@code null} value. A straight {@code null} being
69
   * returned means that the cache contains no mapping for this key.
70
   * @see #get(Object, Class)
71
   * @see #get(Object, ThrowingFunction)
72
   */
73
  @Nullable
74
  ValueWrapper get(Object key);
75

76
  /**
77
   * Return the value to which this cache maps the specified key,
78
   * generically specifying a type that return value will be cast to.
79
   * <p>Note: This variant of {@code get} does not allow for differentiating
80
   * between a cached {@code null} value and no cache entry found at all.
81
   * Use the standard {@link #get(Object)} variant for that purpose instead.
82
   *
83
   * @param key the key whose associated value is to be returned
84
   * @param type the required type of the returned value (may be
85
   * {@code null} to bypass a type check; in case of a {@code null}
86
   * value found in the cache, the specified type is irrelevant)
87
   * @return the value to which this cache maps the specified key
88
   * (which may be {@code null} itself), or also {@code null} if
89
   * the cache contains no mapping for this key
90
   * @throws IllegalStateException if a cache entry has been found
91
   * but failed to match the specified type
92
   * @see #get(Object)
93
   * @since 4.0
94
   */
95
  @Nullable
96
  <T> T get(Object key, @Nullable Class<T> type);
97

98
  /**
99
   * Return the value to which this cache maps the specified key, obtaining
100
   * that value from {@code valueLoader} if necessary. This method provides
101
   * a simple substitute for the conventional "if cached, return; otherwise
102
   * create, cache and return" pattern.
103
   * <p>If possible, implementations should ensure that the loading operation
104
   * is synchronized so that the specified {@code valueLoader} is only called
105
   * once in case of concurrent access on the same key.
106
   * <p>If the {@code valueLoader} throws an exception, it is wrapped in
107
   * a {@link ValueRetrievalException}
108
   *
109
   * @param key the key whose associated value is to be returned
110
   * @return the value to which this cache maps the specified key
111
   * @throws ValueRetrievalException if the {@code valueLoader} throws an exception
112
   * @see #get(Object)
113
   * @since 5.0
114
   */
115
  @Nullable
116
  <K, V> V get(K key, ThrowingFunction<? super K, ? extends V> valueLoader);
117

118
  /**
119
   * Return the value to which this cache maps the specified key,
120
   * wrapped in a {@link CompletableFuture}. This operation must not block
121
   * but is allowed to return a completed {@link CompletableFuture} if the
122
   * corresponding value is immediately available.
123
   * <p>Can return {@code null} if the cache can immediately determine that
124
   * it contains no mapping for this key (e.g. through an in-memory key map).
125
   * Otherwise, the cached value will be returned in the {@link CompletableFuture},
126
   * with {@code null} indicating a late-determined cache miss. A nested
127
   * {@link ValueWrapper} potentially indicates a nullable cached value;
128
   * the cached value may also be represented as a plain element if null
129
   * values are not supported. Calling code needs to be prepared to handle
130
   * all those variants of the result returned by this method.
131
   *
132
   * @param key the key whose associated value is to be returned
133
   * @return the value to which this cache maps the specified key, contained
134
   * within a {@link CompletableFuture} which may also be empty when a cache
135
   * miss has been late-determined. A straight {@code null} being returned
136
   * means that the cache immediately determined that it contains no mapping
137
   * for this key. A {@link ValueWrapper} contained within the
138
   * {@code CompletableFuture} indicates a cached value that is potentially
139
   * {@code null}; this is sensible in a late-determined scenario where a regular
140
   * CompletableFuture-contained {@code null} indicates a cache miss. However,
141
   * a cache may also return a plain value if it does not support the actual
142
   * caching of {@code null} values, avoiding the extra level of value wrapping.
143
   * Infra cache processing can deal with all such implementation strategies.
144
   * @see #retrieve(Object, Supplier)
145
   */
146
  @Nullable
147
  default CompletableFuture<?> retrieve(Object key) {
148
    throw new UnsupportedOperationException(
×
149
            getClass().getName() + " does not support CompletableFuture-based retrieval");
×
150
  }
151

152
  /**
153
   * Return the value to which this cache maps the specified key, obtaining
154
   * that value from {@code valueLoader} if necessary. This method provides
155
   * a simple substitute for the conventional "if cached, return; otherwise
156
   * create, cache and return" pattern, based on {@link CompletableFuture}.
157
   * This operation must not block.
158
   * <p>If possible, implementations should ensure that the loading operation
159
   * is synchronized so that the specified {@code valueLoader} is only called
160
   * once in case of concurrent access on the same key.
161
   * <p>Null values always indicate a user-level {@code null} value with this
162
   * method. The provided {@link CompletableFuture} handle produces a value
163
   * or raises an exception. If the {@code valueLoader} raises an exception,
164
   * it will be propagated to the returned {@code CompletableFuture} handle.
165
   *
166
   * @param key the key whose associated value is to be returned
167
   * @return the value to which this cache maps the specified key, contained
168
   * within a {@link CompletableFuture} which will never be {@code null}.
169
   * The provided future is expected to produce a value or raise an exception.
170
   * @see #retrieve(Object)
171
   * @see #get(Object, ThrowingFunction)
172
   */
173
  default <T> CompletableFuture<T> retrieve(Object key, Supplier<CompletableFuture<T>> valueLoader) {
174
    throw new UnsupportedOperationException(
×
175
            getClass().getName() + " does not support CompletableFuture-based retrieval");
×
176
  }
177

178
  /**
179
   * Associate the specified value with the specified key in this cache.
180
   * <p>If the cache previously contained a mapping for this key, the old
181
   * value is replaced by the specified value.
182
   * <p>Actual registration may be performed in an asynchronous or deferred
183
   * fashion, with subsequent lookups possibly not seeing the entry yet.
184
   * This may for example be the case with transactional cache decorators.
185
   * Use {@link #putIfAbsent} for guaranteed immediate registration.
186
   *
187
   * @param key the key with which the specified value is to be associated
188
   * @param value the value to be associated with the specified key
189
   * @see #putIfAbsent(Object, Object)
190
   */
191
  void put(Object key, @Nullable Object value);
192

193
  /**
194
   * Atomically associate the specified value with the specified key in this cache
195
   * if it is not set already.
196
   * <p>This is equivalent to:
197
   * <pre><code>
198
   * ValueWrapper existingValue = cache.get(key);
199
   * if (existingValue == null) {
200
   *     cache.put(key, value);
201
   * }
202
   * return existingValue;
203
   * </code></pre>
204
   * except that the action is performed atomically. While all out-of-the-box
205
   * {@link CacheManager} implementations are able to perform the put atomically,
206
   * the operation may also be implemented in two steps, e.g. with a check for
207
   * presence and a subsequent put, in a non-atomic way. Check the documentation
208
   * of the native cache implementation that you are using for more details.
209
   * <p>The default implementation delegates to {@link #get(Object)} and
210
   * {@link #put(Object, Object)} along the lines of the code snippet above.
211
   *
212
   * @param key the key with which the specified value is to be associated
213
   * @param value the value to be associated with the specified key
214
   * @return the value to which this cache maps the specified key (which may be
215
   * {@code null} itself), or also {@code null} if the cache did not contain any
216
   * mapping for that key prior to this call. Returning {@code null} is therefore
217
   * an indicator that the given {@code value} has been associated with the key.
218
   * @see #put(Object, Object)
219
   * @since 4.0
220
   */
221
  @Nullable
222
  default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
223
    ValueWrapper existingValue = get(key);
×
224
    if (existingValue == null) {
×
225
      put(key, value);
×
226
    }
227
    return existingValue;
×
228
  }
229

230
  /**
231
   * Evict the mapping for this key from this cache if it is present.
232
   * <p>Actual eviction may be performed in an asynchronous or deferred
233
   * fashion, with subsequent lookups possibly still seeing the entry.
234
   * This may for example be the case with transactional cache decorators.
235
   * Use {@link #evictIfPresent} for guaranteed immediate removal.
236
   *
237
   * @param key the key whose mapping is to be removed from the cache
238
   * @see #evictIfPresent(Object)
239
   */
240
  void evict(Object key);
241

242
  /**
243
   * Evict the mapping for this key from this cache if it is present,
244
   * expecting the key to be immediately invisible for subsequent lookups.
245
   * <p>The default implementation delegates to {@link #evict(Object)},
246
   * returning {@code false} for not-determined prior presence of the key.
247
   * Cache providers and in particular cache decorators are encouraged
248
   * to perform immediate eviction if possible (e.g. in case of generally
249
   * deferred cache operations within a transaction) and to reliably
250
   * determine prior presence of the given key.
251
   *
252
   * @param key the key whose mapping is to be removed from the cache
253
   * @return {@code true} if the cache was known to have a mapping for
254
   * this key before, {@code false} if it did not (or if prior presence
255
   * could not be determined)
256
   * @see #evict(Object)
257
   * @since 4.0
258
   */
259
  default boolean evictIfPresent(Object key) {
260
    evict(key);
×
261
    return false;
×
262
  }
263

264
  /**
265
   * Clear the cache through removing all mappings.
266
   * <p>Actual clearing may be performed in an asynchronous or deferred
267
   * fashion, with subsequent lookups possibly still seeing the entries.
268
   * This may for example be the case with transactional cache decorators.
269
   * Use {@link #invalidate()} for guaranteed immediate removal of entries.
270
   *
271
   * @see #invalidate()
272
   */
273
  void clear();
274

275
  /**
276
   * Invalidate the cache through removing all mappings, expecting all
277
   * entries to be immediately invisible for subsequent lookups.
278
   *
279
   * @return {@code true} if the cache was known to have mappings before,
280
   * {@code false} if it did not (or if prior presence of entries could
281
   * not be determined)
282
   * @see #clear()
283
   * @since 4.0
284
   */
285
  default boolean invalidate() {
286
    clear();
×
287
    return false;
×
288
  }
289

290
  /**
291
   * A (wrapper) object representing a cache value.
292
   */
293
  @FunctionalInterface
294
  interface ValueWrapper {
295

296
    /**
297
     * Return the actual value in the cache.
298
     */
299
    @Nullable
300
    Object get();
301
  }
302

303
  /**
304
   * Wrapper exception to be thrown from {@link #get(Object, ThrowingFunction)}
305
   * in case of the value loader callback failing with an exception.
306
   */
307
  @SuppressWarnings("serial")
308
  class ValueRetrievalException extends RuntimeException {
309

310
    @Nullable
311
    private final Object key;
312

313
    public ValueRetrievalException(@Nullable Object key, Object loader, @Nullable Throwable ex) {
314
      super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
15✔
315
      this.key = key;
3✔
316
    }
1✔
317

318
    @Nullable
319
    public Object getKey() {
320
      return this.key;
×
321
    }
322
  }
323

324
}
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