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