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

TAKETODAY / today-infrastructure / 16523034142

25 Jul 2025 01:22PM UTC coverage: 81.778% (-0.004%) from 81.782%
16523034142

push

github

TAKETODAY
:art: Cache API

59440 of 77637 branches covered (76.56%)

Branch coverage included in aggregate %.

140761 of 167174 relevant lines covered (84.2%)

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 java.util.concurrent.CompletableFuture;
24
import java.util.function.Function;
25
import java.util.function.Supplier;
26

27
import infra.cache.Cache;
28
import infra.lang.Assert;
29
import infra.lang.Nullable;
30
import infra.util.function.ThrowingFunction;
31

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

52
  private final String name;
53

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

207
    @Nullable
208
    private final Object value;
209

210
    private boolean called;
211

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

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

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

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

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

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

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