KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openharmonise > commons > cache > AbstractCache


1 /*
2  * The contents of this file are subject to the
3  * Mozilla Public License Version 1.1 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
9  * See the License for the specific language governing rights and
10  * limitations under the License.
11  *
12  * The Initial Developer of the Original Code is Simulacra Media Ltd.
13  * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
14  *
15  * All Rights Reserved.
16  *
17  * Contributor(s):
18  */

19 package org.openharmonise.commons.cache;
20
21 import java.util.*;
22 import java.util.logging.*;
23
24 import EDU.oswego.cs.dl.util.concurrent.*;
25
26 /**
27  * Abstract class providing the base functionality for object caching. The
28  * cache has a maximum size limit and will page out as required if that limit
29  * is reached, paging out in descending order of time since last access. If the
30  * memory limit of the machine is reached before the cache pages our, the cache
31  * will clear and reduce all the cache sizes by a given ratio.
32  *
33  *
34  * @author Michael Bell
35  * @author John King
36  * @version $Revision: 1.2 $
37  *
38  */

39 public abstract class AbstractCache {
40         
41     /**
42      * Default cache size.
43      */

44     public static final int DEFAULT_CACHESIZE = 3500;
45     
46     /**
47      * Constant indicating a change based on a save action.
48      */

49     public static final String JavaDoc CHANGE_SAVE = "save";
50     
51     /**
52      * Constant indicating a change based on a delete action.
53      */

54     public static final String JavaDoc CHANGE_DELETE = "delete";
55     
56     /**
57      * Constant indicating a change based on an update action.
58      */

59     public static final String JavaDoc CHANGE_UPDATE = "update";
60    
61     /**
62      * Constant indicating a change based on an temporary action.
63      */

64     public static final String JavaDoc CHANGE_TEMP = "temp";
65     
66     /**
67      * Default percentage of the available memory that needs to be reached before
68      * the cache sizes are automatically reduced.
69      */

70     public static final double DEFAULT_CACHELIMIT_PERCENT = 0.9;
71     
72     /**
73      * Default percentage that cache sizes are reduced by when the memory limit
74      * has been reached.
75      */

76     public static final double DEFAULT_CACHEREDUCTION_RATIO = 0.95;
77     
78     /**
79      * List of caches which which retain their size if the memory limit is reached.
80      */

81     private static List m_protected_caches = null;
82     
83     /**
84      * Static Map containing all caches.
85      */

86     private static Map m_all_caches;
87     
88     /**
89      * Logger for this class.
90      */

91     private static Logger m_logger = Logger.getLogger(AbstractCache.class.getName());
92
93     static {
94         //initialise cache map
95
m_all_caches = Collections.synchronizedMap(new HashMap());
96     }
97
98     /**
99      * Name of current cache.
100      */

101     private String JavaDoc m_cache_name;
102     
103     /**
104      * Map containing all cached objects.
105      */

106     private LRUCacheMap m_cache;
107
108     /**
109      * Current cache size limit.
110      */

111     private int m_max_cache_size;
112     
113     /**
114      * Cache size limit at beginning of cache life.
115      */

116     private int m_init_max_cache_size;
117
118     /**
119      * Estimate of avarage object size.
120      */

121     private long m_total_object_size_estimate = 0;
122     
123     /**
124      * Current heap size.
125      */

126     private int m_heap_size = -1; //
127

128     
129     /**
130      * Read-write lock to enable locking of cache access.
131      */

132     private ReadWriteLock lock = new WriterPreferenceReadWriteLock();
133
134     /**
135      * Creates new cache with given parameters.
136      *
137      * @param cache_name the name of cache
138      * @param cache_size the initial size of cache
139      * @param cache_pagesize the paging size
140      * @throws CacheException if an error occurs in object construction
141      */

142     protected AbstractCache(String JavaDoc cache_name, int cache_size) throws CacheException {
143         m_cache_name = cache_name;
144         m_init_max_cache_size = cache_size;
145         m_max_cache_size = cache_size;
146         m_cache = new LRUCacheMap(cache_size);
147
148         m_all_caches.put(m_cache_name, this);
149     }
150
151     /**
152      * Creates cache with default parameters and the given name.
153      *
154      * @param cache_name the name of cache
155      * @throws CacheException if an error occurs in object construction
156      */

157     protected AbstractCache(String JavaDoc cache_name) throws CacheException {
158         this(cache_name, DEFAULT_CACHESIZE);
159
160         m_all_caches.put(m_cache_name, this);
161     }
162
163     /**
164      * Returns cache object matching the given cache key.
165      *
166      * @param key the cache key which corresponds to required cached object
167      * @return the object in cache corresponding to given cache key
168      * @throws CacheException if an error occurs accessing object in cache
169      * @throws InterruptedException
170      * @throws RuntimeException if object is not in cache
171      */

172     public Object JavaDoc getObject(final Object JavaDoc key) throws CacheException {
173         
174         // obtain read lock
175
Object JavaDoc cached_object = null;
176         try {
177             lock.readLock().acquire();
178             cached_object = m_cache.get(key);
179             lock.readLock().release();
180             if (cached_object == null) {
181                 cached_object = addToCache(key);
182             }
183             
184         } catch (InterruptedException JavaDoc e) {
185             throw new CacheException (e);
186         }
187         
188         return cached_object;
189     }
190     
191     /**
192      * Returns a pointer to the cached object referenced by the key.
193      *
194      * @param key the cache key which corresponds to required cached object
195      * @return a <code>CachePointer</code> referencing the cached object corresponding to given cache key
196      * @throws InterruptedException
197      * @throws CacheException
198      *
199      */

200     public CachePointer getObjectPointer(Object JavaDoc key) throws CacheException {
201         
202         //Call 'getObject' to make sure any exceptions which may get
203
//thrown when getting the object are thrown here
204
Object JavaDoc obj = getObject(key);
205         
206         return new CachePointer(key,this);
207     }
208
209     /**
210      * Adds an object to the cache which corresponds to the given cache key,
211      * calls <code>getCacheableObject</code> to get the object to cache.
212      *
213      * @param key the cache key which corresponds to required cached object
214      * @return the object which was existing in the cache for the given key,
215      * if there was already an object for the cache key.
216      *
217      * @throws RuntimeException if any exceptions are thrown during execution
218      * @throws CacheException note that the code will be changed to throw this
219      * exceptoin in the future rather than a <code>RuntimeException</code>
220      */

221     protected Object JavaDoc addToCache(Object JavaDoc key) throws CacheException {
222         Object JavaDoc cacheable_object = null;
223         try {
224
225             cacheable_object = getCacheableObject(key);
226             addToCache(key, cacheable_object);
227         } catch (java.lang.OutOfMemoryError JavaDoc e) {
228               // note if we get here it seems to be too late to recover
229
// white pages appear
230
AbstractCache.clearAllCaches();
231         
232               Runtime JavaDoc runtime = Runtime.getRuntime();
233         
234               String JavaDoc error_msg = (m_cache_name + " cache: " +
235                                  " Thrown OutOfMemoryError Exception" +
236                                  " Not enough memory to allocate cacheable object " +
237                                  ", free memory: " + runtime.freeMemory() +
238                                  ", total memory: " + runtime.totalMemory() +
239                                  ", probable needed memory: " +
240                                  getAvgObjectSizeOfEstimate());
241               
242               m_logger.log(Level.SEVERE, error_msg, e);
243           } catch (Exception JavaDoc e) {
244             throw new CacheException(e);
245         }
246         return cacheable_object;
247     }
248
249     /**
250      * Returns an estimate of the average size of an object in the cache.
251      *
252      * @return an estimate of the average size of an object in the cache
253      */

254     public long getAvgObjectSizeOfEstimate() {
255         // average, estimate only :)
256
return (m_cache.size() == 0)
257                ? 0 : Math.round(m_total_object_size_estimate / m_cache.size());
258     }
259
260     /**
261      * Adds the <code>cacheable_object</code> to the cache with the given
262      * cache key <code>key</code>.
263      *
264      * @param key the cache key
265      * @param cacheable_object the object to be cached
266      * @throws InterruptedException
267      *
268      * @throws RuntimeException if any exceptions are thrown during execution
269      */

270     public void addToCache(Object JavaDoc key, Object JavaDoc cacheable_object) {
271
272             
273             if(m_logger.getLevel() == Level.FINE) {
274                 StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
275                 sbuf.append(m_cache_name)
276                     .append(": cache size - ")
277                     .append( m_cache.size())
278                     .append(", m_max_cache_size - ")
279                     .append(m_max_cache_size);
280                 m_logger.log(Level.FINE, sbuf.toString());
281             }
282             
283             try {
284                 lock.writeLock().acquire();
285                 m_cache.put(key, cacheable_object);
286                 lock.writeLock().release();
287             } catch (InterruptedException JavaDoc e) {
288                 throw new RuntimeException JavaDoc(e);
289             }
290
291     }
292     
293     /**
294      * Removes object associated to the specified cache key from
295      * the cache.
296      *
297      * @param key the cache key
298      */

299     protected void removeObjectFromCache(Object JavaDoc key) {
300         try {
301             lock.writeLock().acquire();
302             m_cache.remove(key);
303             lock.writeLock().release();
304         } catch (InterruptedException JavaDoc e) {
305             m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
306         }
307     }
308     
309     /**
310      * Clears all caches in system that extend this abstract cache class.
311      *
312      */

313     static synchronized public void clearAllCaches() {
314
315         Iterator cache_iter = m_all_caches.values().iterator();
316
317         while (cache_iter.hasNext()) {
318             ((AbstractCache) cache_iter.next()).clearCache();
319         }
320     }
321
322     /**
323      * Clears this cache.
324      * @throws InterruptedException
325      *
326      */

327     public void clearCache() {
328         try {
329             lock.writeLock().acquire();
330             
331             m_cache.clear();
332             lock.writeLock().release();
333         } catch (InterruptedException JavaDoc e) {
334             throw new RuntimeException JavaDoc(e);
335         }
336     }
337
338     /**
339      * Returns the current cache size, taken from the <code>Map</code> that
340      * contains all objects.
341      *
342      * @return the size of the cache
343      */

344     public int getCacheSize() {
345         return m_cache.size();
346     }
347
348     /**
349      * Returns the name of the cache.
350      *
351      * @return the name of the cache
352      */

353     public String JavaDoc getCacheName() {
354         return this.m_cache_name;
355     }
356
357     
358
359     /**
360      * Returns the current maximum size of the cache.
361      *
362      * @return the current maximum size of the cache
363      */

364     public int getCacheMaxSize() {
365         return m_max_cache_size;
366     }
367
368     /**
369      * Sets the maximum cache size.
370      *
371      * @param maxSize the maximum size for the cache
372      */

373     public void setCacheMaxSize(int maxSize) {
374
375         try {
376             LRUCacheMap new_cache = new LRUCacheMap(maxSize);
377             lock.readLock().acquire();
378             int i=0;
379             for (Iterator iter = m_cache.keySet().iterator(); iter.hasNext() && i<maxSize;i++) {
380                 Object JavaDoc key = (Object JavaDoc) iter.next();
381                 new_cache.put(key, m_cache.get(key));
382             }
383             lock.readLock().release();
384             
385             lock.writeLock().acquire();
386             m_cache = new_cache;
387             lock.writeLock().release();
388         } catch (InterruptedException JavaDoc e) {
389             m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
390         }
391     }
392
393
394     /**
395      * Returns the list of keys for the most accessed objects of the size given.
396      *
397      * @param num_favourites the number of keys to return in list
398      *
399      * @return a list of the keys of the most accessed objects
400      */

401     public List getCacheTopKeys(int num_favourites) {
402         List toReturn = null;
403         
404         try {
405             lock.readLock().acquire();
406             toReturn = new Vector(m_cache.values()).subList(0,
407                     (m_cache.size() > num_favourites)
408                     ? num_favourites
409                     : m_cache.size());
410             lock.readLock().release();
411         } catch (InterruptedException JavaDoc e) {
412             m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
413         }
414         return toReturn;
415         
416     }
417
418     /**
419      * Returns a <code>Set</code> of cache keys.
420      *
421      * @return a <code>Set</code> of cache keys
422      */

423     public Set getCacheKeys() {
424         Set toReturn = null;
425         try {
426             lock.readLock().acquire();
427             toReturn = new HashSet(m_cache.keySet());
428             lock.readLock().release();
429         }
430         catch (InterruptedException JavaDoc e) {
431             m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
432         }
433         return toReturn;
434     }
435
436     /**
437      * Returns <code>true</code> if the given key is contained in the cache.
438      *
439      * @param cache_key a cache key
440      * @return <code>true</code> if this cache contains this key
441      * @throws InterruptedException
442      */

443     public boolean containsKey(Object JavaDoc cache_key) throws InterruptedException JavaDoc {
444         lock.readLock().acquire();
445         boolean containsKeyFlag = m_cache.containsKey(cache_key);
446         lock.readLock().release();
447         return containsKeyFlag;
448     }
449     
450     /**
451      * Sets whether this cache checks objects for dependancies before
452      * paging the object out. If, when checking for dependancies, the object is
453      * found to have dependances the object will not be paged out.
454      *
455      *
456      * @param bIsDepAware <code>true</code> if the cache should check dependancies
457      * of an object before paging it out of the cache.
458      */

459     public void setDependancyAware(boolean bIsDepAware) {
460         m_cache.setDependancyAware(bIsDepAware);
461     }
462     
463     /**
464      * Returns <code>true</code> if this cache will check an object for
465      * dependancies before paging the object out.
466      *
467      * @return
468      */

469     public boolean isDependancyAware() {
470         return m_cache.isDependancyAware();
471     }
472
473     /**
474      * Returns a cacheable object from the given key.
475      *
476      * @param key cache key for object
477      * @return Object to be cached
478      * @throws Exception if any error occurs
479      */

480     protected abstract Object JavaDoc getCacheableObject(Object JavaDoc key)
481                                           throws Exception JavaDoc;
482
483     /**
484      * Changes object in cache, depending on the change code <code>sChangeCode</code>
485      * the object will be deleted from the cache or replaced with the new object
486      * <code>newObject</code>.
487      *
488      * @param cache_key key of changed object
489      * @param sChangeCode code of change
490      * @param newObject new object which corresponds with the cache key
491      * @throws InterruptedException
492      */

493     public void changeObject(Object JavaDoc cache_key, String JavaDoc sChangeCode,
494                              Object JavaDoc newObject) {
495         
496         try {
497             lock.writeLock().acquire();
498             m_cache.remove(cache_key);
499             
500             if (sChangeCode.equalsIgnoreCase(CHANGE_SAVE) ||
501                     sChangeCode.equalsIgnoreCase(CHANGE_UPDATE)) {
502                 addToCache(cache_key, newObject);
503             }
504             lock.writeLock().release();
505         } catch (InterruptedException JavaDoc e) {
506             m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
507             throw new RuntimeException JavaDoc(e);
508         }
509     }
510
511 }
Popular Tags