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 .getInstance().createManager().getPropertiesCache(cacheName);
142 LOG.debug("Caches: {}", CacheManagerFactory.getInstance().createManager()
143 .getCacheNames());
144 this.dependencies = HashMultimap.create();
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public ConfigurationKey<?> getConfigurationKey()
161 {
162 return configurationKey;
163 }
164
165
166
167
168 public ValidatedProperty getValidatedProperty(
169
170 final ConfigurationProperties propertySource,
171 final PropertyDescriptor descriptor, final Object defaultValue)
172 throws IllegalArgumentException, UnknownPropertyException,
173 PropertyValidationException
174 {
175 readLock.lock();
176 boolean doReadUnlock = true;
177 final String key = descriptor.getKey().toString();
178 try
179 {
180 final Property property = cache.get(key);
181 if (property instanceof ValidatedProperty)
182 {
183 LOG.debug("Cache hit: {}", key);
184 return (ValidatedProperty) property;
185 }
186 else
187 {
188 readLock.unlock();
189 doReadUnlock = false;
190
191 final ValidatedProperty validatedProperty =
192 propertySource.getValidatedProperty(key, defaultValue);
193
194 if (validatedProperty != null)
195 {
196 writeLock.lock();
197 try
198 {
199 putToCache(descriptor, key, validatedProperty);
200 attachDependencies(validatedProperty);
201 }
202 finally
203 {
204 writeLock.unlock();
205 }
206 }
207
208 LOG.debug("Cache miss: {}", key);
209 return validatedProperty;
210 }
211 }
212 finally
213 {
214 if (doReadUnlock)
215 {
216 readLock.unlock();
217 }
218 }
219 }
220
221 private void putToCache(final PropertyDescriptor descriptor,
222 final String key, final ValidatedProperty validatedProperty)
223 {
224 final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
225 cache
226 .put(key, validatedProperty, updateIntervalInMs, TimeUnit.MILLISECONDS);
227 }
228
229 private void putToCache(final PropertyDescriptor descriptor,
230 final String key, final DescribedProperty property)
231 {
232 final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
233 cache.put(key, property, updateIntervalInMs, TimeUnit.MILLISECONDS);
234 }
235
236
237 public DescribedProperty getProperty(
238 final ConfigurationProperties propertySource,
239 final PropertyDescriptor descriptor, final Object defaultValue)
240
241 {
242 readLock.lock();
243 boolean doReadUnlock = true;
244
245 final String key = descriptor.getKey().toString();
246 try
247 {
248 DescribedProperty property = cache.get(key);
249 if (property != null)
250 {
251 LOG.debug("Cache hit: {}", key);
252 return property;
253 }
254 else
255 {
256 readLock.unlock();
257 doReadUnlock = false;
258
259 property = propertySource.getProperty(key, defaultValue);
260
261 if (property != null)
262 {
263 writeLock.lock();
264 try
265 {
266 putToCache(descriptor, key, property);
267 if (property instanceof ValidatedProperty)
268 {
269 attachDependencies((ValidatedProperty) property);
270 }
271 }
272 finally
273 {
274 writeLock.unlock();
275 }
276 }
277
278 LOG.debug("Cache miss: {}", key);
279 return property;
280 }
281 }
282 finally
283 {
284 if (doReadUnlock)
285 {
286 readLock.unlock();
287 }
288 }
289 }
290
291 public Property removeFromCache(final String key)
292 {
293 writeLock.lock();
294 try
295 {
296 final Property oldProperty = cache.remove(key);
297 removeDependencies(key);
298 LOG.debug("Cache revoke: {}", key);
299 return oldProperty;
300 }
301 finally
302 {
303 writeLock.unlock();
304 }
305 }
306
307 public void removeFromCache(final Properties properties)
308 {
309 writeLock.lock();
310 try
311 {
312 for (final Object key : properties.keySet())
313 {
314 cache.remove((String) key);
315 removeDependencies(ObjectUtils.toString(key, null));
316 LOG.debug("Cache revoke due to init: {}", key);
317 }
318 }
319 finally
320 {
321 writeLock.unlock();
322 }
323 }
324
325 public void removeFromCache(final PropertyCollection collection)
326 {
327 writeLock.lock();
328 try
329 {
330 for (final Property property : collection)
331 {
332 final String key = property.getName();
333 cache.remove(key);
334 removeDependencies(ObjectUtils.toString(key, null));
335 LOG.debug("Cache revoke due to init: {}", key);
336 }
337 }
338 finally
339 {
340 collection.close();
341 writeLock.unlock();
342 }
343 }
344
345
346
347
348 private void attachDependencies(final ValidatedProperty property)
349 {
350 final String key = property.getName();
351
352 for (final String dependency : property.getDependencies())
353 {
354 dependencies.put(dependency, key);
355 }
356 }
357
358
359
360
361 private void removeDependencies(final String key)
362 {
363 for (final String dependant : dependencies.removeAll(key))
364 {
365 LOG.debug("Cache revoke due to dependency on {}: {}", key, dependant);
366 removeFromCache(dependant);
367 }
368 }
369
370
371
372 }