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
21
22
23
24
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
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
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 }