1 package com.atlassian.cache.vcache;
2
3 import java.io.Serializable;
4 import java.util.Collection;
5 import java.util.Optional;
6 import java.util.concurrent.CompletionException;
7 import javax.annotation.Nonnull;
8 import javax.annotation.Nullable;
9
10 import com.atlassian.cache.Cache;
11 import com.atlassian.cache.CacheEntryListener;
12 import com.atlassian.cache.CacheException;
13 import com.atlassian.cache.CacheLoader;
14 import com.atlassian.cache.CacheSettings;
15 import com.atlassian.cache.Supplier;
16 import com.atlassian.vcache.DirectExternalCache;
17 import com.atlassian.vcache.IdentifiedValue;
18 import com.atlassian.vcache.PutPolicy;
19
20 import static com.atlassian.cache.vcache.Utils.asSerializable;
21 import static com.atlassian.cache.vcache.Utils.asString;
22 import static com.atlassian.vcache.VCacheUtils.fold;
23 import static com.atlassian.vcache.VCacheUtils.join;
24 import static java.util.Objects.requireNonNull;
25
26 class ExternalCacheDelegate<K, V>
27 extends ManagedCacheSupport
28 implements Cache<K, V>
29 {
30 private final DirectExternalCache<Serializable> delegate;
31 private final CacheLoader<String, Serializable> cacheLoader;
32
33 public ExternalCacheDelegate(
34 String name,
35 DirectExternalCache<Serializable> delegate,
36 @Nullable CacheLoader<String, Serializable> cacheLoader,
37 CacheSettings settings)
38 {
39 super(name, settings);
40 this.delegate = requireNonNull(delegate);
41 this.cacheLoader = cacheLoader;
42 }
43
44 @Override
45 public void clear()
46 {
47 join(delegate.removeAll());
48 }
49
50 @Override
51 public boolean isLocal()
52 {
53 return false;
54 }
55
56 @Override
57 public boolean containsKey(@Nonnull K key)
58 {
59 return join(delegate.get(asString(key))).isPresent();
60 }
61
62 @Nonnull
63 @Override
64 public Collection<K> getKeys()
65 {
66 throw new UnsupportedOperationException("Not supported on non-local caches");
67 }
68
69 @Nullable
70 @Override
71 public V get(@Nonnull K key)
72 {
73 final String strKey = asString(key);
74 try
75 {
76 if (cacheLoader == null)
77 {
78 return (V) join(delegate.get(strKey)).orElse(null);
79 }
80 else
81 {
82 return (V) join(delegate.get(strKey, () -> asSerializable(cacheLoader.load(strKey))));
83 }
84 }
85 catch (CompletionException ce)
86 {
87 throw new CacheException(ce);
88 }
89 }
90
91 @Nonnull
92 @Override
93 public V get(@Nonnull K key, @Nonnull Supplier<? extends V> valueSupplier)
94 {
95 return (V) join(delegate.get(asString(key), () -> asSerializable(valueSupplier.get())));
96 }
97
98 @Override
99 public void put(@Nonnull K key, @Nonnull V value)
100 {
101 fold(delegate.put(asString(key), asSerializable(value), PutPolicy.PUT_ALWAYS),
102 success -> success,
103 failure -> {
104 throw new CacheException("put() operation failed", failure);
105 });
106 }
107
108 @Nullable
109 @Override
110 public V putIfAbsent(@Nonnull K key, @Nonnull V value)
111 {
112 for (; ; )
113 {
114 final Optional<Serializable> current = join(delegate.get(asString(key)));
115 if (current.isPresent())
116 {
117 return (V) current.get();
118 }
119 else
120 {
121 final boolean added = join(delegate.put(asString(key), asSerializable(value), PutPolicy.ADD_ONLY));
122 if (added)
123 {
124 return null;
125 }
126 }
127 }
128 }
129
130 @Override
131 public void remove(@Nonnull K key)
132 {
133 join(delegate.remove(asString(key)));
134 }
135
136 @Override
137 public boolean remove(@Nonnull K key, @Nonnull V value)
138 {
139 for (; ; )
140 {
141 final Optional<IdentifiedValue<Serializable>> current = join(delegate.getIdentified(asString(key)));
142 if (current.isPresent() && current.get().value().equals(value))
143 {
144 final boolean removed = join(delegate.removeIf(asString(key), current.get().identifier()));
145 if (removed)
146 {
147 return true;
148 }
149
150 }
151 else
152 {
153 return false;
154 }
155 }
156 }
157
158 @Override
159 public void removeAll()
160 {
161 join(delegate.removeAll());
162 }
163
164 @Override
165 public boolean replace(@Nonnull K key, @Nonnull V oldValue, @Nonnull V newValue)
166 {
167 for (; ; )
168 {
169 final Optional<IdentifiedValue<Serializable>> current = join(delegate.getIdentified(asString(key)));
170 if (current.isPresent() && current.get().value().equals(oldValue))
171 {
172 final boolean replaced = join(delegate.replaceIf(
173 asString(key), current.get().identifier(), asSerializable(newValue)));
174 if (replaced)
175 {
176 return true;
177 }
178
179 }
180 else
181 {
182 return false;
183 }
184 }
185 }
186
187 @Override
188 public void addListener(@Nonnull CacheEntryListener<K, V> listener, boolean includeValues)
189 {
190 throw new UnsupportedOperationException("Not supported on non-local caches");
191 }
192
193 @Override
194 public void removeListener(@Nonnull CacheEntryListener<K, V> listener)
195 {
196 throw new UnsupportedOperationException("Not supported on non-local caches");
197 }
198 }