View Javadoc

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   * A Cache that delegates Concurrent Map.
28   *
29   * @since 2.0
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                  // Here we care only for the case when oldValue was null, e.g. missing as in the other
82                  // cases DelegatingRemovalListener will be called with REPLACED notification
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                 // Here we care only for the case when oldValue was null, e.g. missing as in the other
133                 // cases DelegatingRemovalListener will be called with REPLACED notification
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 }