View Javadoc

1   package com.atlassian.cache.memory;
2   
3   import java.util.SortedMap;
4   import java.util.concurrent.Callable;
5   
6   import javax.annotation.Nonnull;
7   import javax.annotation.Nullable;
8   
9   import com.atlassian.cache.CacheException;
10  import com.atlassian.cache.CacheSettings;
11  import com.atlassian.cache.CacheStatisticsKey;
12  import com.atlassian.cache.CachedReference;
13  import com.atlassian.cache.CachedReferenceListener;
14  import com.atlassian.cache.impl.CachedReferenceListenerSupport;
15  import com.atlassian.cache.impl.DefaultCachedReferenceListenerSupport;
16  import com.atlassian.cache.impl.ReferenceKey;
17  import com.atlassian.instrumentation.SimpleTimer;
18  import com.atlassian.instrumentation.caches.CacheCollector;
19  import com.atlassian.instrumentation.caches.CacheKeys;
20  
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.collect.ImmutableSortedMap;
25  import com.google.common.util.concurrent.UncheckedExecutionException;
26  
27  import static com.atlassian.cache.memory.DelegatingCacheStatistics.toStatistics;
28  
29  /**
30   * A Lazy Reference that delegates Concurrent Map.
31   *
32   * @since 2.0
33   */
34  class DelegatingCachedReference<V> extends ManagedCacheSupport implements CachedReference<V>
35  {
36      private final LoadingCache<ReferenceKey, V> internalCache;
37      private final CachedReferenceListenerSupport<V> listenerSupport;
38      private final CacheCollector counter;
39  
40      private DelegatingCachedReference(final LoadingCache<ReferenceKey, V> internalCache,
41              String name, CacheSettings settings, final CacheCollector collector)
42      {
43          super(name, settings);
44          this.internalCache = internalCache;
45  
46          this.listenerSupport = new DefaultCachedReferenceListenerSupport<V>();
47          this.counter = collector;
48      }
49  
50      static <V> DelegatingCachedReference<V> create(final LoadingCache<ReferenceKey, V> internalCache,
51              String name, CacheSettings settings, CacheCollector collector)
52      {
53          return new DelegatingCachedReference<V>(internalCache, name, settings, collector);
54      }
55  
56      @Override
57      public CacheCollector getCacheCollector()
58      {
59          return counter;
60      }
61  
62      @Nonnull
63      @Override
64      public V get()
65      {
66          try
67          {
68              V value = internalCache.getIfPresent(ReferenceKey.KEY);
69              if (value != null)
70              {
71                  if (isStatisticsEnabled())
72                  {
73                      counter.hit();
74                  }
75                  return value;
76              }
77              else
78              {
79                  return getUnderLock();
80              }
81          }
82          catch (UncheckedExecutionException e)
83          {
84              throw new CacheException(e.getCause());
85          }
86          catch (Exception e)
87          {
88              throw new CacheException(e);
89          }
90      }
91  
92      synchronized private V getUnderLock()
93      {
94          return internalCache.getUnchecked(ReferenceKey.KEY);
95      }
96  
97      @Override
98      synchronized public void reset()
99      {
100         try
101         {
102             internalCache.invalidate(ReferenceKey.KEY);
103             if (isStatisticsEnabled())
104             {
105                 counter.remove();
106             }
107         }
108         catch (Exception e)
109         {
110             throw new CacheException(e);
111         }
112     }
113 
114     @Override
115     public void clear()
116     {
117         reset();
118     }
119 
120     @Override
121     public void setStatistics(final boolean enabled)
122     {
123         counter.setEnabled(enabled);
124     }
125 
126     @Override
127     public boolean isStatisticsEnabled()
128     {
129         return counter.isEnabled();
130     }
131 
132     @Override
133     public boolean equals(@Nullable final Object other)
134     {
135         if (other instanceof DelegatingCachedReference)
136         {
137             DelegatingCachedReference<?> otherDelegatingReference = (DelegatingCachedReference<?>) other;
138             if (internalCache.equals(otherDelegatingReference.internalCache))
139             {
140                 return true;
141             }
142         }
143         return false;
144     }
145 
146     @Override
147     public int hashCode()
148     {
149         return 3 + internalCache.hashCode();
150     }
151 
152     @Nonnull
153     @Override
154     public SortedMap<CacheStatisticsKey, com.atlassian.util.concurrent.Supplier<Long>> getStatistics()
155     {
156         if (isStatisticsEnabled())
157         {
158             return toStatistics(counter);
159         }
160         else
161         {
162             return ImmutableSortedMap.of();
163         }
164     }
165 
166     @Override
167     public void addListener(@Nonnull CachedReferenceListener<V> listener, boolean includeValues)
168     {
169         listenerSupport.add(listener, includeValues);
170     }
171 
172     @Override
173     public void removeListener(@Nonnull CachedReferenceListener<V> listener)
174     {
175         listenerSupport.remove(listener);
176     }
177 
178     protected static class DelegatingReferenceRemovalListener<V> implements RemovalListener<ReferenceKey, V>
179     {
180         private DelegatingCachedReference<V> cachedReference;
181 
182         protected void onSupply(V value)
183         {
184             cachedReference.listenerSupport.notifySet(value);
185         }
186 
187         @Override
188         public void onRemoval(RemovalNotification<ReferenceKey, V> notification)
189         {
190             switch (notification.getCause())
191             {
192                 case COLLECTED:
193                 case EXPIRED:
194                     cachedReference.listenerSupport.notifyEvict(notification.getValue());
195                     break;
196                 case EXPLICIT:
197                     cachedReference.listenerSupport.notifyReset(notification.getValue());
198                     break;
199                 case REPLACED:
200                     V value = cachedReference.internalCache.getIfPresent(ReferenceKey.KEY);
201                     cachedReference.listenerSupport.notifySet(value);
202                     break;
203             }
204         }
205 
206         public void setCachedReference(DelegatingCachedReference<V> cachedReference)
207         {
208             this.cachedReference = cachedReference;
209         }
210     }
211 }