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