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.Properties;
20 import java.util.concurrent.TimeUnit;
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.lang.ObjectUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.common.collect.HashMultimap;
31 import com.google.common.collect.Multimap;
32
33 import de.smartics.properties.api.config.domain.ConfigurationProperties;
34 import de.smartics.properties.api.config.domain.DescribedProperty;
35 import de.smartics.properties.api.config.domain.Property;
36 import de.smartics.properties.api.config.domain.PropertyCollection;
37 import de.smartics.properties.api.config.domain.UnknownPropertyException;
38 import de.smartics.properties.api.config.domain.ValidatedProperty;
39 import de.smartics.properties.api.config.domain.key.ConfigurationKey;
40 import de.smartics.properties.api.core.domain.PropertyDescriptor;
41 import de.smartics.properties.api.core.domain.PropertyValidationException;
42 import de.smartics.properties.spi.config.cache.CacheManagerFactory;
43 import de.smartics.properties.spi.config.cache.UnawareCache;
44 import de.smartics.util.lang.Arg;
45
46
47
48
49
50
51
52
53 @ThreadSafe
54 final class DependencyTrackingCache implements Serializable
55 {
56
57
58
59
60
61
62
63 private static final long serialVersionUID = 1L;
64
65
66
67
68 private static final Logger LOG = LoggerFactory
69 .getLogger(DependencyTrackingCache.class);
70
71
72
73
74
75
76
77
78 private final ConfigurationKey<?> configurationKey;
79
80
81
82
83
84
85
86 private final UnawareCache<String, DescribedProperty> cache;
87
88
89
90
91
92
93
94
95
96 private final Multimap<String, String> dependencies;
97
98
99
100
101
102
103 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
104
105
106
107
108
109
110 private final Lock readLock = lock.readLock();
111
112
113
114
115
116
117 private final Lock writeLock = lock.writeLock();
118
119
120
121
122
123
124
125
126
127
128
129
130
131 @SuppressWarnings("unchecked")
132 public DependencyTrackingCache(final ConfigurationKey<?> configurationKey)
133 throws NullPointerException
134 {
135 this.configurationKey =
136 Arg.checkNotNull("configurationKey", configurationKey);
137
138 final String cacheName = configurationKey.toString();
139 this.cache =
140 (UnawareCache<String, DescribedProperty>) CacheManagerFactory
141 .getPropertiesCache(cacheName);
142 LOG.debug("Caches: {}", CacheManagerFactory.getCacheNames());
143 this.dependencies = HashMultimap.create();
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public ConfigurationKey<?> getConfigurationKey()
160 {
161 return configurationKey;
162 }
163
164
165
166
167 public ValidatedProperty getValidatedProperty(
168
169 final ConfigurationProperties propertySource,
170 final PropertyDescriptor descriptor, final Object defaultValue)
171 throws IllegalArgumentException, UnknownPropertyException,
172 PropertyValidationException
173 {
174 readLock.lock();
175 boolean doReadUnlock = true;
176 final String key = descriptor.getKey().toString();
177 try
178 {
179 final Property property = cache.get(key);
180 if (property instanceof ValidatedProperty)
181 {
182 LOG.debug("Cache hit: {}", key);
183 return (ValidatedProperty) property;
184 }
185 else
186 {
187 readLock.unlock();
188 doReadUnlock = false;
189
190 final ValidatedProperty validatedProperty =
191 propertySource.getValidatedProperty(key, defaultValue);
192
193 if (validatedProperty != null)
194 {
195 writeLock.lock();
196 try
197 {
198 putToCache(descriptor, key, validatedProperty);
199 attachDependencies(validatedProperty);
200 }
201 finally
202 {
203 writeLock.unlock();
204 }
205 }
206
207 LOG.debug("Cache miss: {}", key);
208 return validatedProperty;
209 }
210 }
211 finally
212 {
213 if (doReadUnlock)
214 {
215 readLock.unlock();
216 }
217 }
218 }
219
220 private void putToCache(final PropertyDescriptor descriptor,
221 final String key, final ValidatedProperty validatedProperty)
222 {
223 final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
224 cache
225 .put(key, validatedProperty, updateIntervalInMs, TimeUnit.MILLISECONDS);
226 }
227
228 private void putToCache(final PropertyDescriptor descriptor,
229 final String key, final DescribedProperty property)
230 {
231 final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
232 cache.put(key, property, updateIntervalInMs, TimeUnit.MILLISECONDS);
233 }
234
235
236 public DescribedProperty getProperty(final ConfigurationProperties propertySource,
237 final PropertyDescriptor descriptor, final Object defaultValue)
238
239 {
240 readLock.lock();
241 boolean doReadUnlock = true;
242
243 final String key = descriptor.getKey().toString();
244 try
245 {
246 DescribedProperty property = cache.get(key);
247 if (property != null)
248 {
249 LOG.debug("Cache hit: {}", key);
250 return property;
251 }
252 else
253 {
254 readLock.unlock();
255 doReadUnlock = false;
256
257 property = propertySource.getProperty(key, defaultValue);
258
259 if (property != null)
260 {
261 writeLock.lock();
262 try
263 {
264 putToCache(descriptor, key, property);
265 if (property instanceof ValidatedProperty)
266 {
267 attachDependencies((ValidatedProperty) property);
268 }
269 }
270 finally
271 {
272 writeLock.unlock();
273 }
274 }
275
276 LOG.debug("Cache miss: {}", key);
277 return property;
278 }
279 }
280 finally
281 {
282 if (doReadUnlock)
283 {
284 readLock.unlock();
285 }
286 }
287 }
288
289 public Property removeFromCache(final String key)
290 {
291 writeLock.lock();
292 try
293 {
294 final Property oldProperty = cache.remove(key);
295 removeDependencies(key);
296 LOG.debug("Cache revoke: {}", key);
297 return oldProperty;
298 }
299 finally
300 {
301 writeLock.unlock();
302 }
303 }
304
305 public void removeFromCache(final Properties properties)
306 {
307 writeLock.lock();
308 try
309 {
310 for (final Object key : properties.keySet())
311 {
312 cache.remove((String) key);
313 removeDependencies(ObjectUtils.toString(key, null));
314 LOG.debug("Cache revoke due to init: {}", key);
315 }
316 }
317 finally
318 {
319 writeLock.unlock();
320 }
321 }
322
323 public void removeFromCache(final PropertyCollection collection)
324 {
325 writeLock.lock();
326 try
327 {
328 for (final Property property : collection)
329 {
330 final String key = property.getName();
331 cache.remove(key);
332 removeDependencies(ObjectUtils.toString(key, null));
333 LOG.debug("Cache revoke due to init: {}", key);
334 }
335 }
336 finally
337 {
338 collection.close();
339 writeLock.unlock();
340 }
341 }
342
343
344
345
346 private void attachDependencies(final ValidatedProperty property)
347 {
348 final String key = property.getName();
349
350 for (final String dependency : property.getDependencies())
351 {
352 dependencies.put(dependency, key);
353 }
354 }
355
356
357
358
359 private void removeDependencies(final String key)
360 {
361 for (final String dependant : dependencies.removeAll(key))
362 {
363 LOG.debug("Cache revoke due to dependency on {}: {}", key, dependant);
364 removeFromCache(dependant);
365 }
366 }
367
368
369
370 }