1 package com.atlassian.cache.memory;
2
3 import java.util.Collection;
4 import java.util.SortedMap;
5 import java.util.concurrent.Callable;
6 import java.util.concurrent.ExecutionException;
7
8 import javax.annotation.Nonnull;
9 import javax.annotation.Nullable;
10
11 import com.atlassian.cache.Cache;
12 import com.atlassian.cache.CacheEntryListener;
13 import com.atlassian.cache.CacheException;
14 import com.atlassian.cache.CacheSettings;
15 import com.atlassian.cache.CacheStatisticsKey;
16 import com.atlassian.util.concurrent.Supplier;
17
18 import com.atlassian.cache.impl.CacheEntryListenerSupport;
19 import com.atlassian.cache.impl.DefaultCacheEntryListenerSupport;
20 import com.google.common.base.Throwables;
21 import com.google.common.cache.LoadingCache;
22 import com.google.common.cache.RemovalListener;
23 import com.google.common.cache.RemovalNotification;
24 import com.google.common.util.concurrent.UncheckedExecutionException;
25
26 import static com.atlassian.cache.memory.DelegatingCacheStatistics.toStatistics;
27
28
29
30
31
32
33 class DelegatingCache<K, V> extends ManagedCacheSupport implements Cache<K, V>
34 {
35 private final com.google.common.cache.Cache<K, V> internalCache;
36 private final CacheEntryListenerSupport<K, V> listenerSupport;
37
38 private DelegatingCache(final com.google.common.cache.Cache<K, V> internalCache, String name, CacheSettings settings)
39 {
40 super(name, settings);
41 this.internalCache = internalCache;
42 this.listenerSupport = new DefaultCacheEntryListenerSupport<K, V>();
43 }
44
45 static <K, V> DelegatingCache<K, V> create(final LoadingCache<K, V> internalCache, String name, CacheSettings settings)
46 {
47 return new DelegatingLoadingCache<K, V>(internalCache, name, settings);
48 }
49
50 static <K, V> DelegatingCache<K, V> create(final com.google.common.cache.Cache<K, V> internalCache, String name, CacheSettings settings)
51 {
52 return new DelegatingCache<K, V>(internalCache, name, settings);
53 }
54
55 @Override
56 public boolean containsKey(@Nonnull K key)
57 {
58 return null != internalCache.getIfPresent(key);
59 }
60
61 @Nonnull
62 @Override
63 public Collection<K> getKeys()
64 {
65 try
66 {
67 return internalCache.asMap().keySet();
68 }
69 catch (Exception e)
70 {
71 throw new CacheException(e);
72 }
73 }
74
75 @Override
76 public void put(@Nonnull final K key, @Nonnull final V value)
77 {
78 try
79 {
80 V oldValue = internalCache.asMap().put(key, value);
81 if (oldValue == null)
82 {
83
84
85 listenerSupport.notifyAdd(key, value);
86 }
87 }
88 catch (Exception e)
89 {
90 throw new CacheException(e);
91 }
92 }
93
94 @Override
95 public V get(@Nonnull final K key)
96 {
97 return internalCache.getIfPresent(key);
98 }
99
100 @Nonnull
101 @Override
102 public V get(@Nonnull final K key, @Nonnull final com.atlassian.cache.Supplier<? extends V> valueLoader)
103 {
104 try
105 {
106 return internalCache.get(key, new Callable<V>()
107 {
108 @Override
109 public V call()
110 {
111 return valueLoader.get();
112 }
113 });
114 }
115 catch (UncheckedExecutionException e)
116 {
117 Throwable cause = e.getCause();
118 Throwables.propagateIfInstanceOf(cause, CacheException.class);
119 throw new CacheException(cause);
120 }
121 catch (ExecutionException e)
122 {
123 Throwable cause = e.getCause();
124 Throwables.propagateIfInstanceOf(cause, CacheException.class);
125 throw new CacheException(cause);
126 }
127 catch (Exception e)
128 {
129 Throwables.propagateIfInstanceOf(e, CacheException.class);
130 throw new CacheException(e);
131 }
132 }
133
134 @Override
135 public void remove(@Nonnull final K key)
136 {
137 try
138 {
139 try
140 {
141 internalCache.getUnchecked(key);
142 }
143 catch (UncheckedExecutionException e)
144 {
145
146 }
147 finally
148 {
149 internalCache.invalidate(key);
150 }
151 }
152 catch (Exception e)
153 {
154 throw new CacheException(e);
155 }
156 }
157
158 @Override
159 public void removeAll()
160 {
161 try
162 {
163 internalCache.invalidateAll();
164 }
165 catch (Exception e)
166 {
167 throw new CacheException(e);
168 }
169 }
170
171 @Override
172 public V putIfAbsent(@Nonnull K key, @Nonnull V value)
173 {
174 try
175 {
176 V oldValue = internalCache.asMap().putIfAbsent(key, value);
177 if (oldValue == null)
178 {
179
180
181 listenerSupport.notifyAdd(key, value);
182 }
183 return oldValue;
184 }
185 catch (Exception e)
186 {
187 throw new CacheException(e);
188 }
189 }
190
191 @Override
192 public boolean remove(@Nonnull K key, @Nonnull V value)
193 {
194 try
195 {
196 boolean wasRemoved;
197 try
198 {
199 internalCache.getUnchecked(key);
200 }
201 catch (UncheckedExecutionException e)
202 {
203
204 }
205 finally
206 {
207 wasRemoved = internalCache.asMap().remove(key, value);
208 }
209 return wasRemoved;
210 }
211 catch (Exception e)
212 {
213 throw new CacheException(e);
214 }
215 }
216
217 @Override
218 public boolean replace(@Nonnull K key, @Nonnull V oldValue, @Nonnull V newValue)
219 {
220 try
221 {
222 return internalCache.asMap().replace(key, oldValue, newValue);
223 }
224 catch (Exception e)
225 {
226 throw new CacheException(e);
227 }
228 }
229
230 @Nonnull
231 @Override
232 public SortedMap<CacheStatisticsKey,Supplier<Long>> getStatistics()
233 {
234 return toStatistics(internalCache);
235 }
236
237 @Override
238 public void clear()
239 {
240 removeAll();
241 }
242
243 @Override
244 public boolean equals(@Nullable final Object other)
245 {
246 if (other instanceof DelegatingCache)
247 {
248 DelegatingCache<?,?> otherDelegatingCache = (DelegatingCache<?,?>) other;
249 if (internalCache.equals(otherDelegatingCache.internalCache))
250 {
251 return true;
252 }
253 }
254 return false;
255 }
256
257 @Override
258 public int hashCode()
259 {
260 return 3 + internalCache.hashCode();
261 }
262
263 @Override
264 public void addListener(@Nonnull CacheEntryListener<K, V> listener, boolean includeValues)
265 {
266 listenerSupport.add(listener, includeValues);
267 }
268
269 @Override
270 public void removeListener(@Nonnull CacheEntryListener<K, V> listener)
271 {
272 listenerSupport.remove(listener);
273 }
274
275 protected static class DelegatingRemovalListener<K, V> implements RemovalListener<K, V>
276 {
277 private DelegatingCache<K, V> cache;
278
279 protected void onSupply(K key, V value)
280 {
281 cache.listenerSupport.notifyAdd(key, value);
282 }
283
284 @Override
285 public void onRemoval(RemovalNotification<K, V> notification)
286 {
287 switch (notification.getCause())
288 {
289 case COLLECTED:
290 case EXPIRED:
291 cache.listenerSupport.notifyEvict(notification.getKey(), notification.getValue());
292 break;
293 case EXPLICIT:
294 cache.listenerSupport.notifyRemove(notification.getKey(), notification.getValue());
295 break;
296 case REPLACED:
297 K key = notification.getKey();
298 cache.listenerSupport.notifyUpdate(key, cache.internalCache.getIfPresent(key), notification.getValue());
299 break;
300 }
301 }
302
303 public void setCache(DelegatingCache<K, V> cache)
304 {
305 this.cache = cache;
306 }
307 }
308
309 private static class DelegatingLoadingCache<K, V> extends DelegatingCache<K, V>
310 {
311 private final LoadingCache<K, V> internalCache;
312
313 private DelegatingLoadingCache(final com.google.common.cache.LoadingCache<K, V> internalCache, final String name, final CacheSettings settings)
314 {
315 super(internalCache, name, settings);
316 this.internalCache = internalCache;
317 }
318
319 @Override
320 public V get(@Nonnull final K key)
321 {
322 try
323 {
324 return internalCache.get(key);
325 }
326 catch (UncheckedExecutionException e)
327 {
328 Throwable cause = e.getCause();
329 Throwables.propagateIfInstanceOf(cause, CacheException.class);
330 throw new CacheException(cause);
331 }
332 catch (ExecutionException e)
333 {
334 Throwable cause = e.getCause();
335 Throwables.propagateIfInstanceOf(cause, CacheException.class);
336 throw new CacheException(cause);
337 }
338 catch (Exception e)
339 {
340 Throwables.propagateIfInstanceOf(e, CacheException.class);
341 throw new CacheException(e);
342 }
343 }
344 }
345 }