View Javadoc

1   package com.atlassian.vcache.internal.core.service;
2   
3   import com.atlassian.vcache.RequestCache;
4   import com.atlassian.vcache.internal.RequestContext;
5   import org.slf4j.Logger;
6   import org.slf4j.LoggerFactory;
7   
8   import java.time.Duration;
9   import java.util.Arrays;
10  import java.util.HashMap;
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 com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
18  import static java.util.Objects.requireNonNull;
19  
20  /**
21   * Implementation of {@link RequestCache} that uses a delegate {@link HashMap}
22   *
23   * @param <K> the key type
24   * @param <V> the value type
25   * @since 1.0
26   */
27  class DefaultRequestCache<K, V> implements RequestCache<K, V> {
28      public static final Logger log = LoggerFactory.getLogger(DefaultRequestCache.class);
29  
30      private final String name;
31      private final Supplier<RequestContext> contextSupplier;
32      private final Duration lockTimeout;
33  
34      DefaultRequestCache(String name, Supplier<RequestContext> contextSupplier, Duration lockTimeout) {
35          this.name = requireValidCacheName(name);
36          this.contextSupplier = requireNonNull(contextSupplier);
37          this.lockTimeout = requireNonNull(lockTimeout);
38      }
39  
40      @Override
41      public Optional<V> get(K key) {
42          return withLock(kvMap -> Optional.ofNullable(kvMap.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(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 withLock(kvMap -> {
56                  final V existing = kvMap.putIfAbsent(requireNonNull(key), candidateValue);
57                  return (existing == null) ? candidateValue : existing;
58              });
59          });
60      }
61  
62      @SafeVarargs
63      @Override
64      public final Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, K... keys) {
65          // This method needs to be final to prevent subclasses bypassing the locking enforced by this class.
66          return getBulk(factory, Arrays.asList(keys));
67      }
68  
69      @Override
70      public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
71          return withLock(kvMap -> LocalCacheUtils.getBulk(
72                  factory,
73                  keys,
74                  this::get,
75                  args -> putIfAbsent(args.key, args.value),
76                  ensureDelegate().lock));
77      }
78  
79      @Override
80      public void put(K key, V value) {
81          withLock(kvMap -> kvMap.put(requireNonNull(key), requireNonNull(value)));
82      }
83  
84      @Override
85      public Optional<V> putIfAbsent(K key, V value) {
86          return withLock(kvMap -> Optional.ofNullable(
87                  kvMap.putIfAbsent(requireNonNull(key), requireNonNull(value))));
88      }
89  
90      @Override
91      public boolean replaceIf(K key, V currentValue, V newValue) {
92          return withLock(kvMap ->
93                  kvMap.replace(requireNonNull(key), requireNonNull(currentValue), requireNonNull(newValue)));
94      }
95  
96      @Override
97      public boolean removeIf(K key, V value) {
98          return withLock(kvMap -> kvMap.remove(requireNonNull(key), requireNonNull(value)));
99      }
100 
101     @Override
102     public void remove(K key) {
103         withLock(kvMap -> kvMap.remove(key));
104     }
105 
106     @Override
107     public void removeAll() {
108         withLock(kvMap -> {
109                 kvMap.clear();
110                 // Dodgy 'return false' to simulate returning a value.
111                 return false;
112             });
113     }
114 
115     @Override
116     public String getName() {
117         return name;
118     }
119 
120     private MapAndLock ensureDelegate() {
121         final RequestContext requestContext = contextSupplier.get();
122         return requestContext.computeIfAbsent(this, MapAndLock::new);
123     }
124 
125     private <R> R withLock(Function<Map<K, V>, R> fn) {
126         final MapAndLock mal = ensureDelegate();
127 
128         return mal.lock.withLock(() -> fn.apply(mal.map));
129     }
130 
131     private class MapAndLock {
132         final Map<K, V> map = new HashMap<>();
133         final VCacheLock lock = new VCacheLock(name, lockTimeout);
134     }
135 }