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