View Javadoc
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   * A Cache that delegates to EhCache.
32   *
33   * @since 2.0
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             // There is no way to enumerate the keys that were in the cache, therefore we cannot
324             // produce any meaningful event
325         }
326 
327         @Override
328         public void dispose()
329         {
330             // We don't hold onto any resources so there is nothing to be done.
331         }
332 
333         public Object clone() throws CloneNotSupportedException
334         {
335             throw new CloneNotSupportedException();
336         }
337     }
338 }