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.util.concurrent.Supplier;
16
17 import com.google.common.cache.Cache;
18 import com.google.common.cache.CacheBuilder;
19 import com.google.common.cache.LoadingCache;
20
21 import static java.util.concurrent.TimeUnit.MILLISECONDS;
22
23
24
25
26
27
28 public class MemoryCacheManager extends AbstractCacheManager
29 {
30 @Nonnull
31 public MemoryCacheManager()
32 {
33 super(null);
34 }
35
36 public MemoryCacheManager(CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
37 {
38 super(cacheSettingsDefaultsProvider);
39 }
40
41 @Nonnull
42 @Override
43 public <V> CachedReference<V> getCachedReference(@Nonnull final String name,
44 @Nonnull final com.atlassian.cache.Supplier<V> supplier,
45 @Nonnull final CacheSettings settings)
46 {
47
48 final CacheSettings overridenSettings = mergeSettings(name,
49 settings.override(new CacheSettingsBuilder().flushable().build()));
50
51 return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<DelegatingCachedReference<V>>()
52 {
53 @Override
54 public DelegatingCachedReference<V> get()
55 {
56
57
58 final DelegatingCachedReference.DelegatingReferenceRemovalListener<V> listener = new DelegatingCachedReference.DelegatingReferenceRemovalListener<V>();
59
60 LoadingCache<ReferenceKey, V> computingCache = createCacheBuilder(overridenSettings)
61 .removalListener(listener)
62 .build(new com.google.common.cache.CacheLoader<ReferenceKey, V>()
63 {
64 @Override
65 public V load(@Nonnull final ReferenceKey key) throws Exception
66 {
67 V value = supplier.get();
68 listener.onSupply(value);
69 return value;
70 }
71 });
72
73 DelegatingCachedReference<V> cache = DelegatingCachedReference.create(computingCache, name, overridenSettings);
74 listener.setCachedReference(cache);
75 caches.put(name, new WeakSupplier<ManagedCache>(cache));
76 return cache;
77 }
78 });
79 }
80
81 protected ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings)
82 {
83
84 Supplier<ManagedCache> cacheSupplier = caches.get(name);
85 if (cacheSupplier != null)
86 {
87 ManagedCache cache = cacheSupplier.get();
88 if (cache != null)
89 {
90 return cache;
91 }
92 }
93 return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
94 {
95 @Override
96 public ManagedCache get()
97 {
98 if (!caches.containsKey(name))
99 {
100 DelegatingCache.DelegatingRemovalListener<Object,Object> listener = new DelegatingCache.DelegatingRemovalListener<Object,Object>();
101
102 final Cache<Object, Object> simpleCache = createCacheBuilder(settings)
103 .removalListener(listener)
104 .build();
105
106 DelegatingCache<Object,Object> cache = DelegatingCache.create(simpleCache, name, settings);
107 listener.setCache(cache);
108
109 caches.put(name, new StrongSupplier<ManagedCache>(cache));
110 }
111 return caches.get(name).get();
112 }
113 });
114 }
115
116 protected <K, V> ManagedCache createComputingCache(@Nonnull final String name, @Nonnull final CacheSettings settings, final CacheLoader<K, V> loader)
117 {
118 return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
119 {
120 @Override
121 public ManagedCache get()
122 {
123
124
125 final DelegatingCache.DelegatingRemovalListener<K,V> listener = new DelegatingCache.DelegatingRemovalListener<K,V>();
126 final BlockingCacheLoader<K,V> cacheLoader = new BlockingCacheLoader<K, V>(
127 new com.google.common.cache.CacheLoader<K,V>()
128 {
129 @Override
130 public V load(@Nonnull final K key) throws Exception
131 {
132 V value = loader.load(key);
133 listener.onSupply(key, value);
134 return value;
135 }
136
137 });
138
139 LoadingCache<K, V> computingCache = createCacheBuilder(settings)
140 .removalListener(listener)
141 .build(cacheLoader);
142 DelegatingCache<K,V> cache = DelegatingCache.create(computingCache, name, settings, cacheLoader);
143 listener.setCache(cache);
144 caches.put(name, new WeakSupplier<ManagedCache>(cache));
145 return cache;
146 }
147 });
148 }
149
150 private static CacheBuilder<Object,Object> createCacheBuilder(CacheSettings settings)
151 {
152 final CacheBuilder<Object,Object> cacheBuilder = CacheBuilder.newBuilder();
153 if (null != settings.getMaxEntries())
154 {
155 cacheBuilder.maximumSize(settings.getMaxEntries());
156 }
157
158 if (null != settings.getStatisticsEnabled() && settings.getStatisticsEnabled())
159 {
160 cacheBuilder.recordStats();
161 }
162
163 if (null != settings.getExpireAfterAccess())
164 {
165 cacheBuilder.expireAfterAccess(settings.getExpireAfterAccess(), MILLISECONDS);
166 }
167 else if (null != settings.getExpireAfterWrite())
168 {
169 cacheBuilder.expireAfterWrite(settings.getExpireAfterWrite(), MILLISECONDS);
170 }
171 return cacheBuilder;
172 }
173 }