Clover coverage report - Coverage timestamp: Sa Jul 7 2007 09:11:40 CEST FRAMES NO FRAMES |
file stats: | LOC: | 413 |  | Methods: | 16
| NCLOC: | 157 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
AbstractCacheAdministrator.java | 71,4% | 70% | 81,2% | 71,9% |

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;
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: 425 $
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 {
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 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 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 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 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 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 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 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 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 listenerList = new EventListenerList();
103 |  |
104 |  | /**
105 |  | * The algorithm class being used, as specified by the {@link #CACHE_ALGORITHM_KEY}
106 |  | * configuration property.
107 |  | */
108 |  | protected String 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 | 0 | protected AbstractCacheAdministrator() {
147 | 0 | this(null);
148 |  | }
149 |  |
150 |  | /**
151 |  | * Create the AbstractCacheAdministrator.
152 |  | *
153 |  | * @param p the configuration properties for this cache.
154 |  | */
155 | 195 | protected AbstractCacheAdministrator(Properties p) {
156 | 195 | loadProps(p);
157 | 195 | initCacheParameters();
158 |  |
159 | 195 | if (log.isDebugEnabled()) {
160 | 0 | 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 | 0 | public void setAlgorithmClass(String newAlgorithmClass) {
174 | 0 | 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 | 225 | public boolean isBlocking() {
184 | 225 | 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 | 20 | protected void setCacheCapacity(int newCacheCapacity) {
195 | 20 | 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 | 235 | public boolean isMemoryCaching() {
206 | 235 | 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 | 795 | public String getProperty(String key) {
216 | 795 | return config.getProperty(key);
217 |  | }
218 |  |
219 |  | /**
220 |  | * Indicates whether the unlimited disk cache is enabled or not.
221 |  | */
222 | 235 | public boolean isUnlimitedDiskCache() {
223 | 235 | return unlimitedDiskCache;
224 |  | }
225 |  |
226 |  | /**
227 |  | * Check if we use overflowPersistence
228 |  | *
229 |  | * @return Returns the overflowPersistence.
230 |  | */
231 | 235 | public boolean isOverflowPersistence() {
232 | 235 | return this.overflowPersistence;
233 |  | }
234 |  |
235 |  | /**
236 |  | * Sets the overflowPersistence flag
237 |  | *
238 |  | * @param overflowPersistence The overflowPersistence to set.
239 |  | */
240 | 0 | public void setOverflowPersistence(boolean overflowPersistence) {
241 | 0 | 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 | 29 | protected CacheEventListener[] getCacheEventListeners() {
249 | 29 | List classes = StringUtil.split(config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY), ',');
250 | 29 | CacheEventListener[] listeners = new CacheEventListener[classes.size()];
251 |  |
252 | 29 | for (int i = 0; i < classes.size(); i++) {
253 | 29 | String className = (String) classes.get(i);
254 |  |
255 | 29 | try {
256 | 29 | Class clazz = Class.forName(className);
257 |  |
258 | 29 | if (!CacheEventListener.class.isAssignableFrom(clazz)) {
259 | 0 | log.error("Specified listener class '" + className + "' does not implement CacheEventListener. Ignoring this listener.");
260 |  | } else {
261 | 29 | listeners[i] = (CacheEventListener) clazz.newInstance();
262 |  | }
263 |  | } catch (ClassNotFoundException e) {
264 | 0 | log.error("CacheEventListener class '" + className + "' not found. Ignoring this listener.", e);
265 |  | } catch (InstantiationException e) {
266 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not a concrete class. Ignoring this listener.", e);
267 |  | } catch (IllegalAccessException e) {
268 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not public. Ignoring this listener.", e);
269 |  | }
270 |  | }
271 |  |
272 | 29 | return listeners;
273 |  | }
274 |  |
275 |  | /**
276 |  | * If there is a <code>PersistenceListener</code> in the configuration
277 |  | * it will be instantiated and applied to the given cache object. If the
278 |  | * <code>PersistenceListener</code> cannot be found or instantiated, an
279 |  | * error will be logged but the cache will not have a persistence listener
280 |  | * applied to it and no exception will be thrown.<p>
281 |  | *
282 |  | * A cache can only have one <code>PersistenceListener</code>.
283 |  | *
284 |  | * @param cache the cache to apply the <code>PersistenceListener</code> to.
285 |  | *
286 |  | * @return the same cache object that was passed in.
287 |  | */
288 | 116 | protected Cache setPersistenceListener(Cache cache) {
289 | 116 | String persistenceClassname = config.getProperty(PERSISTENCE_CLASS_KEY);
290 |  |
291 | 116 | try {
292 | 116 | Class clazz = Class.forName(persistenceClassname);
293 | 116 | PersistenceListener persistenceListener = (PersistenceListener) clazz.newInstance();
294 |  |
295 | 116 | cache.setPersistenceListener(persistenceListener.configure(config));
296 |  | } catch (ClassNotFoundException e) {
297 | 0 | log.error("PersistenceListener class '" + persistenceClassname + "' not found. Check your configuration.", e);
298 |  | } catch (Exception e) {
299 | 0 | log.error("Error instantiating class '" + persistenceClassname + "'", e);
300 |  | }
301 |  |
302 | 116 | return cache;
303 |  | }
304 |  |
305 |  | /**
306 |  | * Applies all of the recognised listener classes to the supplied
307 |  | * cache object. Recognised classes are {@link CacheEntryEventListener}
308 |  | * and {@link CacheMapAccessEventListener}.<p>
309 |  | *
310 |  | * @param cache The cache to apply the configuration to.
311 |  | * @return cache The configured cache object.
312 |  | */
313 | 195 | protected Cache configureStandardListeners(Cache cache) {
314 | 195 | if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
315 | 116 | cache = setPersistenceListener(cache);
316 |  | }
317 |  |
318 | 195 | if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) {
319 |  | // Grab all the specified listeners and add them to the cache's
320 |  | // listener list. Note that listeners that implement more than
321 |  | // one of the event interfaces will be added multiple times.
322 | 29 | CacheEventListener[] listeners = getCacheEventListeners();
323 |  |
324 | 29 | for (int i = 0; i < listeners.length; i++) {
325 |  | // Pass through the configuration to those listeners that require it
326 | 29 | if (listeners[i] instanceof LifecycleAware) {
327 | 0 | try {
328 | 0 | ((LifecycleAware) listeners[i]).initialize(cache, config);
329 |  | } catch (InitializationException e) {
330 | 0 | log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e);
331 |  |
332 | 0 | continue;
333 |  | }
334 |  | }
335 |  |
336 | 29 | if (listeners[i] instanceof CacheEntryEventListener) {
337 | 29 | cache.addCacheEventListener(listeners[i]);
338 | 0 | } else if (listeners[i] instanceof CacheMapAccessEventListener) {
339 | 0 | cache.addCacheEventListener(listeners[i]);
340 |  | }
341 |  | }
342 |  | }
343 |  |
344 | 195 | return cache;
345 |  | }
346 |  |
347 |  | /**
348 |  | * Finalizes all the listeners that are associated with the given cache object.
349 |  | * Any <code>FinalizationException</code>s that are thrown by the listeners will
350 |  | * be caught and logged.
351 |  | */
352 | 10 | protected void finalizeListeners(Cache cache) {
353 |  | // It's possible for cache to be null if getCache() was never called (CACHE-63)
354 | 10 | if (cache == null) {
355 | 0 | return;
356 |  | }
357 |  |
358 | 10 | Object[] listeners = cache.listenerList.getListenerList();
359 |  |
360 | 10 | for (int i = listeners.length - 2; i >= 0; i -= 2) {
361 | 1 | if (listeners[i + 1] instanceof LifecycleAware) {
362 | 0 | try {
363 | 0 | ((LifecycleAware) listeners[i + 1]).finialize();
364 |  | } catch (FinalizationException e) {
365 | 0 | log.error("Listener could not be finalized", e);
366 |  | }
367 |  | }
368 |  | }
369 |  | }
370 |  |
371 |  | /**
372 |  | * Initialize the core cache parameters from the configuration properties.
373 |  | * The parameters that are initialized are:
374 |  | * <ul>
375 |  | * <li>the algorithm class ({@link #CACHE_ALGORITHM_KEY})</li>
376 |  | * <li>the cache size ({@link #CACHE_CAPACITY_KEY})</li>
377 |  | * <li>whether the cache is blocking or non-blocking ({@link #CACHE_BLOCKING_KEY})</li>
378 |  | * <li>whether caching to memory is enabled ({@link #CACHE_MEMORY_KEY})</li>
379 |  | * <li>whether the persistent cache is unlimited in size ({@link #CACHE_DISK_UNLIMITED_KEY})</li>
380 |  | * </ul>
381 |  | */
382 | 195 | private void initCacheParameters() {
383 | 195 | algorithmClass = getProperty(CACHE_ALGORITHM_KEY);
384 |  |
385 | 195 | blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));
386 |  |
387 | 195 | String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);
388 |  |
389 | 195 | if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
390 | 58 | memoryCaching = false;
391 |  | }
392 |  |
393 | 195 | unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
394 | 195 | overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue();
395 |  |
396 | 195 | String cacheSize = getProperty(CACHE_CAPACITY_KEY);
397 |  |
398 | 195 | try {
399 | 195 | if ((cacheSize != null) && (cacheSize.length() > 0)) {
400 | 88 | cacheCapacity = Integer.parseInt(cacheSize);
401 |  | }
402 |  | } catch (NumberFormatException e) {
403 | 0 | log.error("The value supplied for the cache capacity, '" + cacheSize + "', is not a valid number. The cache capacity setting is being ignored.");
404 |  | }
405 |  | }
406 |  |
407 |  | /**
408 |  | * Load the properties file from the classpath.
409 |  | */
410 | 195 | private void loadProps(Properties p) {
411 | 195 | config = new Config(p);
412 |  | }
413 |  | }