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

ben-manes / caffeine / #5651

18 Jul 2026 03:26AM UTC coverage: 66.303% (-33.7%) from 100.0%
#5651

push

github

ben-manes
dependency updates

2809 of 4164 branches covered (67.46%)

5584 of 8422 relevant lines covered (66.3%)

0.66 hits per line

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

0.0
/jcache/src/main/java/com/github/benmanes/caffeine/jcache/integration/JCacheLoaderAdapter.java
1
/*
2
 * Copyright 2015 Ben Manes. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.github.benmanes.caffeine.jcache.integration;
17

18
import static java.util.Objects.requireNonNull;
19

20
import java.lang.System.Logger;
21
import java.lang.System.Logger.Level;
22
import java.util.HashMap;
23
import java.util.Map;
24
import java.util.Set;
25
import java.util.concurrent.TimeUnit;
26

27
import javax.cache.expiry.Duration;
28
import javax.cache.expiry.ExpiryPolicy;
29
import javax.cache.integration.CacheLoader;
30
import javax.cache.integration.CacheLoaderException;
31

32
import org.jspecify.annotations.Nullable;
33

34
import com.github.benmanes.caffeine.cache.Ticker;
35
import com.github.benmanes.caffeine.jcache.CacheProxy;
36
import com.github.benmanes.caffeine.jcache.Expirable;
37
import com.github.benmanes.caffeine.jcache.copy.Copier;
38
import com.github.benmanes.caffeine.jcache.event.EventDispatcher;
39
import com.github.benmanes.caffeine.jcache.management.JCacheStatisticsMXBean;
40
import com.google.errorprone.annotations.Var;
41

42
/**
43
 * An adapter from a JCache cache loader to Caffeine's.
44
 *
45
 * @author ben.manes@gmail.com (Ben Manes)
46
 */
47
public final class JCacheLoaderAdapter<K, V>
48
    implements com.github.benmanes.caffeine.cache.CacheLoader<K, @Nullable Expirable<V>> {
49
  private static final Logger logger = System.getLogger(JCacheLoaderAdapter.class.getName());
×
50

51
  private final JCacheStatisticsMXBean statistics;
52
  private final EventDispatcher<K, V> dispatcher;
53
  private final CacheLoader<K, V> delegate;
54
  private final ClassLoader classLoader;
55
  private final ExpiryPolicy expiry;
56
  private final Copier copier;
57
  private final Ticker ticker;
58

59
  private @Nullable CacheProxy<K, V> cache;
60

61
  public JCacheLoaderAdapter(CacheLoader<K, V> delegate, EventDispatcher<K, V> dispatcher,
62
      ExpiryPolicy expiry, Ticker ticker, JCacheStatisticsMXBean statistics,
63
      Copier copier, ClassLoader classLoader) {
×
64
    this.dispatcher = requireNonNull(dispatcher);
×
65
    this.statistics = requireNonNull(statistics);
×
66
    this.classLoader = requireNonNull(classLoader);
×
67
    this.delegate = requireNonNull(delegate);
×
68
    this.copier = requireNonNull(copier);
×
69
    this.expiry = requireNonNull(expiry);
×
70
    this.ticker = requireNonNull(ticker);
×
71
  }
×
72

73
  /**
74
   * Sets the cache instance that was created with this loader.
75
   *
76
   * @param cache the cache that uses this loader
77
   */
78
  public void setCache(CacheProxy<K, V> cache) {
79
    this.cache = requireNonNull(cache);
×
80
  }
×
81

82
  @Override
83
  @SuppressWarnings("ConstantValue")
84
  public @Nullable Expirable<V> load(K key) {
85
    try {
86
      boolean statsEnabled = statistics.isEnabled();
×
87
      long start = statsEnabled ? ticker.read() : 0L;
×
88

89
      V value = delegate.load(key);
×
90
      @Var Expirable<V> expirable = null;
×
91
      if (value != null) {
×
92
        requireNonNull(cache);
×
93
        V copy = copyOf(value);
×
94
        long expireTime = expireTimeMillis(/* created= */ true);
×
95
        if (expireTime == 0L) {
×
96
          // Per JSR-107 1.1.1 p.55: ZERO duration → entry is already expired
97
          // and will not be added to the Cache. Match the put-path convention
98
          // of publishing EXPIRED in place of CREATED.
99
          dispatcher.publishExpired(cache, key, copy);
×
100
        } else {
101
          expirable = new Expirable<>(copy, expireTime);
×
102
          dispatcher.publishCreated(cache, key, copy);
×
103
        }
104
      }
105

106
      if (statsEnabled) {
×
107
        // Subtracts the load time from the get time
108
        statistics.recordGetTime(start - ticker.read());
×
109
      }
110
      return expirable;
×
111
    } catch (CacheLoaderException e) {
×
112
      throw e;
×
113
    } catch (RuntimeException e) {
×
114
      throw new CacheLoaderException(e);
×
115
    }
116
  }
117

118
  @Override
119
  public Map<K, Expirable<V>> loadAll(Set<? extends K> keys) {
120
    try {
121
      boolean statsEnabled = statistics.isEnabled();
×
122
      long start = statsEnabled ? ticker.read() : 0L;
×
123
      requireNonNull(cache);
×
124

125
      @SuppressWarnings("ConstantValue")
126
      Map<K, V> loaded = delegate.loadAll(keys);
×
127
      var result = new HashMap<K, Expirable<V>>(loaded.size());
×
128
      for (var entry : loaded.entrySet()) {
×
129
        K key = entry.getKey();
×
130
        V value = entry.getValue();
×
131
        if ((key == null) || (value == null)) {
×
132
          continue;
×
133
        }
134
        V copy = copyOf(value);
×
135
        long expireTime = expireTimeMillis(/* created= */ true);
×
136
        if (expireTime == 0L) {
×
137
          // ZERO → already expired and not added; match the put-path convention.
138
          dispatcher.publishExpired(cache, key, copy);
×
139
        } else {
140
          result.put(key, new Expirable<>(copy, expireTime));
×
141
        }
142
      }
×
143
      for (var entry : result.entrySet()) {
×
144
        dispatcher.publishCreated(cache, entry.getKey(), entry.getValue().get());
×
145
      }
×
146

147
      if (statsEnabled) {
×
148
        // Subtracts the load time from the get time
149
        statistics.recordGetTime(start - ticker.read());
×
150
      }
151
      return result;
×
152
    } catch (CacheLoaderException e) {
×
153
      throw e;
×
154
    } catch (RuntimeException e) {
×
155
      throw new CacheLoaderException(e);
×
156
    }
157
  }
158

159
  @Override
160
  public @Nullable Expirable<V> reload(K key, Expirable<V> oldValue) {
161
    try {
162
      V value = delegate.load(key);
×
163
      if (value == null) {
×
164
        return null;
×
165
      }
166
      requireNonNull(cache);
×
167
      V copy = copyOf(value);
×
168
      @Var long expireTime = expireTimeMillis(/* created= */ false);
×
169
      if (expireTime == Long.MIN_VALUE) {
×
170
        expireTime = oldValue.getExpireTimeMillis();
×
171
      }
172
      if (expireTime == 0L) {
×
173
        dispatcher.publishExpiredQuietly(cache, key, copy);
×
174
        return null;
×
175
      }
176
      dispatcher.publishUpdatedQuietly(cache, key, oldValue.get(), copy);
×
177
      return new Expirable<>(copy, expireTime);
×
178
    } catch (CacheLoaderException e) {
×
179
      throw e;
×
180
    } catch (RuntimeException e) {
×
181
      throw new CacheLoaderException(e);
×
182
    }
183
  }
184

185
  /** Returns a copy of the value if value-based caching is enabled. */
186
  private V copyOf(V value) {
187
    return requireNonNull(copier.copy(value, classLoader));
×
188
  }
189

190
  private long expireTimeMillis(boolean created) {
191
    try {
192
      Duration duration = created ? expiry.getExpiryForCreation() : expiry.getExpiryForUpdate();
×
193
      if (duration == null) {
×
194
        return created ? Long.MAX_VALUE : Long.MIN_VALUE;
×
195
      } else if (duration.isZero()) {
×
196
        return 0;
×
197
      } else if (duration.isEternal()) {
×
198
        return Long.MAX_VALUE;
×
199
      }
200
      long millis = TimeUnit.NANOSECONDS.toMillis(ticker.read());
×
201
      long expireTime = duration.getAdjustedTime(millis);
×
202
      return ((expireTime == 0L) || (expireTime == Long.MAX_VALUE)) ? (expireTime - 1) : expireTime;
×
203
    } catch (RuntimeException e) {
×
204
      // Per JSR-107 1.1.1 p.55 a throwing policy uses an implementation default: creation falls
205
      // back to eternal so the entry is not lost, an update leaves the expiration unchanged
206
      logger.log(Level.WARNING, "Exception thrown by expiry policy", e);
×
207
      return created ? Long.MAX_VALUE : Long.MIN_VALUE;
×
208
    }
209
  }
210
}
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