• 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/Cache.java
1
package com.alicp.jetcache;
2

3
import org.slf4j.Logger;
4
import org.slf4j.LoggerFactory;
5

6
import java.io.Closeable;
7
import java.util.Map;
8
import java.util.Set;
9
import java.util.UUID;
10
import java.util.concurrent.TimeUnit;
11
import java.util.function.Function;
12

13
/**
14
 * The cache interface, null value is supported.
15
 *
16
 * @author huangli
17
 */
18
public interface Cache<K, V> extends Closeable {
19

20
    Logger logger = LoggerFactory.getLogger(Cache.class);
×
21

22
    //-----------------------------JSR 107 style API------------------------------------------------
23

24
    /**
25
     * Gets an entry from the cache.
26
     * <p>If the cache's builder has specified a {@link CacheLoader} and there is no association in the cache
27
     * , it will attempt to load the entry.</p>
28
     * <p>If error occurs during cache access, the method return null instead of throwing an exception.</p>
29
     * @param key the key whose associated value is to be returned
30
     * @return the associated value. null may indicates: <ul>
31
     *     <li>the entry does not exist or expired</li>
32
     *     <li>the entry value is null</li>
33
     *     <li>error occurs during cache access(no exception throws)</li>
34
     * </ul>
35
     * @throws CacheInvokeException only if loader throws an exception
36
     * @see CacheLoader
37
     * @see #GET(Object)
38
     */
39
    default V get(K key) throws CacheInvokeException {
40
        CacheGetResult<V> result = GET(key);
×
41
        if (result.isSuccess()) {
×
42
            return result.getValue();
×
43
        } else {
44
            return null;
×
45
        }
46
    }
47

48
    /**
49
     * Gets a collection of entries from the Cache, returning them as Map of the values associated with
50
     * the set of keys requested.
51
     * <p>If the cache's builder has specified a {@link CacheLoader} and there is no association in the cache
52
     * , it will attempt to load the entry.</p>
53
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
54
     * @param keys The keys whose associated values are to be returned.
55
     * @return A map of entries that were found for the given keys. Keys not found in the cache are not in the returned map.
56
     * @throws CacheInvokeException only if loader throws an exception
57
     * @see CacheLoader
58
     * @see #GET_ALL(Set)
59
     */
60
    default Map<K, V> getAll(Set<? extends K> keys) throws CacheInvokeException {
61
        MultiGetResult<K, V> cacheGetResults = GET_ALL(keys);
×
62
        return cacheGetResults.unwrapValues();
×
63
    }
64

65
    /**
66
     * Associates the specified value with the specified key in the cache.
67
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
68
     * <p>if the implementation supports asynchronous operation, the cache operation of this method is asynchronous.</p>
69
     * @param key key with which the specified value is to be associated
70
     * @param value value to be associated with the specified key
71
     * @see #PUT(Object, Object)
72
     */
73
    default void put(K key, V value) {
74
        PUT(key, value);
×
75
    }
×
76

77
    /**
78
     * Copies all of the entries from the specified map to the cache.
79
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
80
     * <p>if the implementation supports asynchronous operation, the cache operation of this method is asynchronous.</p>
81
     * @param map mappings to be stored in this cache.
82
     * @see #PUT_ALL(Map)
83
     */
84
    default void putAll(Map<? extends K, ? extends V> map) {
85
        PUT_ALL(map);
×
86
    }
×
87

88
    /**
89
     * Atomically associates the specified key with the given value if it is not already associated with a value.
90
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
91
     * <p>{@link MultiLevelCache} does not support this method.</p>
92
     * @param key key with which the specified value is to be associated
93
     * @param value value to be associated with the specified key
94
     * @return true if a value was set, false if the KV association does not exists in the cache,
95
     *         or error occurs during cache access.
96
     * @see #PUT_IF_ABSENT(Object, Object, long, TimeUnit)
97
     */
98
    default boolean putIfAbsent(K key, V value) {
99
        CacheResult result = PUT_IF_ABSENT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
×
100
        return result.getResultCode() == CacheResultCode.SUCCESS;
×
101
    }
102

103
    /**
104
     * Removes the mapping for a key from this cache if it is present.
105
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
106
     * @param key key whose mapping is to be removed from the cache
107
     * @return true if the key is removed successfully, false if the KV association does not exists in the cache,
108
     *         or error occurs during cache access.
109
     * @see #REMOVE(Object)
110
     */
111
    default boolean remove(K key) {
112
        return REMOVE(key).isSuccess();
×
113
    }
114

115
    /**
116
     * Removes entries for the specified keys.
117
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
118
     * <p>if the implementation supports asynchronous operation, the cache operation of this method is asynchronous.</p>
119
     * @param keys the keys to remove
120
     * @see #REMOVE_ALL(Set)
121
     */
122
    default void removeAll(Set<? extends K> keys) {
123
        REMOVE_ALL(keys);
×
124
    }
×
125

126
    /**
127
     * Provides a standard way to access the underlying concrete cache entry
128
     * implementation in order to provide access to further, proprietary features.
129
     * <p>
130
     * If the implementation does not support the specified class,
131
     * the {@link IllegalArgumentException} is thrown.
132
     *
133
     * @param clazz the proprietary class or interface of the underlying
134
     *              concrete cache. It is this type that is returned.
135
     * @return an instance of the underlying concrete cache
136
     * @throws IllegalArgumentException if the caching provider doesn't support
137
     *                                  the specified class.
138
     */
139
    <T> T unwrap(Class<T> clazz);
140

141
    /**
142
     * Clean resources created by this cache.
143
     */
144
    @Override
145
    default void close() {
146
    }
×
147

148
    //--------------------------JetCache API---------------------------------------------
149

150
    /**
151
     * Get the config of this cache.
152
     * @return the cache config
153
     */
154
    CacheConfig<K, V> config();
155

156
    /**
157
     * Use this cache attempt to acquire a exclusive lock specified by the key, this method will not block.
158
     * examples:
159
     * <pre>
160
     *   try(AutoReleaseLock lock = cache.tryLock("MyKey",100, TimeUnit.SECONDS)){
161
     *      if(lock != null){
162
     *          // do something
163
     *      }
164
     *   }
165
     * </pre>
166
     * <p>{@link MultiLevelCache} will use the last level cache to support this operation.</p>
167
     * @param key      lockKey
168
     * @param expire   lock expire time
169
     * @param timeUnit lock expire time unit
170
     * @return an AutoReleaseLock(implements java.lang.AutoCloseable) instance if success.
171
     *         or null if the attempt fails, which indicates there is an another thread/process/server has the lock,
172
     *         or error occurs during cache access.
173
     * @see #tryLockAndRun(Object, long, TimeUnit, Runnable)
174
     */
175
    @SuppressWarnings("unchecked")
176
    default AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) {
177
        if (key == null) {
×
178
            return null;
×
179
        }
180
        final String uuid = UUID.randomUUID().toString();
×
181
        final long expireTimestamp = System.currentTimeMillis() + timeUnit.toMillis(expire);
×
182
        final CacheConfig config = config();
×
183

184

185
        AutoReleaseLock lock = () -> {
×
186
            int unlockCount = 0;
×
187
            while (unlockCount++ < config.getTryLockUnlockCount()) {
×
188
                if(System.currentTimeMillis() < expireTimestamp) {
×
189
                    CacheResult unlockResult = REMOVE(key);
×
190
                    if (unlockResult.getResultCode() == CacheResultCode.FAIL
×
191
                            || unlockResult.getResultCode() == CacheResultCode.PART_SUCCESS) {
×
192
                        logger.info("[tryLock] [{} of {}] [{}] unlock failed. Key={}, msg = {}",
×
193
                                unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getMessage());
×
194
                        // retry
195
                    } else if (unlockResult.isSuccess()) {
×
196
                        logger.debug("[tryLock] [{} of {}] [{}] successfully release the lock. Key={}",
×
197
                                unlockCount, config.getTryLockUnlockCount(), uuid, key);
×
198
                        return;
×
199
                    } else {
200
                        logger.warn("[tryLock] [{} of {}] [{}] unexpected unlock result: Key={}, result={}",
×
201
                                unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getResultCode());
×
202
                        return;
×
203
                    }
204
                } else {
×
205
                    logger.info("[tryLock] [{} of {}] [{}] lock already expired: Key={}",
×
206
                            unlockCount, config.getTryLockUnlockCount(), uuid, key);
×
207
                    return;
×
208
                }
209
            }
210
        };
×
211

212
        int lockCount = 0;
×
213
        Cache cache = this;
×
214
        while (lockCount++ < config.getTryLockLockCount()) {
×
215
            CacheResult lockResult = cache.PUT_IF_ABSENT(key, uuid, expire, timeUnit);
×
216
            if (lockResult.isSuccess()) {
×
217
                logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock. Key={}",
×
218
                        lockCount, config.getTryLockLockCount(), uuid, key);
×
219
                return lock;
×
220
            } else if (lockResult.getResultCode() == CacheResultCode.FAIL || lockResult.getResultCode() == CacheResultCode.PART_SUCCESS) {
×
221
                logger.info("[tryLock] [{} of {}] [{}] cache access failed during get lock, will inquiry {} times. Key={}, msg={}",
×
222
                        lockCount, config.getTryLockLockCount(), uuid,
×
223
                        config.getTryLockInquiryCount(), key, lockResult.getMessage());
×
224
                int inquiryCount = 0;
×
225
                while (inquiryCount++ < config.getTryLockInquiryCount()) {
×
226
                    CacheGetResult inquiryResult = cache.GET(key);
×
227
                    if (inquiryResult.isSuccess()) {
×
228
                        if (uuid.equals(inquiryResult.getValue())) {
×
229
                            logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock after inquiry. Key={}",
×
230
                                    inquiryCount, config.getTryLockInquiryCount(), uuid, key);
×
231
                            return lock;
×
232
                        } else {
233
                            logger.debug("[tryLock] [{} of {}] [{}] not the owner of the lock, return null. Key={}",
×
234
                                    inquiryCount, config.getTryLockInquiryCount(), uuid, key);
×
235
                            return null;
×
236
                        }
237
                    } else {
238
                        logger.info("[tryLock] [{} of {}] [{}] inquiry failed. Key={}, msg={}",
×
239
                                inquiryCount, config.getTryLockInquiryCount(), uuid, key, inquiryResult.getMessage());
×
240
                        // retry inquiry
241
                    }
242
                }
×
243
            } else {
×
244
                // others holds the lock
245
                logger.debug("[tryLock] [{} of {}] [{}] others holds the lock, return null. Key={}",
×
246
                        lockCount, config.getTryLockLockCount(), uuid, key);
×
247
                return null;
×
248
            }
249
        }
×
250

251
        logger.debug("[tryLock] [{}] return null after {} attempts. Key={}", uuid, config.getTryLockLockCount(), key);
×
252
        return null;
×
253
    }
254

255
    /**
256
     * Use this cache to try run an action exclusively.
257
     * <p>{@link MultiLevelCache} will use the last level cache to support this operation.</p>
258
     * examples:
259
     * <pre>
260
     * cache.tryLock("MyKey",100, TimeUnit.SECONDS),() -&gt; {
261
     *     //do something
262
     * });
263
     * </pre>
264
     * @param key lockKey
265
     * @param expire lock expire time
266
     * @param timeUnit lock expire time unit
267
     * @param action the action need to execute
268
     * @return true if successfully get the lock and the action is executed
269
     */
270
    default boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action){
271
        try (AutoReleaseLock lock = tryLock(key, expire, timeUnit)) {
×
272
            if (lock != null) {
×
273
                action.run();
×
274
                return true;
×
275
            } else {
276
                return false;
×
277
            }
278
        }
×
279
    }
280

281
    /**
282
     * Gets an entry from the cache.
283
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
284
     * return. The invoke of getResultCode()/isSuccess()/getMessage()/getValue() on the result will block until cache
285
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
286
     * programming.</p>
287
     * @param key the key
288
     * @return the result
289
     */
290
    CacheGetResult<V> GET(K key);
291

292
    /**
293
     * Gets a collection of entries from the Cache.
294
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
295
     * return. The invoke of getResultCode()/isSuccess()/getMessage()/getValue() on the result will block until cache
296
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
297
     * programming.</p>
298
     * @param keys the key collection
299
     * @return the result
300
     */
301
    MultiGetResult<K, V> GET_ALL(Set<? extends K> keys);
302

303
    /**
304
     * If there is a value associated with the key, return the value;
305
     * otherwise use the loader load the value and return, and then update the cache.
306
     * @param key the key
307
     * @param loader the value loader
308
     * @return the value
309
     * @see CacheConfig#isCacheNullValue()
310
     */
311
    default V computeIfAbsent(K key, Function<K, V> loader) {
312
        return computeIfAbsent(key, loader, config().isCacheNullValue());
×
313
    }
314

315
    /**
316
     * If there is a value associated with the key, return the value;
317
     * otherwise use the loader load the value and return, and then update the cache.
318
     * @param key the key
319
     * @param loader the value loader
320
     * @param cacheNullWhenLoaderReturnNull true if null value returned by loader should put into cache use the key
321
     * @return the value
322
     */
323
    V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull);
324

325
    /**
326
     * If there is a value associated with the key, return the value;
327
     * otherwise use the loader load the value and return, and then update the cache.
328
     * @param key the key
329
     * @param loader the value loader
330
     * @param cacheNullWhenLoaderReturnNull true if null value returned by loader should put into cache use the key
331
     * @param expireAfterWrite the TTL(time to live) of the KV association
332
     * @param timeUnit the time unit of expireAfterWrite
333
     * @return the value
334
     */
335
    V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expireAfterWrite, TimeUnit timeUnit);
336

337
    /**
338
     * Associates the specified value with the specified key in the cache.
339
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
340
     * @param key key with which the specified value is to be associated
341
     * @param value value to be associated with the specified key
342
     * @param expireAfterWrite the TTL(time to live) of the KV association
343
     * @param timeUnit the time unit of expireAfterWrite
344
     * @see #PUT(Object, Object, long, TimeUnit)
345
     */
346
    default void put(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
347
        PUT(key, value, expireAfterWrite, timeUnit);
×
348
    }
×
349

350
    /**
351
     * Associates the specified value with the specified key in the cache.
352
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
353
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
354
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
355
     * programming.</p>
356
     * @param key key with which the specified value is to be associated
357
     * @param value value to be associated with the specified key
358
     * @return the result
359
     */
360
    default CacheResult PUT(K key, V value) {
361
        if (key == null) {
×
362
            return CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
363
        }
364
        return PUT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
×
365
    }
366

367
    /**
368
     * Associates the specified value with the specified key in the cache.
369
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
370
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
371
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
372
     * programming.</p>
373
     * @param key key with which the specified value is to be associated
374
     * @param value value to be associated with the specified key
375
     * @param expireAfterWrite the TTL(time to live) of the KV association
376
     * @param timeUnit the time unit of expireAfterWrite
377
     * @return the result
378
     */
379
    CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
380

381
    /**
382
     * Copies all of the entries from the specified map to the cache.
383
     * <p>If error occurs during cache access, the method will not throw an exception.</p>
384
     * @param map mappings to be stored in this cache.
385
     * @param expireAfterWrite the TTL(time to live) of the KV association
386
     * @param timeUnit the time unit of expireAfterWrite
387
     * @see #PUT_ALL(Map, long, TimeUnit)
388
     */
389
    default void putAll(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) {
390
        PUT_ALL(map, expireAfterWrite, timeUnit);
×
391
    }
×
392

393
    /**
394
     * Copies all of the entries from the specified map to the cache.
395
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
396
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
397
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
398
     * programming.</p>
399
     * @param map mappings to be stored in this cache.
400
     * @return the result
401
     */
402
    default CacheResult PUT_ALL(Map<? extends K, ? extends V> map) {
403
        if (map == null) {
×
404
            return CacheResult.FAIL_ILLEGAL_ARGUMENT;
×
405
        }
406
        return PUT_ALL(map, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
×
407
    }
408

409
    /**
410
     * Copies all of the entries from the specified map to the cache.
411
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
412
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
413
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
414
     * programming.</p>
415
     * @param map mappings to be stored in this cache.
416
     * @param expireAfterWrite the TTL(time to live) of the KV association
417
     * @param timeUnit the time unit of expireAfterWrite
418
     * @return the result
419
     */
420
    CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
421

422
    /**
423
     * Removes the mapping for a key from this cache if it is present.
424
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
425
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
426
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
427
     * programming.</p>
428
     * @param key key whose mapping is to be removed from the cache
429
     * @return the result
430
     */
431
    CacheResult REMOVE(K key);
432

433
    /**
434
     * Removes entries for the specified keys.
435
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
436
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
437
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
438
     * programming.</p>
439
     * @param keys the keys to remove
440
     * @return the result
441
     */
442
    CacheResult REMOVE_ALL(Set<? extends K> keys);
443

444
    /**
445
     * If the specified key is not already associated with a value, associate it with the given value.
446
     * <p>if the implementation supports asynchronous operation, the cache access may not completed after this method
447
     * return. The invoke of getResultCode()/isSuccess()/getMessage() on the result will block until cache
448
     * operation is completed. Call future() method on the result will get a CompletionStage instance for asynchronous
449
     * programming.</p>
450
     * @param key key with which the specified value is to be associated
451
     * @param value value to be associated with the specified key
452
     * @param expireAfterWrite the TTL(time to live) of the KV association
453
     * @param timeUnit the time unit of expireAfterWrite
454
     * @return SUCCESS if the specified key is not already associated with a value,
455
     * or EXISTS if the specified key is already associated with a value,
456
     * or FAIL if error occurs.
457
     */
458
    CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
459

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