1 package com.atlassian.cache.memory;
2
3 import com.atlassian.cache.CacheLoader;
4 import com.atlassian.cache.CacheSettings;
5 import com.atlassian.cache.CacheSettingsBuilder;
6 import com.atlassian.cache.CacheSettingsDefaultsProvider;
7 import com.atlassian.cache.CachedReference;
8 import com.atlassian.cache.ManagedCache;
9 import com.atlassian.cache.impl.AbstractCacheManager;
10 import com.atlassian.cache.impl.ReferenceKey;
11 import com.atlassian.cache.impl.StrongSupplier;
12 import com.atlassian.cache.impl.WeakSupplier;
13 import com.atlassian.instrumentation.DefaultInstrumentRegistry;
14 import com.atlassian.instrumentation.SimpleTimer;
15 import com.atlassian.instrumentation.caches.CacheCollector;
16 import com.atlassian.instrumentation.caches.CacheKeys;
17 import com.google.common.cache.Cache;
18 import com.google.common.cache.CacheBuilder;
19 import com.google.common.cache.LoadingCache;
20
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23
24 import static java.util.concurrent.TimeUnit.MILLISECONDS;
25
26
27
28
29
30
31 public class MemoryCacheManager extends AbstractCacheManager
32 {
33 @Nonnull
34 public MemoryCacheManager()
35 {
36 super(null);
37 }
38
39 public MemoryCacheManager(CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
40 {
41 super(cacheSettingsDefaultsProvider);
42 }
43
44 @Nonnull
45 @SuppressWarnings("unchecked")
46 @Override
47 public <K, V> com.atlassian.cache.Cache<K, V> getCache(@Nonnull String name, @Nullable CacheLoader<K, V> loader, @Nonnull CacheSettings settings)
48 {
49 return getCache(name, loader, settings, DelegatingCache.DEFAULT_CREATE_FUNCTION);
50 }
51
52 @Nonnull
53 @SuppressWarnings("unchecked")
54 public <K, V> com.atlassian.cache.Cache<K, V> getCache(@Nonnull String name, @Nullable CacheLoader<K, V> loader,
55 @Nonnull CacheSettings settings,
56 @Nonnull final DelegatingCache.CreateFunction createFunction)
57 {
58 if (null == loader)
59 {
60 return (com.atlassian.cache.Cache<K, V>) createSimpleCache(name, mergeSettings(name, settings), createFunction);
61 }
62 else
63 {
64 return (com.atlassian.cache.Cache<K, V>) createComputingCache(name, mergeSettings(name, settings), loader, createFunction);
65 }
66 }
67
68 @Nonnull
69 @Override
70 public <V> CachedReference<V> getCachedReference(@Nonnull final String name,
71 @Nonnull final com.atlassian.cache.Supplier<V> supplier,
72 @Nonnull final CacheSettings settings)
73 {
74 return getCachedReference(name, supplier, settings, DelegatingCachedReference.DEFAULT_CREATE_FUNCTION);
75 }
76
77 public <V> CachedReference<V> getCachedReference(@Nonnull final String name,
78 @Nonnull final com.atlassian.cache.Supplier<V> supplier,
79 @Nonnull final CacheSettings settings,
80 @Nonnull final DelegatingCachedReference.CreateFunction createFunction)
81 {
82
83 final CacheSettings overridenSettings = mergeSettings(name,
84 settings.override(new CacheSettingsBuilder().flushable().build()));
85
86 return cacheCreationLocks.apply(name).withLock(new java.util.function.Supplier<DelegatingCachedReference<V>>()
87 {
88 @Override
89 public DelegatingCachedReference<V> get()
90 {
91
92
93 final DelegatingCachedReference.DelegatingReferenceRemovalListener<V> listener = new DelegatingCachedReference.DelegatingReferenceRemovalListener<V>();
94
95 final CacheCollector collector = new DefaultInstrumentRegistry().pullCacheCollector(name);
96
97 LoadingCache<ReferenceKey, V> computingCache = createCacheBuilder(overridenSettings)
98 .removalListener(listener)
99 .build(new com.google.common.cache.CacheLoader<ReferenceKey, V>()
100 {
101
102 @Override
103 public V load(@Nonnull final ReferenceKey key) throws Exception
104 {
105 if(collector.isEnabled())
106 {
107 SimpleTimer timer = new SimpleTimer(CacheKeys.LOAD_TIME.toString());
108 timer.start();
109 try
110 {
111 V value = supplier.get();
112 listener.onSupply(value);
113 return value;
114 }
115 finally
116 {
117 timer.end();
118 collector.miss();
119 collector.getSplits().add(timer);
120 }
121 }
122 else
123 {
124 V value = supplier.get();
125 listener.onSupply(value);
126 return value;
127 }
128 }
129 });
130
131 DelegatingCachedReference<V> cache = createFunction.create(computingCache, name, overridenSettings, collector);
132 listener.setCachedReference(cache);
133 putCacheInMap(name, new WeakSupplier<ManagedCache>(cache));
134 return cache;
135 }
136 });
137 }
138
139 @Override
140 protected ManagedCache createSimpleCache(@Nonnull String name, @Nonnull CacheSettings settings) {
141 return createSimpleCache(name, settings, DelegatingCache.DEFAULT_CREATE_FUNCTION);
142 }
143
144 protected <K,V> ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings,
145 @Nonnull final DelegatingCache.CreateFunction createFunction)
146 {
147
148 java.util.function.Supplier<ManagedCache> cacheSupplier = caches.get(name);
149 if (cacheSupplier != null)
150 {
151 ManagedCache cache = cacheSupplier.get();
152 if (cache != null)
153 {
154 return cache;
155 }
156 }
157 return cacheCreationLocks.apply(name).withLock(new java.util.function.Supplier<ManagedCache>()
158 {
159 @Override
160 public ManagedCache get()
161 {
162 if (!caches.containsKey(name))
163 {
164 DelegatingCache.DelegatingRemovalListener<K, V> listener = new DelegatingCache.DelegatingRemovalListener<>();
165
166 final Cache<K, V> simpleCache = createCacheBuilder(settings)
167 .removalListener(listener)
168 .build();
169
170 DelegatingCache<K, V> cache = createFunction.create(simpleCache, name, settings, null);
171 listener.setCache(cache);
172
173 putCacheInMap(name, new StrongSupplier<ManagedCache>(cache));
174 }
175 return caches.get(name).get();
176 }
177 });
178 }
179
180
181 @Override
182 protected <K, V> ManagedCache createComputingCache(@Nonnull String name, @Nonnull CacheSettings settings, @Nullable CacheLoader<K, V> loader) {
183 return createComputingCache(name, settings, loader, DelegatingCache.DEFAULT_CREATE_FUNCTION);
184 }
185
186 protected <K, V> ManagedCache createComputingCache(@Nonnull final String name,
187 @Nonnull final CacheSettings settings,
188 final CacheLoader<K, V> loader,
189 @Nonnull final DelegatingCache.CreateFunction createFunction)
190 {
191 return cacheCreationLocks.apply(name).withLock(new java.util.function.Supplier<ManagedCache>()
192 {
193 @Override
194 public ManagedCache get()
195 {
196
197
198 final DelegatingCache.DelegatingRemovalListener<K, V> listener = new DelegatingCache.DelegatingRemovalListener<K, V>();
199
200 final CacheLoader<K, V> wrappedLoader = new CacheLoader<K, V>()
201 {
202 @Nonnull
203 @Override
204 public V load(@Nonnull K key)
205 {
206 V value = loader.load(key);
207 listener.onSupply(key, value);
208 return value;
209 }
210 };
211
212 final Cache<K, V> simpleCache = createCacheBuilder(settings)
213 .removalListener(listener)
214 .build();
215
216 DelegatingCache<K, V> cache = createFunction.create(simpleCache, name, settings, wrappedLoader);
217 listener.setCache(cache);
218 putCacheInMap(name, new WeakSupplier<ManagedCache>(cache));
219 return cache;
220 }
221 });
222 }
223
224
225
226
227
228
229
230 protected void putCacheInMap(@Nonnull String name, @Nonnull java.util.function.Supplier<ManagedCache> supplier)
231 {
232 caches.put(name, supplier);
233 }
234
235 private static CacheBuilder<Object, Object> createCacheBuilder(CacheSettings settings)
236 {
237 final CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
238 if (null != settings.getMaxEntries())
239 {
240 cacheBuilder.maximumSize(settings.getMaxEntries());
241 }
242
243
244 if (null != settings.getStatisticsEnabled() && settings.getStatisticsEnabled())
245 {
246 cacheBuilder.recordStats();
247 }
248
249 if (null != settings.getExpireAfterAccess())
250 {
251 cacheBuilder.expireAfterAccess(settings.getExpireAfterAccess(), MILLISECONDS);
252 }
253 else if (null != settings.getExpireAfterWrite())
254 {
255 cacheBuilder.expireAfterWrite(settings.getExpireAfterWrite(), MILLISECONDS);
256 }
257 return cacheBuilder;
258 }
259 }