• 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

84.72
today-context/src/main/java/infra/cache/support/CaffeineCache.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.support;
19

20
import com.github.benmanes.caffeine.cache.AsyncCache;
21
import com.github.benmanes.caffeine.cache.LoadingCache;
22

23
import org.jspecify.annotations.Nullable;
24

25
import java.util.concurrent.CompletableFuture;
26
import java.util.function.Function;
27
import java.util.function.Supplier;
28

29
import infra.cache.Cache;
30
import infra.lang.Assert;
31
import infra.util.function.ThrowingFunction;
32

33
/**
34
 * Infra {@link Cache} adapter implementation
35
 * on top of a Caffeine {@link com.github.benmanes.caffeine.cache.Cache} instance.
36
 *
37
 * <p>Supports the {@link #retrieve(Object)} and {@link #retrieve(Object, Supplier)}
38
 * operations through Caffeine's {@link AsyncCache}, when provided via the
39
 * {@link #CaffeineCache(String, AsyncCache, boolean)} constructor.
40
 *
41
 * <p>Requires Caffeine 3.0 or higher.
42
 *
43
 * @author Ben Manes
44
 * @author Juergen Hoeller
45
 * @author Stephane Nicoll
46
 * @author <a href="https://github.com/TAKETODAY">海子 Yang</a>
47
 * @see CaffeineCacheManager
48
 * @since 3.0 2020-08-15 19:50
49
 */
50
@SuppressWarnings("unchecked")
51
public class CaffeineCache extends AbstractValueAdaptingCache {
52

53
  private final String name;
54

55
  @SuppressWarnings("rawtypes")
56
  private final com.github.benmanes.caffeine.cache.Cache cache;
57

58
  @Nullable
59
  private AsyncCache<Object, Object> asyncCache;
60

61
  /**
62
   * Create a {@link CaffeineCache} instance with the specified name and the
63
   * given internal {@link com.github.benmanes.caffeine.cache.Cache} to use.
64
   *
65
   * @param name the name of the cache
66
   * @param cache the backing Caffeine Cache instance
67
   */
68
  public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache) {
69
    this(name, cache, true);
5✔
70
  }
1✔
71

72
  /**
73
   * Create a {@link CaffeineCache} instance with the specified name and the
74
   * given internal {@link com.github.benmanes.caffeine.cache.Cache} to use.
75
   *
76
   * @param name the name of the cache
77
   * @param cache the backing Caffeine Cache instance
78
   * @param allowNullValues whether to accept and convert {@code null}
79
   * values for this cache
80
   */
81
  public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache, boolean allowNullValues) {
82
    super(allowNullValues);
3✔
83
    Assert.notNull(name, "Name is required");
3✔
84
    Assert.notNull(cache, "Cache is required");
3✔
85
    this.name = name;
3✔
86
    this.cache = cache;
3✔
87
  }
1✔
88

89
  /**
90
   * Create a {@link CaffeineCache} instance with the specified name and the
91
   * given internal {@link AsyncCache} to use.
92
   *
93
   * @param name the name of the cache
94
   * @param cache the backing Caffeine AsyncCache instance
95
   * @param allowNullValues whether to accept and convert {@code null} values
96
   * for this cache
97
   */
98
  public CaffeineCache(String name, AsyncCache<Object, Object> cache, boolean allowNullValues) {
99
    super(allowNullValues);
3✔
100
    Assert.notNull(name, "Name is required");
3✔
101
    Assert.notNull(cache, "Cache is required");
3✔
102
    this.name = name;
3✔
103
    this.cache = cache.synchronous();
4✔
104
    this.asyncCache = cache;
3✔
105
  }
1✔
106

107
  @Override
108
  public final String getName() {
109
    return name;
3✔
110
  }
111

112
  /**
113
   * Return the internal Caffeine Cache
114
   * (possibly an adapter on top of an {@link #getAsyncCache()}).
115
   */
116
  @Override
117
  public final com.github.benmanes.caffeine.cache.Cache<Object, Object> getNativeCache() {
118
    return cache;
3✔
119
  }
120

121
  /**
122
   * Return the internal Caffeine AsyncCache.
123
   *
124
   * @throws IllegalStateException if no AsyncCache is available
125
   * @see #CaffeineCache(String, AsyncCache, boolean)
126
   * @see CaffeineCacheManager#setAsyncCacheMode
127
   * @since 4.0
128
   */
129
  public final AsyncCache<Object, Object> getAsyncCache() {
130
    Assert.state(asyncCache != null,
8✔
131
            "No Caffeine AsyncCache available: set CaffeineCacheManager.setAsyncCacheMode(true)");
132
    return asyncCache;
3✔
133
  }
134

135
  @Nullable
136
  public <K, V> V get(K key, ThrowingFunction<? super K, ? extends V> valueLoader) {
137
    return (V) fromStoreValue(cache.get(key, new LoadFunction(valueLoader)));
12✔
138
  }
139

140
  @Override
141
  @Nullable
142
  public CompletableFuture<?> retrieve(Object key) {
143
    CompletableFuture<?> result = getAsyncCache().getIfPresent(key);
5✔
144
    if (result != null && allowNullValues) {
5!
145
      result = result.handle((value, ex) -> fromStoreValue(value));
9✔
146
    }
147
    return result;
2✔
148
  }
149

150
  @Override
151
  public <T> CompletableFuture<T> retrieve(Object key, Supplier<CompletableFuture<T>> valueLoader) {
152
    if (allowNullValues) {
3!
153
      return (CompletableFuture<T>) getAsyncCache()
7✔
154
              .get(key, (k, e) -> valueLoader.get().thenApply(this::toStoreValue))
10✔
155
              .thenApply(this::fromStoreValue);
1✔
156
    }
157
    else {
158
      return (CompletableFuture<T>) getAsyncCache().get(key, (k, e) -> valueLoader.get());
×
159
    }
160
  }
161

162
  @Override
163
  @Nullable
164
  protected Object lookup(Object key) {
165
    if (cache instanceof LoadingCache) {
4✔
166
      return ((LoadingCache<Object, Object>) cache).get(key);
6✔
167
    }
168
    return cache.getIfPresent(key);
5✔
169
  }
170

171
  @Override
172
  public void put(Object key, @Nullable Object value) {
173
    cache.put(key, toStoreValue(value));
7✔
174
  }
1✔
175

176
  @Override
177
  @Nullable
178
  public ValueWrapper putIfAbsent(Object key, @Nullable final Object value) {
179
    PutIfAbsentFunction callable = new PutIfAbsentFunction(value);
6✔
180
    Object result = cache.get(key, callable);
6✔
181
    return (callable.called ? null : toValueWrapper(result));
9✔
182
  }
183

184
  @Override
185
  public void evict(Object key) {
186
    cache.invalidate(key);
4✔
187
  }
1✔
188

189
  @Override
190
  public boolean evictIfPresent(Object key) {
191
    return (cache.asMap().remove(key) != null);
×
192
  }
193

194
  @Override
195
  public void clear() {
196
    cache.invalidateAll();
3✔
197
  }
1✔
198

199
  @Override
200
  public boolean invalidate() {
201
    boolean notEmpty = !cache.asMap().isEmpty();
×
202
    cache.invalidateAll();
×
203
    return notEmpty;
×
204
  }
205

206
  private class PutIfAbsentFunction implements Function<Object, Object> {
207

208
    @Nullable
209
    private final Object value;
210

211
    private boolean called;
212

213
    public PutIfAbsentFunction(@Nullable Object value) {
5✔
214
      this.value = value;
3✔
215
    }
1✔
216

217
    @Override
218
    public Object apply(Object key) {
219
      this.called = true;
3✔
220
      return toStoreValue(value);
6✔
221
    }
222
  }
223

224
  private class LoadFunction implements Function<Object, Object> {
225

226
    private final ThrowingFunction<Object, Object> valueLoader;
227

228
    @SuppressWarnings("rawtypes")
229
    public LoadFunction(ThrowingFunction valueLoader) {
5✔
230
      this.valueLoader = valueLoader;
3✔
231
    }
1✔
232

233
    @Override
234
    public Object apply(Object o) {
235
      try {
236
        return toStoreValue(valueLoader.apply(o));
8✔
237
      }
238
      catch (Exception ex) {
1✔
239
        throw new ValueRetrievalException(o, valueLoader, ex);
8✔
240
      }
241
    }
242
  }
243

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