Clover coverage report -
Coverage timestamp: So Nov 6 2005 14:19:51 CET
file stats: LOC: 417   Methods: 16
NCLOC: 159   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
AbstractCacheAdministrator.java 35,7% 47,9% 75% 48,7%
coverage coverage
 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: 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 {
 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  96 protected AbstractCacheAdministrator(Properties p) {
 156  96 loadProps(p);
 157  96 initCacheParameters();
 158   
 159  96 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  108 public boolean isBlocking() {
 184  108 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  16 protected void setCacheCapacity(int newCacheCapacity) {
 195  16 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  108 public boolean isMemoryCaching() {
 206  108 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  396 public String getProperty(String key) {
 216  396 return config.getProperty(key);
 217    }
 218   
 219    /**
 220    * Indicates whether the unlimited disk cache is enabled or not.
 221    */
 222  108 public boolean isUnlimitedDiskCache() {
 223  108 return unlimitedDiskCache;
 224    }
 225   
 226    /**
 227    * Check if we use overflowPersistence
 228    *
 229    * @return Returns the overflowPersistence.
 230    */
 231  108 public boolean isOverflowPersistence() {
 232  108 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  0 protected CacheEventListener[] getCacheEventListeners() {
 249  0 CacheEventListener[] listeners = null;
 250   
 251  0 List classes = StringUtil.split(config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY), ',');
 252  0 listeners = new CacheEventListener[classes.size()];
 253   
 254  0 for (int i = 0; i < classes.size(); i++) {
 255  0 String className = (String) classes.get(i);
 256   
 257  0 try {
 258  0 Class clazz = Class.forName(className);
 259   
 260  0 if (!CacheEventListener.class.isAssignableFrom(clazz)) {
 261  0 log.error("Specified listener class '" + className + "' does not implement CacheEventListener. Ignoring this listener.");
 262    } else {
 263  0 listeners[i] = (CacheEventListener) clazz.newInstance();
 264    }
 265    } catch (ClassNotFoundException e) {
 266  0 log.error("CacheEventListener class '" + className + "' not found. Ignoring this listener.", e);
 267    } catch (InstantiationException e) {
 268  0 log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not a concrete class. Ignoring this listener.", e);
 269    } catch (IllegalAccessException e) {
 270  0 log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not public. Ignoring this listener.", e);
 271    }
 272    }
 273   
 274  0 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  54 protected Cache setPersistenceListener(Cache cache) {
 291  54 String persistenceClassname = config.getProperty(PERSISTENCE_CLASS_KEY);
 292   
 293  54 try {
 294  54 Class clazz = Class.forName(persistenceClassname);
 295  54 PersistenceListener persistenceListener = (PersistenceListener) clazz.newInstance();
 296   
 297  54 cache.setPersistenceListener(persistenceListener.configure(config));
 298    } catch (ClassNotFoundException e) {
 299  0 log.error("PersistenceListener class '" + persistenceClassname + "' not found. Check your configuration.", e);
 300    } catch (Exception e) {
 301  0 log.error("Error instantiating class '" + persistenceClassname + "'", e);
 302    }
 303   
 304  54 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  96 protected Cache configureStandardListeners(Cache cache) {
 316  96 if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
 317  54 cache = setPersistenceListener(cache);
 318    }
 319   
 320  96 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  0 CacheEventListener[] listeners = getCacheEventListeners();
 325   
 326  0 for (int i = 0; i < listeners.length; i++) {
 327    // Pass through the configuration to those listeners that require it
 328  0 if (listeners[i] instanceof LifecycleAware) {
 329  0 try {
 330  0 ((LifecycleAware) listeners[i]).initialize(cache, config);
 331    } catch (InitializationException e) {
 332  0 log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e);
 333   
 334  0 continue;
 335    }
 336    }
 337   
 338  0 if (listeners[i] instanceof CacheEntryEventListener) {
 339  0 cache.addCacheEventListener(listeners[i], CacheEntryEventListener.class);
 340    }
 341   
 342  0 if (listeners[i] instanceof CacheMapAccessEventListener) {
 343  0 cache.addCacheEventListener(listeners[i], CacheMapAccessEventListener.class);
 344    }
 345    }
 346    }
 347   
 348  96 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  4 protected void finalizeListeners(Cache cache) {
 357    // It's possible for cache to be null if getCache() was never called (CACHE-63)
 358  4 if (cache == null) {
 359  0 return;
 360    }
 361   
 362  4 Object[] listeners = cache.listenerList.getListenerList();
 363   
 364  4 for (int i = listeners.length - 2; i >= 0; i -= 2) {
 365  0 if (listeners[i + 1] instanceof LifecycleAware) {
 366  0 try {
 367  0 ((LifecycleAware) listeners[i + 1]).finialize();
 368    } catch (FinalizationException e) {
 369  0 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  96 private void initCacheParameters() {
 387  96 algorithmClass = getProperty(CACHE_ALGORITHM_KEY);
 388   
 389  96 blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));
 390   
 391  96 String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);
 392   
 393  96 if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
 394  18 memoryCaching = false;
 395    }
 396   
 397  96 unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
 398  96 overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue();
 399   
 400  96 String cacheSize = getProperty(CACHE_CAPACITY_KEY);
 401   
 402  96 try {
 403  96 if ((cacheSize != null) && (cacheSize.length() > 0)) {
 404  52 cacheCapacity = Integer.parseInt(cacheSize);
 405    }
 406    } catch (NumberFormatException e) {
 407  0 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  96 private void loadProps(Properties p) {
 415  96 config = new Config(p);
 416    }
 417    }