KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > oscache > base > AbstractCacheAdministrator


1 /*
2  * Copyright (c) 2002-2003 by OpenSymphony
3  * All rights reserved.
4  */

5 package com.opensymphony.oscache.base;
6
7 import com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache;
8 import com.opensymphony.oscache.base.events.*;
9 import com.opensymphony.oscache.base.persistence.PersistenceListener;
10 import com.opensymphony.oscache.util.StringUtil;
11
12 import org.apache.commons.logging.Log;
13 import org.apache.commons.logging.LogFactory;
14
15 import java.util.*;
16
17 import javax.swing.event.EventListenerList JavaDoc;
18
19 /**
20  * An AbstractCacheAdministrator defines an abstract cache administrator, implementing all
21  * the basic operations related to the configuration of a cache, including assigning
22  * any configured event handlers to cache objects.<p>
23  *
24  * Extend this class to implement a custom cache administrator.
25  *
26  * @version $Revision: 1.1 $
27  * @author a HREF="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
28  * @author <a HREF="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
29  * @author <a HREF="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
30  * @author <a HREF="mailto:fabian.crabus@gurulogic.de">Fabian Crabus</a>
31  * @author <a HREF="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
32  */

33 public abstract class AbstractCacheAdministrator implements java.io.Serializable JavaDoc {
34     private static transient final Log log = LogFactory.getLog(AbstractCacheAdministrator.class);
35
36     /**
37      * A boolean cache configuration property that indicates whether the cache
38      * should cache objects in memory. Set this property to <code>false</code>
39      * to disable in-memory caching.
40      */

41     public final static String JavaDoc CACHE_MEMORY_KEY = "cache.memory";
42
43     /**
44      * An integer cache configuration property that specifies the maximum number
45      * of objects to hold in the cache. Setting this to a negative value will
46      * disable the capacity functionality - there will be no limit to the number
47      * of objects that are held in cache.
48      */

49     public final static String JavaDoc CACHE_CAPACITY_KEY = "cache.capacity";
50
51     /**
52      * A String cache configuration property that specifies the classname of
53      * an alternate caching algorithm. This class must extend
54      * {@link com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache}
55      * By default caches will use {@link com.opensymphony.oscache.base.algorithm.LRUCache} as
56      * the default algorithm if the cache capacity is set to a postive value, or
57      * {@link com.opensymphony.oscache.base.algorithm.UnlimitedCache} if the
58      * capacity is negative (ie, disabled).
59      */

60     public final static String JavaDoc CACHE_ALGORITHM_KEY = "cache.algorithm";
61
62     /**
63      * A boolean cache configuration property that indicates whether the persistent
64      * cache should be unlimited in size, or should be restricted to the same size
65      * as the in-memory cache. Set this property to <code>true</code> to allow the
66      * persistent cache to grow without bound.
67      */

68     public final static String JavaDoc CACHE_DISK_UNLIMITED_KEY = "cache.unlimited.disk";
69
70     /**
71      * The configuration key that specifies whether we should block waiting for new
72      * content to be generated, or just serve the old content instead. The default
73      * behaviour is to serve the old content since that provides the best performance
74      * (at the cost of serving slightly stale data).
75      */

76     public final static String JavaDoc CACHE_BLOCKING_KEY = "cache.blocking";
77
78     /**
79      * A String cache configuration property that specifies the classname that will
80      * be used to provide cache persistence. This class must extend {@link PersistenceListener}.
81      */

82     public static final String JavaDoc PERSISTENCE_CLASS_KEY = "cache.persistence.class";
83
84     /**
85      * A String cache configuration property that specifies if the cache persistence
86      * will only be used in overflow mode, that is, when the memory cache capacity has been reached.
87      */

88     public static final String JavaDoc CACHE_PERSISTENCE_OVERFLOW_KEY = "cache.persistence.overflow.only";
89
90     /**
91      * A String cache configuration property that holds a comma-delimited list of
92      * classnames. These classes specify the event handlers that are to be applied
93      * to the cache.
94      */

95     public static final String JavaDoc CACHE_ENTRY_EVENT_LISTENERS_KEY = "cache.event.listeners";
96     protected Config config = null;
97
98     /**
99      * Holds a list of all the registered event listeners. Event listeners are specified
100      * using the {@link #CACHE_ENTRY_EVENT_LISTENERS_KEY} configuration key.
101      */

102     protected EventListenerList JavaDoc listenerList = new EventListenerList JavaDoc();
103
104     /**
105      * The algorithm class being used, as specified by the {@link #CACHE_ALGORITHM_KEY}
106      * configuration property.
107      */

108     protected String JavaDoc algorithmClass = null;
109
110     /**
111      * The cache capacity (number of entries), as specified by the {@link #CACHE_CAPACITY_KEY}
112      * configuration property.
113      */

114     protected int cacheCapacity = -1;
115
116     /**
117      * Whether the cache blocks waiting for content to be build, or serves stale
118      * content instead. This value can be specified using the {@link #CACHE_BLOCKING_KEY}
119      * configuration property.
120      */

121     private boolean blocking = false;
122
123     /**
124      * Whether or not to store the cache entries in memory. This is configurable using the
125      * {@link com.opensymphony.oscache.base.AbstractCacheAdministrator#CACHE_MEMORY_KEY} property.
126      */

127     private boolean memoryCaching = true;
128
129     /**
130      * Whether the persistent cache should be used immediately or only when the memory capacity
131          * has been reached, ie. overflow only.
132      * This can be set via the {@link #CACHE_PERSISTENCE_OVERFLOW_KEY} configuration property.
133      */

134     private boolean overflowPersistence;
135
136     /**
137      * Whether the disk cache should be unlimited in size, or matched 1-1 to the memory cache.
138      * This can be set via the {@link #CACHE_DISK_UNLIMITED_KEY} configuration property.
139      */

140     private boolean unlimitedDiskCache;
141
142     /**
143      * Create the AbstractCacheAdministrator.
144      * This will initialize all values and load the properties from oscache.properties.
145      */

146     protected AbstractCacheAdministrator() {
147         this(null);
148     }
149
150     /**
151      * Create the AbstractCacheAdministrator.
152      *
153      * @param p the configuration properties for this cache.
154      */

155     protected AbstractCacheAdministrator(Properties p) {
156         loadProps(p);
157         initCacheParameters();
158
159         if (log.isDebugEnabled()) {
160             log.debug("Constructed AbstractCacheAdministrator()");
161         }
162     }
163
164     /**
165      * Sets the algorithm to use for the cache.
166      *
167      * @see com.opensymphony.oscache.base.algorithm.LRUCache
168      * @see com.opensymphony.oscache.base.algorithm.FIFOCache
169      * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache
170      * @param newAlgorithmClass The class to use (eg.
171      * <code>"com.opensymphony.oscache.base.algorithm.LRUCache"</code>)
172      */

173     public void setAlgorithmClass(String JavaDoc newAlgorithmClass) {
174         algorithmClass = newAlgorithmClass;
175     }
176
177     /**
178      * Indicates whether the cache will block waiting for new content to
179      * be built, or serve stale content instead of waiting. Regardless of this
180      * setting, the cache will <em>always</em> block if new content is being
181      * created, ie, there's no stale content in the cache that can be served.
182      */

183     public boolean isBlocking() {
184         return blocking;
185     }
186
187     /**
188      * Sets the cache capacity (number of items). Administrator implementations
189      * should override this method to ensure that their {@link Cache} objects
190      * are updated correctly (by calling {@link AbstractConcurrentReadCache#setMaxEntries(int)}}}.
191      *
192      * @param newCacheCapacity The new capacity
193      */

194     protected void setCacheCapacity(int newCacheCapacity) {
195         cacheCapacity = newCacheCapacity;
196     }
197
198     /**
199      * Whether entries are cached in memory or not.
200      * Default is true.
201      * Set by the <code>cache.memory</code> property.
202      *
203      * @return Status whether or not memory caching is used.
204      */

205     public boolean isMemoryCaching() {
206         return memoryCaching;
207     }
208
209     /**
210      * Retrieves the value of one of the configuration properties.
211      *
212      * @param key The key assigned to the property
213      * @return Property value, or <code>null</code> if the property could not be found.
214      */

215     public String JavaDoc getProperty(String JavaDoc key) {
216         return config.getProperty(key);
217     }
218
219     /**
220      * Indicates whether the unlimited disk cache is enabled or not.
221      */

222     public boolean isUnlimitedDiskCache() {
223         return unlimitedDiskCache;
224     }
225
226     /**
227      * Check if we use overflowPersistence
228      *
229      * @return Returns the overflowPersistence.
230      */

231     public boolean isOverflowPersistence() {
232         return this.overflowPersistence;
233     }
234
235     /**
236      * Sets the overflowPersistence flag
237      *
238      * @param overflowPersistence The overflowPersistence to set.
239      */

240     public void setOverflowPersistence(boolean overflowPersistence) {
241         this.overflowPersistence = overflowPersistence;
242     }
243
244     /**
245      * Retrieves an array containing instances all of the {@link CacheEventListener}
246      * classes that are specified in the OSCache configuration file.
247      */

248     protected CacheEventListener[] getCacheEventListeners() {
249         CacheEventListener[] listeners = null;
250
251         List JavaDoc classes = StringUtil.split(config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY), ',');
252         listeners = new CacheEventListener[classes.size()];
253
254         for (int i = 0; i < classes.size(); i++) {
255             String JavaDoc className = (String JavaDoc) classes.get(i);
256
257             try {
258                 Class JavaDoc clazz = Class.forName(className);
259
260                 if (!CacheEventListener.class.isAssignableFrom(clazz)) {
261                     log.error("Specified listener class '" + className + "' does not implement CacheEventListener. Ignoring this listener.");
262                 } else {
263                     listeners[i] = (CacheEventListener) clazz.newInstance();
264                 }
265             } catch (ClassNotFoundException JavaDoc e) {
266                 log.error("CacheEventListener class '" + className + "' not found. Ignoring this listener.", e);
267             } catch (InstantiationException JavaDoc e) {
268                 log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not a concrete class. Ignoring this listener.", e);
269             } catch (IllegalAccessException JavaDoc e) {
270                 log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not public. Ignoring this listener.", e);
271             }
272         }
273
274         return listeners;
275     }
276
277     /**
278      * If there is a <code>PersistenceListener</code> in the configuration
279      * it will be instantiated and applied to the given cache object. If the
280      * <code>PersistenceListener</code> cannot be found or instantiated, an
281      * error will be logged but the cache will not have a persistence listener
282      * applied to it and no exception will be thrown.<p>
283      *
284      * A cache can only have one <code>PersistenceListener</code>.
285      *
286      * @param cache the cache to apply the <code>PersistenceListener</code> to.
287      *
288      * @return the same cache object that was passed in.
289      */

290     protected Cache setPersistenceListener(Cache cache) {
291         String JavaDoc persistenceClassname = config.getProperty(PERSISTENCE_CLASS_KEY);
292
293         try {
294             Class JavaDoc clazz = Class.forName(persistenceClassname);
295             PersistenceListener persistenceListener = (PersistenceListener) clazz.newInstance();
296
297             cache.setPersistenceListener(persistenceListener.configure(config));
298         } catch (ClassNotFoundException JavaDoc e) {
299             log.error("PersistenceListener class '" + persistenceClassname + "' not found. Check your configuration.", e);
300         } catch (Exception JavaDoc e) {
301             log.error("Error instantiating class '" + persistenceClassname + "'", e);
302         }
303
304         return cache;
305     }
306
307     /**
308      * Applies all of the recognised listener classes to the supplied
309      * cache object. Recognised classes are {@link CacheEntryEventListener}
310      * and {@link CacheMapAccessEventListener}.<p>
311      *
312      * @param cache The cache to apply the configuration to.
313      * @return cache The configured cache object.
314      */

315     protected Cache configureStandardListeners(Cache cache) {
316         if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
317             cache = setPersistenceListener(cache);
318         }
319
320         if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) {
321             // Grab all the specified listeners and add them to the cache's
322
// listener list. Note that listeners that implement more than
323
// one of the event interfaces will be added multiple times.
324
CacheEventListener[] listeners = getCacheEventListeners();
325
326             for (int i = 0; i < listeners.length; i++) {
327                 // Pass through the configuration to those listeners that require it
328
if (listeners[i] instanceof LifecycleAware) {
329                     try {
330                         ((LifecycleAware) listeners[i]).initialize(cache, config);
331                     } catch (InitializationException e) {
332                         log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e);
333
334                         continue;
335                     }
336                 }
337
338                 if (listeners[i] instanceof CacheEntryEventListener) {
339                     cache.addCacheEventListener(listeners[i], CacheEntryEventListener.class);
340                 }
341
342                 if (listeners[i] instanceof CacheMapAccessEventListener) {
343                     cache.addCacheEventListener(listeners[i], CacheMapAccessEventListener.class);
344                 }
345             }
346         }
347
348         return cache;
349     }
350
351     /**
352      * Finalizes all the listeners that are associated with the given cache object.
353      * Any <code>FinalizationException</code>s that are thrown by the listeners will
354      * be caught and logged.
355      */

356     protected void finalizeListeners(Cache cache) {
357         // It's possible for cache to be null if getCache() was never called (CACHE-63)
358
if (cache == null) {
359             return;
360         }
361
362         Object JavaDoc[] listeners = cache.listenerList.getListenerList();
363
364         for (int i = listeners.length - 2; i >= 0; i -= 2) {
365             if (listeners[i + 1] instanceof LifecycleAware) {
366                 try {
367                     ((LifecycleAware) listeners[i + 1]).finialize();
368                 } catch (FinalizationException e) {
369                     log.error("Listener could not be finalized", e);
370                 }
371             }
372         }
373     }
374
375     /**
376      * Initialize the core cache parameters from the configuration properties.
377      * The parameters that are initialized are:
378      * <ul>
379      * <li>the algorithm class ({@link #CACHE_ALGORITHM_KEY})</li>
380      * <li>the cache size ({@link #CACHE_CAPACITY_KEY})</li>
381      * <li>whether the cache is blocking or non-blocking ({@link #CACHE_BLOCKING_KEY})</li>
382      * <li>whether caching to memory is enabled ({@link #CACHE_MEMORY_KEY})</li>
383      * <li>whether the persistent cache is unlimited in size ({@link #CACHE_DISK_UNLIMITED_KEY})</li>
384      * </ul>
385      */

386     private void initCacheParameters() {
387         algorithmClass = getProperty(CACHE_ALGORITHM_KEY);
388
389         blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));
390
391         String JavaDoc cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);
392
393         if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
394             memoryCaching = false;
395         }
396
397         unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
398         overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue();
399
400         String JavaDoc cacheSize = getProperty(CACHE_CAPACITY_KEY);
401
402         try {
403             if ((cacheSize != null) && (cacheSize.length() > 0)) {
404                 cacheCapacity = Integer.parseInt(cacheSize);
405             }
406         } catch (NumberFormatException JavaDoc e) {
407             log.error("The value supplied for the cache capacity, '" + cacheSize + "', is not a valid number. The cache capacity setting is being ignored.");
408         }
409     }
410
411     /**
412      * Load the properties file from the classpath.
413      */

414     private void loadProps(Properties p) {
415         config = new Config(p);
416     }
417 }
418
Popular Tags