1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.smartics.properties.impl.config.cache;
17
18 import java.io.Serializable;
19 import java.util.Map;
20 import java.util.Properties;
21 import java.util.concurrent.locks.Lock;
22 import java.util.concurrent.locks.ReentrantReadWriteLock;
23
24 import javax.annotation.concurrent.ThreadSafe;
25
26 import org.apache.commons.collections.map.AbstractReferenceMap;
27 import org.apache.commons.collections.map.ReferenceMap;
28 import org.apache.commons.lang.ObjectUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import com.google.common.collect.HashMultimap;
33 import com.google.common.collect.Multimap;
34
35 import de.smartics.properties.api.config.domain.ConfigurationProperties;
36 import de.smartics.properties.api.config.domain.Property;
37 import de.smartics.properties.api.config.domain.ResolvedProperty;
38 import de.smartics.properties.api.config.domain.UnknownPropertyException;
39 import de.smartics.properties.api.core.domain.PropertyValidationException;
40
41
42
43
44
45
46
47
48 @ThreadSafe
49 final class DependencyTrackingCache implements Serializable
50 {
51
52
53
54
55
56
57
58 private static final long serialVersionUID = 1L;
59
60
61
62
63 private static final Logger LOG = LoggerFactory
64 .getLogger(DependencyTrackingCache.class);
65
66
67
68
69
70
71
72
73
74 private final Map<String, Property> cache;
75
76
77
78
79
80
81
82
83
84 private final Multimap<String, String> dependencies;
85
86
87
88
89
90
91 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
92
93
94
95
96
97
98 private final Lock readLock = lock.readLock();
99
100
101
102
103
104
105 private final Lock writeLock = lock.writeLock();
106
107
108
109
110
111
112
113
114 @SuppressWarnings("unchecked")
115 public DependencyTrackingCache()
116 {
117 this.cache =
118 new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.HARD,
119 true);
120 this.dependencies = HashMultimap.create();
121 }
122
123
124
125
126
127
128
129
130
131 public int getCacheSize()
132 {
133 final int cacheSize;
134 readLock.lock();
135 try
136 {
137 cacheSize = cache.size();
138 }
139 finally
140 {
141 readLock.unlock();
142 }
143
144 return cacheSize;
145 }
146
147
148
149 public ResolvedProperty getResolvedProperty(
150 final ConfigurationProperties propertySource, final String key,
151 final Object defaultValue) throws IllegalArgumentException,
152 UnknownPropertyException, PropertyValidationException
153 {
154 readLock.lock();
155
156 try
157 {
158 final Property property = cache.get(key);
159 if (property instanceof ResolvedProperty)
160 {
161 LOG.debug("Cache hit: {}", key);
162 return (ResolvedProperty) property;
163 }
164 else
165 {
166 readLock.unlock();
167
168 final ResolvedProperty resolvedProperty =
169 propertySource.getResolvedProperty(key, defaultValue);
170
171 writeLock.lock();
172 try
173 {
174 cache.put(key, resolvedProperty);
175 attachDependencies(resolvedProperty);
176 }
177 finally
178 {
179 writeLock.unlock();
180 readLock.lock();
181 }
182
183 LOG.debug("Cache miss: {}", key);
184 return resolvedProperty;
185 }
186 }
187 finally
188 {
189 readLock.unlock();
190 }
191 }
192
193 public Property getProperty(final ConfigurationProperties propertySource,
194 final String key, final Object defaultValue)
195 {
196 readLock.lock();
197
198 try
199 {
200 Property property = cache.get(key);
201 if (property != null)
202 {
203 LOG.debug("Cache hit: {}", key);
204 return property;
205 }
206 else
207 {
208 readLock.unlock();
209
210 property = propertySource.getProperty(key, defaultValue);
211
212 writeLock.lock();
213 try
214 {
215 cache.put(key, property);
216 if (property instanceof ResolvedProperty)
217 {
218 attachDependencies((ResolvedProperty) property);
219 }
220 }
221 finally
222 {
223 writeLock.unlock();
224 readLock.lock();
225 }
226
227 LOG.debug("Cache miss: {}", key);
228 return property;
229 }
230 }
231 finally
232 {
233 readLock.unlock();
234 }
235 }
236
237 public Property removeFromCache(final String key)
238 {
239 writeLock.lock();
240 try
241 {
242 final Property oldProperty = cache.remove(key);
243 removeDependencies(key);
244 LOG.debug("Cache revoke: {}", key);
245 return oldProperty;
246 }
247 finally
248 {
249 writeLock.unlock();
250 }
251 }
252
253 public void removeFromCache(final Properties properties)
254 {
255 writeLock.lock();
256 try
257 {
258 for (final Object key : properties.keySet())
259 {
260 cache.remove(key);
261 removeDependencies(ObjectUtils.toString(key, null));
262 LOG.debug("Cache revoke due to init: {}", key);
263 }
264 }
265 finally
266 {
267 writeLock.unlock();
268 }
269 }
270
271
272
273
274 private void attachDependencies(final ResolvedProperty property)
275 {
276 final String key = property.getName();
277
278 for (final String dependency : property.getDependencies())
279 {
280 dependencies.put(dependency, key);
281 }
282 }
283
284
285
286
287 private void removeDependencies(final String key)
288 {
289 for (final String dependant : dependencies.removeAll(key))
290 {
291 LOG.debug("Cache revoke due to dependency on {}: {}", key, dependant);
292 removeFromCache(dependant);
293 }
294 }
295
296
297
298 }