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 com.atlassian.vcache.internal.core.RecursionDetector;
6   
7   import java.util.Arrays;
8   import java.util.HashMap;
9   import java.util.Map;
10  import java.util.Optional;
11  import java.util.Set;
12  import java.util.concurrent.locks.ReentrantLock;
13  import java.util.function.Function;
14  import java.util.function.Supplier;
15  
16  import static com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
17  import static java.util.Objects.requireNonNull;
18  
19  /**
20   * Implementation of {@link RequestCache} that uses a delegate {@link HashMap}
21   *
22   * @param <K> the key type
23   * @param <V> the value type
24   * @since 1.0
25   */
26  class DefaultRequestCache<K, V> implements RequestCache<K, V> {
27      private final String name;
28      private final Supplier<RequestContext> contextSupplier;
29      private final RecursionDetector<K> recursionDetector = new RecursionDetector<>();
30  
31      DefaultRequestCache(String name, Supplier<RequestContext> contextSupplier) {
32          this.name = requireValidCacheName(name);
33          this.contextSupplier = requireNonNull(contextSupplier);
34      }
35  
36      @Override
37      public Optional<V> get(K key) {
38          return withLock(kvMap -> Optional.ofNullable(kvMap.get(key)));
39      }
40  
41      @Override
42      public V get(K key, Supplier<? extends V> supplier) {
43          try (RecursionDetector.Guard<K> ignored = recursionDetector.guardOn(key)) {
44              return withLock(kvMap -> kvMap.computeIfAbsent(requireNonNull(key), k -> requireNonNull(supplier.get())));
45          }
46      }
47  
48      @SafeVarargs
49      @Override
50      public final Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, K... keys) {
51          // This method needs to be final to prevent subclasses bypassing the locking enforced by this class.
52          return getBulk(factory, Arrays.asList(keys));
53      }
54  
55      @Override
56      public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
57          return withLock(kvMap -> LocalCacheUtils.getBulk(
58                  factory,
59                  keys,
60                  this::get,
61                  args -> put(args.key, args.value),
62                  recursionDetector));
63      }
64  
65      @Override
66      public void put(K key, V value) {
67          withLock(kvMap -> kvMap.put(requireNonNull(key), requireNonNull(value)));
68      }
69  
70      @Override
71      public Optional<V> putIfAbsent(K key, V value) {
72          return withLock(kvMap -> Optional.ofNullable(
73                  kvMap.putIfAbsent(requireNonNull(key), requireNonNull(value))));
74      }
75  
76      @Override
77      public boolean replaceIf(K key, V currentValue, V newValue) {
78          return withLock(kvMap ->
79                  kvMap.replace(requireNonNull(key), requireNonNull(currentValue), requireNonNull(newValue)));
80      }
81  
82      @Override
83      public boolean removeIf(K key, V value) {
84          return withLock(kvMap -> kvMap.remove(requireNonNull(key), requireNonNull(value)));
85      }
86  
87      @Override
88      public void remove(K key) {
89          withLock(kvMap -> kvMap.remove(key));
90      }
91  
92      @Override
93      public void removeAll() {
94          withLock(kvMap -> {
95                  kvMap.clear();
96                  // Dodgy 'return false' to simulate returning a value.
97                  return false;
98              });
99      }
100 
101     @Override
102     public String getName() {
103         return name;
104     }
105 
106     private MapAndLock<K, V> ensureDelegate() {
107         final RequestContext requestContext = contextSupplier.get();
108         return requestContext.computeIfAbsent(this, MapAndLock::new);
109     }
110 
111     private <R> R withLock(Function<Map<K, V>, R> fn) {
112         final MapAndLock<K, V> mal = ensureDelegate();
113         mal.globalLock.lock();
114         try {
115             return fn.apply(mal.map);
116         } finally {
117             mal.globalLock.unlock();
118         }
119     }
120 
121     private static class MapAndLock<K, V> {
122         final Map<K, V> map = new HashMap<>();
123         final ReentrantLock globalLock = new ReentrantLock();
124     }
125 }