1 package org.apache.fulcrum.cache.impl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.ObjectOutputStream;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import org.apache.avalon.framework.activity.Disposable;
30 import org.apache.avalon.framework.activity.Initializable;
31 import org.apache.avalon.framework.configuration.Configurable;
32 import org.apache.avalon.framework.configuration.Configuration;
33 import org.apache.avalon.framework.configuration.ConfigurationException;
34 import org.apache.avalon.framework.logger.AbstractLogEnabled;
35 import org.apache.avalon.framework.thread.ThreadSafe;
36 import org.apache.fulcrum.cache.CachedObject;
37 import org.apache.fulcrum.cache.GlobalCacheService;
38 import org.apache.fulcrum.cache.ObjectExpiredException;
39 import org.apache.fulcrum.cache.RefreshableCachedObject;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class DefaultGlobalCacheService extends AbstractLogEnabled implements
59 GlobalCacheService, Runnable, Configurable, Initializable, Disposable,
60 ThreadSafe
61 {
62
63
64
65 public static final int DEFAULT_INITIAL_CACHE_SIZE = 20;
66
67
68
69
70 public static final String INITIAL_CACHE_SIZE = "cacheInitialSize";
71
72
73
74
75 public static final String CACHE_CHECK_FREQUENCY = "cacheCheckFrequency";
76
77
78
79
80
81 public static final long DEFAULT_CACHE_CHECK_FREQUENCY = 5000;
82
83
84 protected transient ConcurrentHashMap<String, CachedObject<?>> cache = null;
85
86
87
88
89 private transient long cacheCheckFrequency;
90
91
92
93
94 private transient int cacheInitialSize;
95
96
97 private transient Thread houseKeepingThread;
98
99
100 private transient boolean continueThread;
101
102 public DefaultGlobalCacheService()
103 {
104
105 }
106
107
108
109
110
111
112 public long getCacheCheckFrequency()
113 {
114 return this.cacheCheckFrequency;
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129 @Override
130 public <T> CachedObject<T> getObject(String objectId) throws ObjectExpiredException
131 {
132 @SuppressWarnings("unchecked")
133 final CachedObject<T> cachedObject = (CachedObject<T>) this.cache.get(objectId);
134 if (cachedObject == null)
135 {
136
137 throw new ObjectExpiredException();
138 }
139 if (cachedObject.isStale())
140 {
141 if (cachedObject instanceof RefreshableCachedObject)
142 {
143 RefreshableCachedObject<?> refreshableCachedObj = (RefreshableCachedObject<?>) cachedObject;
144 if (refreshableCachedObj.isUntouched())
145 {
146 throw new ObjectExpiredException();
147 }
148
149 refreshableCachedObj.refresh();
150 if (refreshableCachedObj.isStale())
151 {
152 throw new ObjectExpiredException();
153 }
154 }
155 else
156 {
157
158 throw new ObjectExpiredException();
159 }
160 }
161 if (cachedObject instanceof RefreshableCachedObject)
162 {
163
164 RefreshableCachedObject<?> refreshableCachedObj = (RefreshableCachedObject<?>) cachedObject;
165 refreshableCachedObj.touch();
166 }
167 return cachedObject;
168 }
169
170
171
172
173
174
175
176
177
178 @Override
179 public <T> void addObject(final String objectId, final CachedObject<T> object)
180 {
181
182
183 if (this.cache.containsKey(objectId))
184 {
185 this.cache.remove(objectId);
186 }
187 this.cache.put(objectId, object);
188 }
189
190
191
192
193
194
195
196 @Override
197 public void removeObject(String objectId)
198 {
199 this.cache.remove(objectId);
200 }
201
202
203
204
205
206
207
208
209
210 @Override
211 public List<String> getKeys()
212 {
213 ArrayList<String> keys = new ArrayList<>(this.cache.size());
214 for (String key : this.cache.keySet())
215 {
216 try
217 {
218 getObject(key);
219 }
220 catch (ObjectExpiredException oee)
221 {
222
223 continue;
224 }
225 keys.add(key);
226 }
227 return keys;
228 }
229
230
231
232
233
234
235 @Override
236 public List<CachedObject<?>> getCachedObjects()
237 {
238 final ArrayList<CachedObject<?>> objects = new ArrayList<>(this.cache.size());
239 for (String key : this.cache.keySet())
240 {
241 CachedObject<?> cachedObject = null;
242 try
243 {
244
245 cachedObject = getObject(key);
246 if ( cachedObject != null )
247 {
248 objects.add(cachedObject);
249 }
250 }
251 catch (ObjectExpiredException oee)
252 {
253
254 continue;
255 }
256 }
257 return objects;
258 }
259
260
261
262
263
264 @Override
265 public void run()
266 {
267 while (this.continueThread)
268 {
269
270
271 synchronized (this)
272 {
273 try
274 {
275 wait(this.cacheCheckFrequency);
276 }
277 catch (InterruptedException exc)
278 {
279
280 }
281 }
282
283 clearCache();
284 }
285 }
286
287
288
289
290 public void clearCache()
291 {
292 List<String> refreshThese = new ArrayList<>(20);
293
294
295 for (String key : this.cache.keySet())
296 {
297 final CachedObject<?> cachedObject = this.cache.get(key);
298 if (cachedObject instanceof RefreshableCachedObject)
299 {
300 RefreshableCachedObject<?> refreshableObject = (RefreshableCachedObject<?>) cachedObject;
301 if (refreshableObject.isUntouched())
302 {
303 this.cache.remove(key);
304 }
305 else if (refreshableObject.isStale())
306 {
307
308 refreshThese.add(key);
309 }
310 }
311 else if (cachedObject.isStale())
312 {
313 this.cache.remove(key);
314 }
315 }
316
317 for (String key : refreshThese)
318 {
319 CachedObject<?> cachedObject = this.cache.get(key);
320 RefreshableCachedObject<?> refreshableCachedObject = (RefreshableCachedObject<?>) cachedObject;
321 refreshableCachedObject.refresh();
322 }
323 }
324
325
326
327
328
329
330 @Override
331 public int getNumberOfObjects()
332 {
333 return this.cache.size();
334 }
335
336
337
338
339
340
341 @Override
342 public int getCacheSize() throws IOException
343 {
344 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
345 final ObjectOutputStream out = new ObjectOutputStream(baos);
346 out.writeObject(this.cache);
347 out.flush();
348
349
350
351
352
353 return baos.toByteArray().length - 4;
354 }
355
356
357
358
359 @Override
360 public void flushCache()
361 {
362 this.cache.clear();
363 }
364
365
366
367
368
369 @Override
370 public void configure(Configuration conf) throws ConfigurationException
371 {
372 this.cacheCheckFrequency = conf.getAttributeAsLong(
373 CACHE_CHECK_FREQUENCY, DEFAULT_CACHE_CHECK_FREQUENCY);
374 this.cacheInitialSize = conf.getAttributeAsInteger(INITIAL_CACHE_SIZE,
375 DEFAULT_INITIAL_CACHE_SIZE);
376 }
377
378
379
380
381 @Override
382 public void initialize() throws Exception
383 {
384 this.cache = new ConcurrentHashMap<>(this.cacheInitialSize);
385
386 this.continueThread = true;
387 this.houseKeepingThread = new Thread(this);
388
389
390
391
392 this.houseKeepingThread.setDaemon(true);
393 this.houseKeepingThread.start();
394 }
395
396
397
398
399 @Override
400 public void dispose()
401 {
402 synchronized (this)
403 {
404 this.continueThread = false;
405 notifyAll();
406 }
407 }
408 }