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
22
23
24
25
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
48 final Optional<V> current = get(key);
49
50 return current.orElseGet(() -> {
51
52 final V candidateValue = requireNonNull(supplier.get());
53
54
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
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
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 }