View Javadoc

1   package com.atlassian.vcache.internal.legacy;
2   
3   import com.atlassian.cache.Cache;
4   import com.atlassian.vcache.JvmCache;
5   import com.atlassian.vcache.internal.core.service.LocalCacheUtils;
6   import com.atlassian.vcache.internal.core.service.VCacheLock;
7   
8   import java.time.Duration;
9   import java.util.Arrays;
10  import java.util.HashSet;
11  import java.util.Map;
12  import java.util.Optional;
13  import java.util.Set;
14  import java.util.function.Function;
15  import java.util.function.Supplier;
16  
17  import static java.util.Objects.requireNonNull;
18  
19  /**
20   * Implementation of {@link JvmCache} that backs onto a legacy {@link Cache}.
21   *
22   * @param <K> the key type
23   * @param <V> the value type
24   * @since 1.0.0
25   */
26  class LegacyJvmCache<K, V> implements JvmCache<K, V> {
27      private final Cache<K, V> delegate;
28      private final VCacheLock globalLock;
29  
30      LegacyJvmCache(Cache<K, V> delegate, Duration lockTimeout) {
31          this.delegate = requireNonNull(delegate);
32          this.globalLock = new VCacheLock(delegate.getName(), lockTimeout);
33      }
34  
35      @Override
36      public Set<K> getKeys() {
37          return globalLock.withLock(() -> new HashSet<>(delegate.getKeys()));
38      }
39  
40      @Override
41      public Optional<V> get(K key) {
42          return globalLock.withLock(() -> Optional.ofNullable(delegate.get(key)));
43      }
44  
45      @Override
46      public V get(K key, Supplier<? extends V> supplier) {
47          // Look for the existing value
48          final Optional<V> current = get(requireNonNull(key));
49  
50          return current.orElseGet(() -> {
51              // Calculate the missing value
52              final V candidateValue = requireNonNull(supplier.get());
53  
54              // Now return either candidate value because one does not exist, otherwise the value that beat us to be inserted
55              return globalLock.withLock(() -> delegate.get(key, () -> candidateValue));
56          });
57      }
58  
59      @SafeVarargs
60      @Override
61      public final Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, K... keys) {
62          // This method needs to be final to prevent subclasses bypassing the locking enforced by this class.
63          return getBulk(factory, Arrays.asList(keys));
64      }
65  
66      @Override
67      public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
68          return globalLock.withLock(() -> LocalCacheUtils.getBulk(
69                  factory,
70                  keys,
71                  this::get,
72                  args -> putIfAbsent(args.key, args.value),
73                  globalLock));
74      }
75  
76      @Override
77      public void put(K key, V value) {
78          globalLock.withLock(() -> delegate.put(key, value));
79      }
80  
81      @Override
82      public Optional<V> putIfAbsent(K key, V value) {
83          return globalLock.withLock(() -> Optional.ofNullable(delegate.putIfAbsent(key, value)));
84      }
85  
86      @Override
87      public boolean replaceIf(K key, V currentValue, V newValue) {
88          return globalLock.withLock(() -> delegate.replace(key, currentValue, newValue));
89      }
90  
91      @Override
92      public boolean removeIf(K key, V value) {
93          return globalLock.withLock(() -> delegate.remove(key, value));
94      }
95  
96      @Override
97      public void remove(K key) {
98          globalLock.withLock(() -> delegate.remove(key));
99      }
100 
101     @Override
102     public void removeAll() {
103         globalLock.withLock(delegate::removeAll);
104     }
105 
106     @Override
107     public String getName() {
108         return delegate.getName();
109     }
110 }