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
31
32
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 }