1 package com.atlassian.cache.ehcache;
2
3 import com.atlassian.cache.Cache;
4 import com.atlassian.cache.CacheEntryListener;
5 import com.atlassian.cache.CacheException;
6 import com.atlassian.cache.CacheSettings;
7 import com.atlassian.cache.CacheStatisticsKey;
8 import com.atlassian.cache.ehcache.wrapper.ValueProcessor;
9 import com.atlassian.cache.ehcache.wrapper.ValueProcessorEhcacheLoaderDecorator;
10 import com.atlassian.cache.impl.CacheEntryListenerSupport;
11 import com.atlassian.cache.impl.LazyCacheEntryListenerSupport;
12 import com.google.common.collect.ImmutableSortedMap;
13 import net.sf.ehcache.Ehcache;
14 import net.sf.ehcache.Element;
15 import net.sf.ehcache.event.CacheEventListener;
16 import net.sf.ehcache.loader.CacheLoader;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import javax.annotation.Nonnull;
21 import javax.annotation.Nullable;
22 import java.util.Collection;
23 import java.util.SortedMap;
24 import java.util.function.Supplier;
25
26 import static com.atlassian.cache.ehcache.DelegatingCacheStatistics.toStatistics;
27 import static com.atlassian.cache.ehcache.wrapper.WrapperUtils.unwrapAllKeys;
28 import static com.atlassian.cache.ehcache.wrapper.WrapperUtils.unwrapElement;
29
30
31
32
33
34
35 class DelegatingCache<K, V> extends ManagedCacheSupport implements Cache<K, V>
36 {
37 private final Ehcache delegate;
38 private final Logger eventLogger;
39 private final Logger stacktraceLogger;
40
41 private final CacheEntryListenerSupport<K, V> listenerSupport = new LazyCacheEntryListenerSupport<K, V>()
42 {
43 @Override
44 protected void init()
45 {
46 delegate.getCacheEventNotificationService().registerListener(new DelegatingCacheEventListener());
47 }
48 };
49
50
51 private final ValueProcessor valueProcessor;
52
53 private DelegatingCache(final Ehcache delegate, CacheSettings settings, final ValueProcessor valueProcessor)
54 {
55 super(delegate, settings);
56 this.delegate = delegate;
57 this.eventLogger = LoggerFactory.getLogger("com.atlassian.cache.event." + delegate.getName());
58 this.stacktraceLogger = LoggerFactory.getLogger("com.atlassian.cache.stacktrace." + delegate.getName());
59 this.valueProcessor = valueProcessor;
60 }
61
62 static <K, V> DelegatingCache<K, V> create(final Ehcache delegate, CacheSettings settings, final ValueProcessor valueProcessor)
63 {
64 return new DelegatingCache<>(delegate, settings, valueProcessor);
65 }
66
67 @Override
68 public boolean containsKey(@Nonnull K key)
69 {
70 return delegate.isKeyInCache(wrap(key));
71 }
72
73 @Nonnull
74 @SuppressWarnings("unchecked")
75 @Override
76 public Collection<K> getKeys()
77 {
78 try
79 {
80 return unwrapAllKeys(delegate.getKeys(), valueProcessor);
81 }
82 catch (Exception e)
83 {
84 throw new CacheException(e);
85 }
86 }
87
88 @Override
89 public void put(@Nonnull final K key, @Nonnull final V value)
90 {
91 try
92 {
93 delegate.put(new Element(wrap(key), wrap(value)));
94 }
95 catch (Exception e)
96 {
97 throw new CacheException(e);
98 }
99 }
100
101 @SuppressWarnings("unchecked")
102 @Nullable
103 @Override
104 public V get(@Nonnull final K key)
105 {
106 try
107 {
108 Element element = unwrap(delegate.get(wrap(key)));
109 return element == null ? null : (V) element.getObjectValue();
110 }
111 catch (net.sf.ehcache.CacheException e)
112 {
113 throw new CacheException(e.getCause());
114 }
115 catch (Exception e)
116 {
117 throw new CacheException(e);
118 }
119 }
120
121 @SuppressWarnings("unchecked")
122 @Nonnull
123 @Override
124 public V get(@Nonnull final K key, @Nonnull final com.atlassian.cache.Supplier<? extends V> valueSupplier)
125 {
126 try
127 {
128 Element element = unwrap(delegate.getWithLoader(wrap(key), getCacheLoader(valueSupplier), null));
129 return (V) element.getObjectValue();
130 }
131 catch (net.sf.ehcache.CacheException e)
132 {
133 throw new CacheException(e.getCause());
134 }
135 catch (Exception e)
136 {
137 throw new CacheException(e);
138 }
139 }
140
141 @Override
142 public void remove(@Nonnull final K key)
143 {
144 try
145 {
146 delegate.remove(wrap(key));
147 }
148 catch (Exception e)
149 {
150 throw new CacheException(e);
151 }
152 }
153
154 @Override
155 public void removeAll()
156 {
157 try
158 {
159 delegate.removeAll();
160 eventLogger.info("Cache {} was flushed", delegate.getName());
161 if (stacktraceLogger.isInfoEnabled()) {
162 stacktraceLogger.info("Cache {} was flushed. Stacktrace:", delegate.getName(), new Exception());
163 }
164 }
165 catch (Exception e)
166 {
167 throw new CacheException(e);
168 }
169 }
170
171 @Override
172 public void clear()
173 {
174 removeAll();
175 }
176
177 @Nullable
178 @Override
179 @SuppressWarnings("unchecked")
180 public V putIfAbsent(@Nonnull K key, @Nonnull V value)
181 {
182 try
183 {
184 Element previous = unwrap(delegate.putIfAbsent(new Element(wrap(key), wrap(value))));
185 if (previous != null)
186 {
187 return (V) previous.getObjectValue();
188 }
189 else
190 {
191 return null;
192 }
193 }
194 catch (Exception e)
195 {
196 throw new CacheException(e);
197 }
198 }
199
200 @Override
201 public boolean remove(@Nonnull K key, @Nonnull V value)
202 {
203 try
204 {
205 return delegate.removeElement(new Element(wrap(key), wrap(value)));
206 }
207 catch (Exception e)
208 {
209 throw new CacheException(e);
210 }
211 }
212
213 @Override
214 public boolean replace(@Nonnull K key, @Nonnull V oldValue, @Nonnull V newValue)
215 {
216 try
217 {
218 return delegate.replace(new Element(wrap(key), wrap(oldValue)), new Element(wrap(key), wrap(newValue)));
219 }
220 catch (Exception e)
221 {
222 throw new CacheException(e);
223 }
224 }
225
226 @Nonnull
227 @Override
228 public SortedMap<CacheStatisticsKey,Supplier<Long>> getStatistics()
229 {
230 if (isStatisticsEnabled())
231 {
232 return toStatistics(delegate.getStatistics());
233 }
234 else
235 {
236 return ImmutableSortedMap.of();
237 }
238 }
239
240 @Override
241 public boolean equals(@Nullable final Object other)
242 {
243 if (other instanceof DelegatingCache)
244 {
245 DelegatingCache<?,?> otherDelegatingCache = (DelegatingCache<?,?>)other;
246 if (delegate.equals(otherDelegatingCache.delegate))
247 {
248 return true;
249 }
250 }
251 return false;
252 }
253
254 @Override
255 public int hashCode()
256 {
257 return 3 + delegate.hashCode();
258 }
259
260 @Override
261 public void addListener(@Nonnull CacheEntryListener<K, V> listener, boolean includeValues)
262 {
263 listenerSupport.add(listener, includeValues);
264 }
265
266 @Override
267 public void removeListener(@Nonnull CacheEntryListener<K, V> listener)
268 {
269 listenerSupport.remove(listener);
270 }
271
272 private Object wrap(Object o) {
273 return valueProcessor.wrap(o);
274 }
275
276 private Object unwrap(Object o) {
277 return valueProcessor.unwrap(o);
278 }
279
280 private Element unwrap(Element element) {
281 return unwrapElement(element, valueProcessor);
282 }
283
284 private CacheLoader getCacheLoader(final com.atlassian.cache.Supplier<? extends V> valueSupplier) {
285 return new ValueProcessorEhcacheLoaderDecorator(new ReferenceCacheLoader(valueSupplier), valueProcessor);
286 }
287
288 private class DelegatingCacheEventListener implements CacheEventListener
289 {
290 @Override
291 public void notifyElementRemoved(Ehcache ehcache, Element element) throws net.sf.ehcache.CacheException
292 {
293 listenerSupport.notifyRemove((K) unwrap(element.getObjectKey()), (V) unwrap(element.getObjectValue()));
294 }
295
296 @Override
297 public void notifyElementPut(Ehcache ehcache, Element element) throws net.sf.ehcache.CacheException
298 {
299 listenerSupport.notifyAdd((K) unwrap(element.getObjectKey()), (V) unwrap(element.getObjectValue()));
300 }
301
302 @Override
303 public void notifyElementUpdated(Ehcache ehcache, Element element) throws net.sf.ehcache.CacheException
304 {
305 listenerSupport.notifyUpdate((K) unwrap(element.getObjectKey()), (V) unwrap(element.getObjectValue()), null);
306 }
307
308 @Override
309 public void notifyElementExpired(Ehcache ehcache, Element element)
310 {
311 listenerSupport.notifyEvict((K) unwrap(element.getObjectKey()), (V) unwrap(element.getObjectValue()));
312 }
313
314 @Override
315 public void notifyElementEvicted(Ehcache ehcache, Element element)
316 {
317 listenerSupport.notifyEvict((K) unwrap(element.getObjectKey()), (V) unwrap(element.getObjectValue()));
318 }
319
320 @Override
321 public void notifyRemoveAll(Ehcache ehcache)
322 {
323
324
325 }
326
327 @Override
328 public void dispose()
329 {
330
331 }
332
333 public Object clone() throws CloneNotSupportedException
334 {
335 throw new CloneNotSupportedException();
336 }
337 }
338 }