Clover coverage report -
Coverage timestamp: Sat Apr 30 2005 21:58:28 PDT
file stats: LOC: 418   Methods: 16
NCLOC: 159   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
AbstractCacheAdministrator.java 35.7% 46.5% 68.8% 47%
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.8.2.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  80
     protected AbstractCacheAdministrator(Properties p) {
 156  80
         loadProps(p);
 157  80
         initCacheParameters();
 158   
 
 159  80
         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  92
     public boolean isBlocking() {
 184  92
         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  0
     protected void setCacheCapacity(int newCacheCapacity) {
 195  0
         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  92
     public boolean isMemoryCaching() {
 206  92
         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  332
     public String getProperty(String key) {
 216  332
         return config.getProperty(key);
 217   
     }
 218   
 
 219   
     /**
 220   
      * Indicates whether the unlimited disk cache is enabled or not.
 221   
      */
 222  92
     public boolean isUnlimitedDiskCache() {
 223  92
         return unlimitedDiskCache;
 224   
     }
 225   
 
 226   
     /**
 227   
      * Check if we use overflowPersistence
 228   
      *
 229   
      * @return Returns the overflowPersistence.
 230   
      */
 231  92
     public boolean isOverflowPersistence() {
 232  92
         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  80
     protected Cache configureStandardListeners(Cache cache) {
 316  80
         if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
 317  54
             cache = setPersistenceListener(cache);
 318   
         }
 319   
 
 320  80
         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  80
         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  80
     private void initCacheParameters() {
 387  80
         algorithmClass = getProperty(CACHE_ALGORITHM_KEY);
 388   
 
 389  80
         blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));
 390   
 
 391  80
         String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);
 392   
 
 393  80
         if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
 394  18
             memoryCaching = false;
 395   
         }
 396   
 
 397  80
         unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
 398  80
         overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue();
 399   
 
 400  80
         String cacheSize = getProperty(CACHE_CAPACITY_KEY);
 401   
 
 402  80
         try {
 403  80
             if ((cacheSize != null) && (cacheSize.length() > 0)) {
 404  36
                 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  80
     private void loadProps(Properties p) {
 415  80
         config = new Config(p);
 416   
     }
 417   
 }
 418