Coverage report

  %line %branch
org.apache.turbine.services.cache.TurbineGlobalCacheService
4% 
50% 

 1  
 package org.apache.turbine.services.cache;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2005 The Apache Software Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License")
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 import java.io.ByteArrayOutputStream;
 20  
 import java.io.IOException;
 21  
 import java.io.ObjectOutputStream;
 22  
 
 23  
 import java.util.Enumeration;
 24  
 import java.util.Hashtable;
 25  
 import java.util.Vector;
 26  
 
 27  
 import org.apache.commons.configuration.Configuration;
 28  
 
 29  
 import org.apache.turbine.services.InitializationException;
 30  
 import org.apache.turbine.services.TurbineBaseService;
 31  
 
 32  
 /**
 33  
  * This Service functions as a Global Cache.  A global cache is a good
 34  
  * place to store items that you may need to access often but don't
 35  
  * necessarily need (or want) to fetch from the database everytime.  A
 36  
  * good example would be a look up table of States that you store in a
 37  
  * database and use throughout your application.  Since information
 38  
  * about States doesn't change very often, you could store this
 39  
  * information in the Global Cache and decrease the overhead of
 40  
  * hitting the database everytime you need State information.
 41  
  *
 42  
  * The following properties are needed to configure this service:<br>
 43  
  *
 44  
  * <code><pre>
 45  
  * services.GlobalCacheService.classname=org.apache.turbine.services.cache.TurbineGlobalCacheService
 46  
  * services.GlobalCacheService.cache.initial.size=20
 47  
  * services.GlobalCacheService.cache.check.frequency=5000
 48  
  * </pre></code>
 49  
  *
 50  
  * <dl>
 51  
  * <dt>classname</dt><dd>the classname of this service</dd>
 52  
  * <dt>cache.initial.size</dt><dd>Initial size of hash table use to store cached
 53  
  objects.  If this property is not present, the default value is 20</dd>
 54  
  * <dt>cache.check.frequency</dt><dd>Cache check frequency in Millis (1000
 55  
  Millis = 1 second).  If this property is not present, the default value is 5000</dd>
 56  
  * </dl>
 57  
  *
 58  
  * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a>
 59  
  * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
 60  
  * @author <a href="mailto:john@zenplex.com">John Thorhauer</a>
 61  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 62  
  * @version $Id: TurbineGlobalCacheService.java 264148 2005-08-29 14:21:04Z henning $
 63  
  */
 64  
 public class TurbineGlobalCacheService
 65  
         extends TurbineBaseService
 66  
         implements GlobalCacheService,
 67  
         Runnable
 68  
 {
 69  
     /**
 70  
      * Initial size of hash table
 71  
      * Value must be > 0.
 72  
      * Default = 20
 73  
      */
 74  
     public static final int DEFAULT_INITIAL_CACHE_SIZE = 20;
 75  
 
 76  
     /**
 77  
      * The property for the InitalCacheSize
 78  
      */
 79  
     public static final String INITIAL_CACHE_SIZE = "cache.initial.size";
 80  
 
 81  
     /**
 82  
      * The property for the Cache check frequency
 83  
      */
 84  
     public static final String CACHE_CHECK_FREQUENCY = "cache.check.frequency";
 85  
 
 86  
     /**
 87  
      * Cache check frequency in Millis (1000 Millis = 1 second).
 88  
      * Value must be > 0.
 89  
      * Default = 5 seconds
 90  
      */
 91  
     public static final long DEFAULT_CACHE_CHECK_FREQUENCY = 5000; // 5 seconds
 92  
 
 93  
     /** The cache. **/
 94  6
     private Hashtable cache = null;
 95  
 
 96  
     /** cacheCheckFrequency (default - 5 seconds) */
 97  6
     private long cacheCheckFrequency = DEFAULT_CACHE_CHECK_FREQUENCY;
 98  
 
 99  
     /**
 100  
      * Constructor.
 101  
      */
 102  
     public TurbineGlobalCacheService()
 103  6
     {
 104  6
     }
 105  
 
 106  
     /**
 107  
      * Called the first time the Service is used.
 108  
      */
 109  
     public void init()
 110  
             throws InitializationException
 111  
     {
 112  0
         int cacheInitialSize = DEFAULT_INITIAL_CACHE_SIZE;
 113  0
         Configuration conf = getConfiguration();
 114  0
         if (conf != null)
 115  
         {
 116  
             try
 117  
             {
 118  0
                 cacheInitialSize = conf.getInt(INITIAL_CACHE_SIZE, DEFAULT_INITIAL_CACHE_SIZE);
 119  0
                 if (cacheInitialSize <= 0)
 120  
                 {
 121  0
                     throw new IllegalArgumentException(INITIAL_CACHE_SIZE + " must be >0");
 122  
                 }
 123  0
                 cacheCheckFrequency = conf.getLong(CACHE_CHECK_FREQUENCY, DEFAULT_CACHE_CHECK_FREQUENCY);
 124  0
                 if (cacheCheckFrequency <= 0)
 125  
                 {
 126  0
                     throw new IllegalArgumentException(CACHE_CHECK_FREQUENCY + " must be >0");
 127  
                 }
 128  
             }
 129  0
             catch (Exception x)
 130  
             {
 131  0
                 throw new InitializationException(
 132  
                         "Failed to initialize TurbineGlobalCacheService", x);
 133  0
             }
 134  
         }
 135  
 
 136  
         try
 137  
         {
 138  0
             cache = new Hashtable(cacheInitialSize);
 139  
 
 140  
             // Start housekeeping thread.
 141  0
             Thread housekeeping = new Thread(this);
 142  
             // Indicate that this is a system thread. JVM will quit only when there
 143  
             // are no more active user threads. Settings threads spawned internally
 144  
             // by Turbine as daemons allows commandline applications using Turbine
 145  
             // to terminate in an orderly manner.
 146  0
             housekeeping.setDaemon(true);
 147  0
             housekeeping.start();
 148  
 
 149  0
             setInit(true);
 150  
         }
 151  0
         catch (Exception e)
 152  
         {
 153  0
             throw new InitializationException(
 154  
                     "TurbineGlobalCacheService failed to initialize", e);
 155  0
         }
 156  0
     }
 157  
 
 158  
     /**
 159  
      * Returns an item from the cache.  RefreshableCachedObject will be
 160  
      * refreshed if it is expired and not untouched.
 161  
      *
 162  
      * @param id The key of the stored object.
 163  
      * @return The object from the cache.
 164  
      * @exception ObjectExpiredException when either the object is
 165  
      * not in the cache or it has expired.
 166  
      */
 167  
     public CachedObject getObject(String id)
 168  
             throws ObjectExpiredException
 169  
     {
 170  0
         CachedObject obj = null;
 171  
 
 172  0
         obj = (CachedObject) cache.get(id);
 173  
 
 174  0
         if (obj == null)
 175  
         {
 176  
             // Not in the cache.
 177  0
             throw new ObjectExpiredException();
 178  
         }
 179  
 
 180  0
         if (obj.isStale())
 181  
         {
 182  0
             if (obj instanceof RefreshableCachedObject)
 183  
             {
 184  0
                 RefreshableCachedObject rco = (RefreshableCachedObject) obj;
 185  0
                 if (rco.isUntouched())
 186  
                 // Do not refresh an object that has exceeded TimeToLive
 187  0
                     throw new ObjectExpiredException();
 188  
                 // Refresh Object
 189  0
                 rco.refresh();
 190  0
                 if (rco.isStale())
 191  
                 // Object is Expired.
 192  0
                     throw new ObjectExpiredException();
 193  
             }
 194  
             else
 195  
             {
 196  
                 // Expired.
 197  0
                 throw new ObjectExpiredException();
 198  
             }
 199  
         }
 200  
 
 201  0
         if (obj instanceof RefreshableCachedObject)
 202  
         {
 203  
             // notify it that it's being accessed.
 204  0
             RefreshableCachedObject rco = (RefreshableCachedObject) obj;
 205  0
             rco.touch();
 206  
         }
 207  
 
 208  0
         return obj;
 209  
     }
 210  
 
 211  
     /**
 212  
      * Adds an object to the cache.
 213  
      *
 214  
      * @param id The key to store the object by.
 215  
      * @param o The object to cache.
 216  
      */
 217  
     public void addObject(String id,
 218  
                           CachedObject o)
 219  
     {
 220  
         // If the cache already contains the key, remove it and add
 221  
         // the fresh one.
 222  0
         if (cache.containsKey(id))
 223  
         {
 224  0
             cache.remove(id);
 225  
         }
 226  0
         cache.put(id, o);
 227  0
     }
 228  
 
 229  
     /**
 230  
      * Removes an object from the cache.
 231  
      *
 232  
      * @param id The String id for the object.
 233  
      */
 234  
     public void removeObject(String id)
 235  
     {
 236  0
         cache.remove(id);
 237  0
     }
 238  
 
 239  
     /**
 240  
      * Circle through the cache and remove stale objects.  Frequency
 241  
      * is determined by the cacheCheckFrequency property.
 242  
      */
 243  
     public void run()
 244  
     {
 245  
         while (true)
 246  
         {
 247  
             // Sleep for amount of time set in cacheCheckFrequency -
 248  
             // default = 5 seconds.
 249  
             try
 250  
             {
 251  0
                 Thread.sleep(cacheCheckFrequency);
 252  
             }
 253  0
             catch (InterruptedException exc)
 254  
             {
 255  0
             }
 256  
 
 257  0
             clearCache();
 258  
         }
 259  
     }
 260  
 
 261  
     /**
 262  
      * Iterate through the cache and remove or refresh stale objects.
 263  
      */
 264  
     public void clearCache()
 265  
     {
 266  0
         Vector refreshThese = new Vector(20);
 267  
         // Sync on this object so that other threads do not
 268  
         // change the Hashtable while enumerating over it.
 269  0
         synchronized (this)
 270  
         {
 271  0
             for (Enumeration e = cache.keys(); e.hasMoreElements();)
 272  
             {
 273  0
                 String key = (String) e.nextElement();
 274  0
                 CachedObject co = (CachedObject) cache.get(key);
 275  0
                 if (co instanceof RefreshableCachedObject)
 276  
                 {
 277  0
                     RefreshableCachedObject rco = (RefreshableCachedObject) co;
 278  0
                     if (rco.isUntouched())
 279  0
                         cache.remove(key);
 280  0
                     else if (rco.isStale())
 281  
                     // Do refreshing outside of sync block so as not
 282  
                     // to prolong holding the lock on this object
 283  0
                         refreshThese.addElement(key);
 284  
                 }
 285  0
                 else if (co.isStale())
 286  
                 {
 287  0
                     cache.remove(key);
 288  
                 }
 289  
             }
 290  0
         }
 291  
 
 292  0
         for (Enumeration e = refreshThese.elements(); e.hasMoreElements();)
 293  
         {
 294  0
             String key = (String) e.nextElement();
 295  0
             CachedObject co = (CachedObject) cache.get(key);
 296  0
             RefreshableCachedObject rco = (RefreshableCachedObject) co;
 297  0
             rco.refresh();
 298  
         }
 299  0
     }
 300  
 
 301  
     /**
 302  
      * Returns the number of objects currently stored in the cache
 303  
      *
 304  
      * @return int number of object in the cache
 305  
      */
 306  
     public int getNumberOfObjects()
 307  
     {
 308  0
         return cache.size();
 309  
     }
 310  
 
 311  
     /**
 312  
      * Returns the current size of the cache.
 313  
      *
 314  
      * @return int representing current cache size in number of bytes
 315  
      */
 316  
     public int getCacheSize()
 317  
             throws IOException
 318  
     {
 319  0
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 320  0
         ObjectOutputStream out = new ObjectOutputStream(baos);
 321  0
         out.writeObject(cache);
 322  0
         out.flush();
 323  
         //
 324  
         // Subtract 4 bytes from the length, because the serialization
 325  
         // magic number (2 bytes) and version number (2 bytes) are
 326  
         // both written to the stream before the object
 327  
         //
 328  0
         int objectsize = baos.toByteArray().length - 4;
 329  0
         return objectsize;
 330  
     }
 331  
 
 332  
     /**
 333  
      * Flush the cache of all objects.
 334  
      */
 335  
     public void flushCache()
 336  
     {
 337  
 
 338  0
         synchronized (this)
 339  
         {
 340  0
             for (Enumeration e = cache.keys(); e.hasMoreElements();)
 341  
             {
 342  0
                 String key = (String) e.nextElement();
 343  0
                 cache.remove(key);
 344  
             }
 345  0
         }
 346  0
     }
 347  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.