View Javadoc
1 package org.apache.turbine.services.cache; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>;. 55 */ 56 57 import java.io.ByteArrayOutputStream; 58 import java.io.IOException; 59 import java.io.ObjectOutputStream; 60 import java.util.Enumeration; 61 import java.util.Hashtable; 62 import java.util.Vector; 63 import org.apache.turbine.services.InitializationException; 64 import org.apache.turbine.services.TurbineBaseService; 65 import org.apache.turbine.services.resources.TurbineResources; 66 67 import org.apache.commons.configuration.Configuration; 68 69 70 /*** 71 * This Service functions as a Global Cache. A global cache is a good 72 * place to store items that you may need to access often but don't 73 * necessarily need (or want) to fetch from the database everytime. A 74 * good example would be a look up table of States that you store in a 75 * database and use throughout your application. Since information 76 * about States doesn't change very often, you could store this 77 * information in the Global Cache and decrease the overhead of 78 * hitting the database everytime you need State information. 79 * 80 * The following properties are needed to configure this service:<br> 81 * 82 * <code><pre> 83 * services.GlobalCacheService.classname=org.apache.turbine.services.cache.TurbineGlobalCacheService 84 * services.GlobalCacheService.cache.initial.size=20 85 * services.GlobalCacheService.cache.check.frequency=5000 86 * </pre></code> 87 * 88 * <dl> 89 * <dt>classname</dt><dd>the classname of this service</dd> 90 * <dt>cache.initial.size/dt><dd>Initial size of hash table use to store cached 91 objects. If this property is not present, the default value is 20</dd> 92 * <dt>cache.check.frequency</dt><dd>Cache check frequency in Millis (1000 93 Millis = 1 second). If this property is not present, the default value is 5000</dd> 94 * </dl> 95 * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a> 96 * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a> 97 * @author <a href="mailto:john@zenplex.com">John Thorhauer</a> 98 * @version $Id: TurbineGlobalCacheService.java,v 1.4 2002/04/16 22:07:08 kschrader Exp $ 99 */ 100 public class TurbineGlobalCacheService 101 extends TurbineBaseService 102 implements GlobalCacheService, 103 Runnable 104 { 105 /*** 106 * Initial size of hash table 107 * Value must be > 0. 108 * Default = 20 109 */ 110 public static final int DEFAULT_INITIAL_CACHE_SIZE = 20; 111 112 /*** 113 * The property for the InitalCacheSize 114 */ 115 public static final String INITIAL_CACHE_SIZE = "cache.initial.size"; 116 117 /*** 118 * The property for the Cache check frequency 119 */ 120 public static final String CACHE_CHECK_FREQUENCY = "cache.check.frequency"; 121 122 /*** 123 * Cache check frequency in Millis (1000 Millis = 1 second). 124 * Value must be > 0. 125 * Default = 5 seconds 126 */ 127 public static final long DEFAULT_CACHE_CHECK_FREQUENCY = 128 TurbineResources.getLong("cache.check.frequency", 5000 ); // 5 seconds 129 130 /*** The cache. **/ 131 private Hashtable cache = null; 132 133 /*** cacheCheckFrequency (default - 5 seconds) */ 134 private long cacheCheckFrequency = DEFAULT_CACHE_CHECK_FREQUENCY; 135 136 /*** 137 * Constructor. 138 */ 139 public TurbineGlobalCacheService() 140 { 141 } 142 143 /*** 144 * Called the first time the Service is used. 145 */ 146 public void init() 147 throws InitializationException { 148 int cacheInitialSize = DEFAULT_INITIAL_CACHE_SIZE; 149 Configuration conf = getConfiguration(); 150 if (conf != null) { 151 try { 152 cacheInitialSize = conf.getInt(INITIAL_CACHE_SIZE, DEFAULT_INITIAL_CACHE_SIZE); 153 if (cacheInitialSize <= 0) { 154 throw new IllegalArgumentException(INITIAL_CACHE_SIZE + " must be >0"); 155 } 156 cacheCheckFrequency = conf.getLong(CACHE_CHECK_FREQUENCY, DEFAULT_CACHE_CHECK_FREQUENCY); 157 if (cacheCheckFrequency <= 0) { 158 throw new IllegalArgumentException(CACHE_CHECK_FREQUENCY + " must be >0"); 159 } 160 } 161 catch (Exception x) { 162 throw new InitializationException( 163 "Failed to initialize TurbineGlobalCacheService",x); 164 } 165 } 166 167 try { 168 cache = new Hashtable(cacheInitialSize); 169 170 // Start housekeeping thread. 171 Thread housekeeping = new Thread(this); 172 // Indicate that this is a system thread. JVM will quit only when there 173 // are no more active user threads. Settings threads spawned internally 174 // by Turbine as daemons allows commandline applications using Turbine 175 // to terminate in an orderly manner. 176 housekeeping.setDaemon(true); 177 housekeeping.start(); 178 setInit(true); 179 } 180 catch (Exception e) { 181 throw new InitializationException( 182 "TurbineGlobalCacheService failed to initialize", e); 183 } 184 } 185 186 /*** 187 * Returns an item from the cache. RefreshableCachedObject will be 188 * refreshed if it is expired not untouched. 189 * 190 * @param id The key of the stored object. 191 * @return The object from the cache. 192 * @exception ObjectExpiredException when either the object is 193 * not in the cache or it has expired. 194 */ 195 public CachedObject getObject(String id) 196 throws ObjectExpiredException 197 { 198 CachedObject obj = null; 199 boolean stale = false; 200 201 obj = (CachedObject) cache.get(id); 202 203 if (obj == null) 204 { 205 // Not in the cache. 206 throw new ObjectExpiredException(); 207 } 208 209 if (obj.isStale()) { 210 if (obj instanceof RefreshableCachedObject) { 211 RefreshableCachedObject rco = (RefreshableCachedObject) obj; 212 if (rco.isUntouched()) 213 // Do not refresh an object that has exceeded TimeToLive 214 throw new ObjectExpiredException(); 215 // Refresh Object 216 rco.refresh(); 217 if (rco.isStale()) 218 // Object is Expired. 219 throw new ObjectExpiredException(); 220 } else { 221 // Expired. 222 throw new ObjectExpiredException(); 223 } 224 } 225 226 if (obj instanceof RefreshableCachedObject) 227 { 228 // notify it that it's being accessed. 229 RefreshableCachedObject rco = (RefreshableCachedObject) obj; 230 rco.touch(); 231 } 232 233 return obj; 234 } 235 236 /*** 237 * Adds an object to the cache. 238 * 239 * @param id The key to store the object by. 240 * @param o The object to cache. 241 */ 242 public void addObject(String id, 243 CachedObject o) 244 { 245 // If the cache already contains the key, remove it and add 246 // the fresh one. 247 if ( cache.containsKey(id) ) 248 { 249 cache.remove(id); 250 } 251 cache.put(id,o); 252 } 253 254 /*** 255 * Removes an object from the cache. 256 * 257 * @param id The String id for the object. 258 */ 259 public void removeObject(String id) 260 { 261 cache.remove(id); 262 } 263 264 /*** 265 * Circle through the cache and remove stale objects. Frequency 266 * is determined by the cacheCheckFrequency property. 267 */ 268 public void run() 269 { 270 while(true) 271 { 272 // Sleep for amount of time set in cacheCheckFrequency - 273 // default = 5 seconds. 274 try 275 { 276 Thread.sleep(cacheCheckFrequency); 277 } 278 catch(InterruptedException exc) 279 { 280 } 281 282 clearCache(); 283 } 284 } 285 286 /*** 287 * Iterate through the cache and remove or refresh stale objects. 288 */ 289 public void clearCache() 290 { 291 Vector refreshThese = new Vector(20); 292 // Sync on this object so that other threads do not 293 // change the Hashtable while enumerating over it. 294 synchronized (this) 295 { 296 for ( Enumeration e = cache.keys(); e.hasMoreElements(); ) 297 { 298 String key = (String) e.nextElement(); 299 CachedObject co = (CachedObject) cache.get(key); 300 if (co instanceof RefreshableCachedObject) 301 { 302 RefreshableCachedObject rco = (RefreshableCachedObject)co; 303 if (rco.isUntouched()) 304 cache.remove(key); 305 else if (rco.isStale()) 306 // Do refreshing outside of sync block so as not 307 // to prolong holding the lock on this object 308 refreshThese.addElement(key); 309 } 310 else if ( co.isStale() ) 311 { 312 cache.remove(key); 313 } 314 } 315 } 316 317 for ( Enumeration e = refreshThese.elements(); e.hasMoreElements(); ) 318 { 319 String key = (String)e.nextElement(); 320 CachedObject co = (CachedObject)cache.get(key); 321 RefreshableCachedObject rco = (RefreshableCachedObject)co; 322 rco.refresh(); 323 } 324 } 325 326 /*** 327 * Returns the number of objects currently stored in the cache 328 * 329 * @return int number of object in the cache 330 */ 331 public int getNumberOfObjects() 332 { 333 return cache.size(); 334 } 335 336 /*** 337 * Returns the current size of the cache. 338 * 339 * @return int representing current cache size in number of bytes 340 */ 341 public int getCacheSize() 342 throws IOException 343 { 344 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 345 ObjectOutputStream out = new ObjectOutputStream(baos); 346 out.writeObject(cache); 347 out.flush(); 348 // 349 // Subtract 4 bytes from the length, because the serialization 350 // magic number (2 bytes) and version number (2 bytes) are 351 // both written to the stream before the object 352 // 353 int objectsize = baos.toByteArray().length - 4; 354 return objectsize; 355 } 356 357 /*** 358 * Flush the cache of all objects. 359 */ 360 public void flushCache() { 361 362 synchronized (this) { 363 for ( Enumeration e = cache.keys(); e.hasMoreElements(); ) { 364 String key = (String) e.nextElement(); 365 CachedObject co = (CachedObject) cache.get(key); 366 cache.remove(key); 367 } 368 } 369 } 370 }

This page was automatically generated by Maven