1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.ashkay;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23
24 import EDU.oswego.cs.dl.util.concurrent.Latch;
25 import org.apache.log4j.Logger;
26
27
28 /***
29 * ObjectCache is the default implementation of an object caching mechanism.
30 * <br><br>
31 * ObjectCaching makes use of a Factory for the creation of objects that are not
32 * cached. The user of an ObjectCache provides an implementation of
33 * ObjectFactory to the cache. If the cache does not find the object asked for,
34 * it uses the factory to create the object, cache it, and return it.
35 * <br><br>
36 * Looking up an object from cache requires a key. The key is a unique
37 * identifier. The key could be a database record id, a URI, or any other id
38 * that is guaranteed to be unique. The key is also used by the factory to
39 * create the object. The factory ought to be able to construct an object given
40 * only a key. This key is then used as the key in the cache.
41 * <br><br>
42 * Gets may also be done with a data parameter. The data provides construction
43 * information to the factory about the object being created. The cache does not
44 * use the data directly but, if the object is not cached, will pass the data
45 * to the factory. Common use of the data would be if the user of the cache
46 * has an "un-objectified" data source (e.g. XML document) from some other
47 * source that would save the factory the lookup call. So, if the user has the
48 * key and the data and wants the object form of the data, it simply asks the
49 * cache passing in the key and data.
50 * <br><br>
51 * The ObjectCache uses CachingStrategies to detirmine a strategy for caching
52 * objects that is stores. These strategies may affect caching in any number of
53 * ways. You might want to use a cache that does not prevent garbage collection
54 * of cached objects, only getting benefit of the cache as memory allows. A
55 * client may want to place timeouts for objects to force them to be re-loaded
56 * after a period of time. Any number of caching strategies may be used, but be
57 * careful not to use two caching strategies that work in opposition. That is
58 * a check left to the user at this point.
59 *
60 * @author <a href="mailto:bangroot@users.sf.net">Dave Brown</a>
61 */
62 public class ObjectCache
63 {
64 private static final Logger LOG = Logger.getLogger(ObjectCache.class);
65
66 private ObjectFactory factory;
67 private List strategies = new ArrayList();
68 private Map cache = new HashMap();
69
70 public ObjectCache()
71 {
72 this(null, new ArrayList());
73 }
74
75 /***
76 * Constructs an ObjectCache from a factory using the default strategy of
77 * NONE.
78 *
79 * @param aFactory - the factory for the cache to use
80 */
81
82 public ObjectCache(ObjectFactory aFactory)
83 {
84 this(aFactory, new ArrayList());
85 }
86
87 /***
88 * Constructs an ObjectCache without a factory using the strategies indicated.
89 *
90 * @param theStrategies
91 */
92
93 public ObjectCache(List theStrategies)
94 {
95 this(null, theStrategies);
96 }
97
98 /***
99 * Constructs an ObjectCache from a factory and strategy.
100 *
101 * @param aFactory - the factory for the cache to use
102 * @param theStrategies - the caching strategies for this cache
103 */
104 public ObjectCache(ObjectFactory aFactory, List theStrategies)
105 {
106 factory = aFactory;
107 strategies = theStrategies;
108 }
109
110 /***
111 * Adds a caching strategy to this cache
112 *
113 * @param aStrategy - the strategy to add
114 */
115 public ObjectCache addStrategy(CachingStrategy aStrategy)
116 {
117 synchronized (strategies)
118 {
119 strategies.add(aStrategy);
120 }
121
122 return this;
123 }
124
125 /***
126 * Removes a strategy from this cache
127 *
128 * @param aStrategy - the strategy to remove
129 */
130 public ObjectCache removeStrategy(CachingStrategy aStrategy)
131 {
132 synchronized (strategies)
133 {
134 strategies.remove(aStrategy);
135 }
136
137 return this;
138 }
139
140 /***
141 * Checks if this cache uses the specified caching strategy
142 *
143 * @param aStrategy - the strategy to check
144 */
145 public boolean usesStrategy(CachingStrategy aStrategy)
146 {
147 synchronized (strategies)
148 {
149 return strategies.contains(aStrategy);
150 }
151 }
152
153 /***
154 * Finds an object in the cache and returns it. If the cache contains no
155 * object for the specified key, the cache attempts to construct it using
156 * its factory.
157 *
158 * @param key - the key to lookup
159 * @throws CreationException on any error during creation
160 * @see #get(java.lang.Object, java.lang.Object)
161 */
162 public Object get(Object key) throws CreationException
163 {
164 return this.get(key, null);
165 }
166
167 /***
168 * Forcefully evicts an object/key from the cache. This will only evict the
169 * object if it has been created (i.e. not being created by a factory).
170 *
171 * @param key to the object to evict
172 * @return the object evicted or null if none
173 */
174 public Object evict(Object key)
175 {
176 Object val = null;
177 Object entry = cache.get(key);
178 if (entry instanceof CacheEntry)
179 {
180 val = ((CacheEntry) entry).getEntryObject();
181 synchronized (cache)
182 {
183 cache.remove(key);
184 }
185 }
186 return val;
187 }
188
189 /***
190 * Finds an object in the cache and returns it. If the cache contains no
191 * object for the specified key, the cache attempts to construct it using
192 * its factory and the passed in data. The cache also uses the caching
193 * strategies to validate the entry in the cache.
194 *
195 * @param key - the key to lookup
196 * @param data - the data to aid construction
197 * @throws CreationException on any error during creation
198 */
199 public Object get(Object key, Object data) throws CreationException
200 {
201 CacheEntry entry = null;
202 entry = findCacheEntry(key);
203
204 boolean passes = validateEntry(entry);
205
206 if (!passes)
207 {
208 Latch buildLatch = null;
209 buildLatch = prepareLatch(key);
210 try
211 {
212 entry = createObjectFor(key, data);
213 }
214 finally
215 {
216 if (null == entry)
217 {
218 synchronized (cache)
219 {
220 cache.put(key, null);
221 }
222 }
223 buildLatch.release();
224 }
225 }
226
227 Object val = null;
228 if (null != entry)
229 {
230 val = entry.getEntryObject();
231 }
232
233 return val;
234 }
235
236 private CacheEntry createObjectFor(Object key, Object data) throws CreationException
237 {
238 CacheEntry entry = null;
239
240 if (null != factory)
241 {
242 Object tempO = factory.createObjectFor(key, data);
243 if (null != tempO)
244 {
245 entry = new CacheEntry(key, tempO);
246 entry.setCache(this);
247 entry = prepareEntry(entry);
248 }
249 }
250
251 synchronized (cache)
252 {
253 cache.put(key, entry);
254 }
255
256 return entry;
257 }
258
259 private CacheEntry prepareEntry(CacheEntry entry)
260 {
261 synchronized (strategies)
262 {
263 Iterator strategyIter = strategies.iterator();
264 while (strategyIter.hasNext())
265 {
266 CachingStrategy strat = (CachingStrategy) strategyIter.next();
267 entry = strat.prepare(entry);
268 }
269 }
270 return entry;
271 }
272
273 private Latch prepareLatch(Object key)
274 {
275 Latch buildLatch;
276 synchronized (cache)
277 {
278 buildLatch = new Latch();
279 cache.put(key, buildLatch);
280 }
281 return buildLatch;
282 }
283
284 private boolean validateEntry(CacheEntry entry)
285 {
286 boolean passes = false;
287 if (entry != null)
288 {
289 passes = true;
290 synchronized (strategies)
291 {
292 Iterator strategyIter = strategies.iterator();
293 while (strategyIter.hasNext())
294 {
295 CachingStrategy strat = (CachingStrategy) strategyIter.next();
296 if (!strat.validate(entry))
297 {
298 passes = false;
299 }
300 }
301 }
302 }
303 return passes;
304 }
305
306 private CacheEntry findCacheEntry(Object key) throws CreationException
307 {
308 CacheEntry entry = null;
309 Object cacheObject = cache.get(key);
310
311 if (cacheObject != null)
312 {
313 if (cacheObject instanceof Latch)
314 {
315 try
316 {
317 ((Latch) cacheObject).acquire();
318 }
319 catch (InterruptedException e)
320 {
321 throw new CreationException(e);
322 }
323
324 entry = findCacheEntry(key);
325 }
326 else
327 {
328 entry = (CacheEntry) cacheObject;
329 }
330 }
331
332 return entry;
333 }
334
335 public void put(Object key, Object value)
336 {
337 CacheEntry entry = new CacheEntry(key, value);
338 entry.setCache(this);
339 entry = prepareEntry(entry);
340 cache.put(key, entry);
341 }
342
343 /***
344 * Clears the cache
345 */
346 public void clear()
347 {
348 cache.clear();
349 }
350
351 /***
352 * Returns the size of the cache
353 *
354 * @return int size of the cache
355 */
356 public int size()
357 {
358 return cache.size();
359 }
360 }