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

alibaba / jetcache / #405

16 Apr 2024 05:58AM UTC coverage: 0.0% (-88.9%) from 88.866%
#405

push

areyouok
add encoding to fix coverage report

0 of 5353 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/jetcache-core/src/main/java/com/alicp/jetcache/AbstractCache.java
1
package com.alicp.jetcache;
2

3
import com.alicp.jetcache.embedded.AbstractEmbeddedCache;
4
import com.alicp.jetcache.event.CacheEvent;
5
import com.alicp.jetcache.event.CacheGetAllEvent;
6
import com.alicp.jetcache.event.CacheGetEvent;
7
import com.alicp.jetcache.event.CachePutAllEvent;
8
import com.alicp.jetcache.event.CachePutEvent;
9
import com.alicp.jetcache.event.CacheRemoveAllEvent;
10
import com.alicp.jetcache.event.CacheRemoveEvent;
11
import com.alicp.jetcache.external.AbstractExternalCache;
12
import com.alicp.jetcache.support.SquashedLogger;
13
import org.slf4j.Logger;
14
import org.slf4j.LoggerFactory;
15

16
import java.nio.ByteBuffer;
17
import java.time.Duration;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.concurrent.CountDownLatch;
23
import java.util.concurrent.TimeUnit;
24
import java.util.concurrent.locks.ReentrantLock;
25
import java.util.function.Consumer;
26
import java.util.function.Function;
27

28
/**
29
 * Created on 2016/10/7.
30
 *
31
 * @author huangli
32
 */
33
public abstract class AbstractCache<K, V> implements Cache<K, V> {
×
34

35
    private static Logger logger = LoggerFactory.getLogger(AbstractCache.class);
×
36

37
    private volatile ConcurrentHashMap<Object, LoaderLock> loaderMap;
38

39
    protected volatile boolean closed;
40
    private static final ReentrantLock reentrantLock = new ReentrantLock();
×
41

42
    ConcurrentHashMap<Object, LoaderLock> initOrGetLoaderMap() {
43
        if (loaderMap == null) {
×
44
            reentrantLock.lock();
×
45
            try {
46
                if (loaderMap == null) {
×
47
                    loaderMap = new ConcurrentHashMap<>();
×
48
                }
49
            }finally {
50
                reentrantLock.unlock();
×
51
            }
52
        }
53
        return loaderMap;
×
54
    }
55

56
    protected void logError(String oper, Object key, Throwable e) {
57
        StringBuilder sb = new StringBuilder(256);
×
58
        sb.append("jetcache(")
×
59
                .append(this.getClass().getSimpleName()).append(") ")
×
60
                .append(oper)
×
61
                .append(" error.");
×
62
        if (!(key instanceof byte[])) {
×
63
            try {
64
                sb.append(" key=[")
×
65
                        .append(config().getKeyConvertor().apply((K) key))
×
66
                        .append(']');
×
67
            } catch (Exception ex) {
×
68
                // ignore
69
            }
×
70
        }
71
        SquashedLogger.getLogger(logger).error(sb, e);
×
72
    }
×
73

74
    public void notify(CacheEvent e) {
75
        List<CacheMonitor> monitors = config().getMonitors();
×
76
        for (CacheMonitor m : monitors) {
×
77
            m.afterOperation(e);
×
78
        }
×
79
    }
×
80

81
    @Override
82
    public final CacheGetResult<V> GET(K key) {
83
        long t = System.currentTimeMillis();
×
84
        CacheGetResult<V> result;
85
        if (key == null) {
×
86
            result = new CacheGetResult<V>(CacheResultCode.FAIL, CacheResult.MSG_ILLEGAL_ARGUMENT, null);
×
87
        } else {
88
            result = do_GET(key);
×
89
        }
90
        result.future().thenRun(() -> {
×
91
            CacheGetEvent event = new CacheGetEvent(this, System.currentTimeMillis() - t, key, result);
×
92
            notify(event);
×
93
        });
×
94
        return result;
×
95
    }
96

97
    protected abstract CacheGetResult<V> do_GET(K key);
98

99
    @Override
100
    public final MultiGetResult<K, V> GET_ALL(Set<? extends K> keys) {
101
        long t = System.currentTimeMillis();
×
102
        MultiGetResult<K, V> result;
103
        if (keys == null) {
×
104
            result = new MultiGetResult<>(CacheResultCode.FAIL, CacheResult.MSG_ILLEGAL_ARGUMENT, null);
×
105
        } else {
106
            result = do_GET_ALL(keys);
×
107
        }
108
        result.future().thenRun(() -> {
×
109
            CacheGetAllEvent event = new CacheGetAllEvent(this, System.currentTimeMillis() - t, keys, result);
×
110
            notify(event);
×
111
        });
×
112
        return result;
×
113
    }
114

115
    protected abstract MultiGetResult<K, V> do_GET_ALL(Set<? extends K> keys);
116

117
    @Override
118
    public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull) {
119
        return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull,
×
120
                0, null, this);
121
    }
122

123
    @Override
124
    public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull,
125
                                   long expireAfterWrite, TimeUnit timeUnit) {
126
        return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull,
×
127
                expireAfterWrite, timeUnit, this);
128
    }
129

130
    private static <K, V> boolean needUpdate(V loadedValue, boolean cacheNullWhenLoaderReturnNull, Function<K, V> loader) {
131
        if (loadedValue == null && !cacheNullWhenLoaderReturnNull) {
×
132
            return false;
×
133
        }
134
        if (loader instanceof CacheLoader && ((CacheLoader<K, V>) loader).vetoCacheUpdate()) {
×
135
            return false;
×
136
        }
137
        return true;
×
138
    }
139

140
    static <K, V> V computeIfAbsentImpl(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull,
141
                                               long expireAfterWrite, TimeUnit timeUnit, Cache<K, V> cache) {
142
        AbstractCache<K, V> abstractCache = CacheUtil.getAbstractCache(cache);
×
143
        CacheLoader<K, V> newLoader = CacheUtil.createProxyLoader(cache, loader, abstractCache::notify);
×
144
        CacheGetResult<V> r;
145
        if (cache instanceof RefreshCache) {
×
146
            RefreshCache<K, V> refreshCache = ((RefreshCache<K, V>) cache);
×
147
            r = refreshCache.GET(key);
×
148
            refreshCache.addOrUpdateRefreshTask(key, newLoader);
×
149
        } else {
×
150
            r = cache.GET(key);
×
151
        }
152
        if (r.isSuccess()) {
×
153
            return r.getValue();
×
154
        } else {
155
            Consumer<V> cacheUpdater = (loadedValue) -> {
×
156
                if(needUpdate(loadedValue, cacheNullWhenLoaderReturnNull, newLoader)) {
×
157
                    if (timeUnit != null) {
×
158
                        cache.PUT(key, loadedValue, expireAfterWrite, timeUnit).waitForResult();
×
159
                    } else {
160
                        cache.PUT(key, loadedValue).waitForResult();
×
161
                    }
162
                }
163
            };
×
164

165
            V loadedValue;
166
            if (cache.config().isCachePenetrationProtect()) {
×
167
                loadedValue = synchronizedLoad(cache.config(), abstractCache, key, newLoader, cacheUpdater);
×
168
            } else {
169
                loadedValue = newLoader.apply(key);
×
170
                cacheUpdater.accept(loadedValue);
×
171
            }
172

173
            return loadedValue;
×
174
        }
175
    }
176

177
    static <K, V> V synchronizedLoad(CacheConfig config, AbstractCache<K,V> abstractCache,
178
                                     K key, Function<K, V> newLoader, Consumer<V> cacheUpdater) {
179
        ConcurrentHashMap<Object, LoaderLock> loaderMap = abstractCache.initOrGetLoaderMap();
×
180
        Object lockKey = buildLoaderLockKey(abstractCache, key);
×
181
        while (true) {
182
            boolean create[] = new boolean[1];
×
183
            LoaderLock ll = loaderMap.computeIfAbsent(lockKey, (unusedKey) -> {
×
184
                create[0] = true;
×
185
                LoaderLock loaderLock = new LoaderLock();
×
186
                loaderLock.signal = new CountDownLatch(1);
×
187
                loaderLock.loaderThread = Thread.currentThread();
×
188
                return loaderLock;
×
189
            });
190
            if (create[0] || ll.loaderThread == Thread.currentThread()) {
×
191
                try {
192
                    CacheGetResult<V> getResult = abstractCache.GET(key);
×
193
                    if (getResult.isSuccess()) {
×
194
                        ll.success = true;
×
195
                        ll.value = getResult.getValue();
×
196
                        return getResult.getValue();
×
197
                    } else {
198
                        V loadedValue = newLoader.apply(key);
×
199
                        ll.success = true;
×
200
                        ll.value = loadedValue;
×
201
                        cacheUpdater.accept(loadedValue);
×
202
                        return loadedValue;
×
203
                    }
204
                } finally {
205
                    if (create[0]) {
×
206
                        ll.signal.countDown();
×
207
                        loaderMap.remove(lockKey);
×
208
                    }
209
                }
210
            } else {
211
                try {
212
                    Duration timeout = config.getPenetrationProtectTimeout();
×
213
                    if (timeout == null) {
×
214
                        ll.signal.await();
×
215
                    } else {
216
                        boolean ok = ll.signal.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
×
217
                        if(!ok) {
×
218
                            logger.info("loader wait timeout:" + timeout);
×
219
                            return newLoader.apply(key);
×
220
                        }
221
                    }
222
                } catch (InterruptedException e) {
×
223
                    logger.warn("loader wait interrupted");
×
224
                    return newLoader.apply(key);
×
225
                }
×
226
                if (ll.success) {
×
227
                    return (V) ll.value;
×
228
                } else {
229
                    continue;
230
                }
231

232
            }
233
        }
234
    }
235

236
    private static Object buildLoaderLockKey(Cache c, Object key) {
237
        if (c instanceof AbstractEmbeddedCache) {
×
238
            return ((AbstractEmbeddedCache) c).buildKey(key);
×
239
        } else if (c instanceof AbstractExternalCache) {
×
240
            byte bytes[] = ((AbstractExternalCache) c).buildKey(key);
×
241
            return ByteBuffer.wrap(bytes);
×
242
        } else if (c instanceof MultiLevelCache) {
×
243
            c = ((MultiLevelCache) c).caches()[0];
×
244
            return buildLoaderLockKey(c, key);
×
245
        } else if(c instanceof ProxyCache) {
×
246
            c = ((ProxyCache) c).getTargetCache();
×
247
            return buildLoaderLockKey(c, key);
×
248
        } else {
249
            throw new CacheException("impossible");
×
250
        }
251
    }
252

253
    @Override
254
    public final CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
255
        long t = System.currentTimeMillis();
×
256
        CacheResult result;
257
        if (key == null) {
×
258
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
259
        } else {
260
            result = do_PUT(key, value, expireAfterWrite, timeUnit);
×
261
        }
262
        result.future().thenRun(() -> {
×
263
            CachePutEvent event = new CachePutEvent(this, System.currentTimeMillis() - t, key, value, result);
×
264
            notify(event);
×
265
        });
×
266
        return result;
×
267
    }
268

269
    protected abstract CacheResult do_PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
270

271
    @Override
272
    public final CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) {
273
        long t = System.currentTimeMillis();
×
274
        CacheResult result;
275
        if (map == null) {
×
276
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
277
        } else {
278
            result = do_PUT_ALL(map, expireAfterWrite, timeUnit);
×
279
        }
280
        result.future().thenRun(() -> {
×
281
            CachePutAllEvent event = new CachePutAllEvent(this, System.currentTimeMillis() - t, map, result);
×
282
            notify(event);
×
283
        });
×
284
        return result;
×
285
    }
286

287
    protected abstract CacheResult do_PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
288

289
    @Override
290
    public final CacheResult REMOVE(K key) {
291
        long t = System.currentTimeMillis();
×
292
        CacheResult result;
293
        if (key == null) {
×
294
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
295
        } else {
296
            result = do_REMOVE(key);
×
297
        }
298
        result.future().thenRun(() -> {
×
299
            CacheRemoveEvent event = new CacheRemoveEvent(this, System.currentTimeMillis() - t, key, result);
×
300
            notify(event);
×
301
        });
×
302
        return result;
×
303
    }
304

305
    protected abstract CacheResult do_REMOVE(K key);
306

307
    @Override
308
    public final CacheResult REMOVE_ALL(Set<? extends K> keys) {
309
        long t = System.currentTimeMillis();
×
310
        CacheResult result;
311
        if (keys == null) {
×
312
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
313
        } else {
314
            result = do_REMOVE_ALL(keys);
×
315
        }
316
        result.future().thenRun(() -> {
×
317
            CacheRemoveAllEvent event = new CacheRemoveAllEvent(this, System.currentTimeMillis() - t, keys, result);
×
318
            notify(event);
×
319
        });
×
320
        return result;
×
321
    }
322

323
    protected abstract CacheResult do_REMOVE_ALL(Set<? extends K> keys);
324

325
    @Override
326
    public final CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
327
        long t = System.currentTimeMillis();
×
328
        CacheResult result;
329
        if (key == null) {
×
330
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
331
        } else {
332
            result = do_PUT_IF_ABSENT(key, value, expireAfterWrite, timeUnit);
×
333
        }
334
        result.future().thenRun(() -> {
×
335
            CachePutEvent event = new CachePutEvent(this, System.currentTimeMillis() - t, key, value, result);
×
336
            notify(event);
×
337
        });
×
338
        return result;
×
339
    }
340

341
    protected abstract CacheResult do_PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
342

343
    @Override
344
    public void close() {
345
        this.closed = true;
×
346
    }
×
347

348
    public boolean isClosed() {
349
        return this.closed;
×
350
    }
351

352
    static class LoaderLock {
×
353
        CountDownLatch signal;
354
        Thread loaderThread;
355
        volatile boolean success;
356
        volatile Object value;
357
    }
358
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc