1 17 18 package com.whirlycott.cache; 19 20 import java.util.Arrays ; 21 import java.util.Iterator ; 22 import java.util.LinkedList ; 23 import java.util.List ; 24 import java.util.Map ; 25 import java.util.Map.Entry; 26 27 import org.apache.commons.collections.CollectionUtils; 28 import org.apache.commons.logging.Log; 29 import org.apache.commons.logging.LogFactory; 30 31 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap; 32 33 import com.whirlycott.cache.policy.ExpirationTimePredicate; 34 35 42 public class CacheDecorator implements Runnable , Cache { 43 44 45 private final static Log log = LogFactory.getLog(CacheDecorator.class); 46 47 51 protected int adaptiveMemorySize = 5000; 53 54 55 protected final int adaptiveMemorySizeOverflow = 512; 56 57 58 protected volatile int adaptiveResultCounter = 0; 59 60 61 protected volatile long currentTime = System.currentTimeMillis(); 62 63 64 protected volatile int adaptiveResults[]; 65 66 67 protected volatile ManagedCache managedCache; 68 69 70 protected int maxSize; 71 72 73 protected final String name; 74 75 76 private final CacheMaintenancePolicy policy; 77 78 79 protected final RecordKeeper recordKeeper = new RecordKeeper(); 80 81 82 protected long sleepTime = 10000L; 83 84 87 protected Thread tunerThread; 88 89 95 public CacheDecorator(final ManagedCache _managedCache, 96 final CacheConfiguration _configuration, 97 final CacheMaintenancePolicy[] policies) { 98 99 name = _configuration.getName(); 100 101 if (null == name) 102 throw new IllegalArgumentException (Messages.getString("CacheDecorator.cache_config_cannot_be_null")); 104 if (null == policies) 105 throw new IllegalArgumentException (Messages.getString("CacheDecorator.policies_cannot_be_null")); 107 112 if (1 != policies.length) 113 throw new IllegalArgumentException (Messages.getString("CacheDecorator.must_provide_single_policy")); 115 policy = policies[0]; 116 117 adaptiveResults = new int[adaptiveMemorySize + adaptiveMemorySizeOverflow]; 119 120 managedCache = _managedCache; 122 123 configure(_configuration); 125 126 tunerThread = new Thread (this); 128 final Object [] args = { name }; 129 tunerThread.setName(Messages.getCompoundString("CacheDecorator.whirlycache_tuner", args)); tunerThread.setDaemon(true); 131 tunerThread.start(); 132 } 133 134 137 public void clear() { 138 log.info(Messages.getString("CacheDecorator.clearing_cache")); managedCache.clear(); 140 Arrays.fill(adaptiveResults, 0); 141 } 142 143 146 protected void configure(final CacheConfiguration configuration) { 147 setMaxSize(configuration.getMaxSize()); 149 150 setSleepTime(configuration.getTunerSleepTime() * 1000L); 152 } 153 154 161 protected void doAdaptiveAccounting(final int _value) { 162 final int currentCounter = adaptiveResultCounter; 164 if (currentCounter >= adaptiveMemorySize) { 165 adaptiveResultCounter = 0; 166 adaptiveResults[0] = _value; 167 } else { 168 adaptiveResults[currentCounter] = _value; 169 adaptiveResultCounter++; 170 } 171 } 172 173 176 protected int getAdaptiveMemorySize() { 177 return adaptiveMemorySize; 178 } 179 180 184 public float getAdaptiveRatio() { 185 final int copy[] = new int[adaptiveMemorySize]; 186 System.arraycopy(adaptiveResults, 0, copy, 0, adaptiveMemorySize); 187 int positives = 0; 188 for (int i = 0; i < copy.length; i++) { 189 if (copy[i] == 1) 190 positives++; 191 } 192 return new Float (positives).floatValue() / new Float (adaptiveMemorySize).floatValue(); 194 } 195 196 200 public String getEfficiencyReport() { 201 final Object [] args = { 202 new Integer (managedCache.size()), 203 new Long (recordKeeper.getTotalOperations()), 204 new Long (recordKeeper.getHits()), 205 new Float (getAdaptiveRatio()), 206 new Float (getTotalHitrate()) 207 208 }; 209 return Messages.getCompoundString("CacheDecorator.efficiency_report", args); } 211 212 216 protected int getMaxSize() { 217 return maxSize; 218 } 219 220 223 protected CacheMaintenancePolicy getPolicy() { 224 return policy; 225 } 226 227 230 protected long getSleepTime() { 231 return sleepTime; 232 } 233 234 238 protected float getTotalHitrate() { 239 return new Long (recordKeeper.getHits()).floatValue() / new Long (recordKeeper.getTotalOperations()).floatValue(); 240 } 241 242 243 246 public Object remove(final Cacheable _key) { 247 final Object o = internalRemove(_key); 248 _key.onRemove(o); 249 return o; 250 } 251 252 256 public Object remove(final Object _key) { 257 return internalRemove(_key); 258 } 259 260 265 protected Object internalRemove(final Object _key) { 266 if (Constants.BUILD_STATS_ENABLED) 267 recordKeeper.incrementTotalOperations(); 268 269 if (_key != null) { 270 final Item cachedItem = (Item) managedCache.remove(_key); 271 272 if (Constants.BUILD_STATS_ENABLED) 274 doAdaptiveAccounting(0); 275 276 return null == cachedItem ? null : cachedItem.getItem(); 277 } else { 278 return null; 279 } 280 } 281 282 283 286 public Object retrieve(final Cacheable _key) { 287 final Object o = internalRetrieve(_key); 288 _key.onRetrieve(o); 289 return o; 290 } 291 292 protected Object internalRetrieve(final Object _key) { 293 if (Constants.BUILD_STATS_ENABLED) { 295 doAdaptiveAccounting(1); 296 297 recordKeeper.incrementTotalOperations(); 299 } 300 301 final Item cachedItem = (Item) managedCache.get(_key); 303 if (cachedItem != null) { 304 305 if (Constants.BUILD_STATS_ENABLED) { 307 cachedItem.setUsed(recordKeeper.getTotalOperations()); 308 cachedItem.incrementCount(); 309 } 310 311 final Object retval = cachedItem.getItem(); 313 if (retval != null && Constants.BUILD_STATS_ENABLED) 314 recordKeeper.incrementHits(); 315 316 return retval; 317 } else { 318 319 return null; 321 } 322 } 323 324 327 public Object retrieve(final Object _key) { 328 return internalRetrieve(_key); 329 } 330 331 334 public void run() { 335 log.debug(Messages.getString("CacheDecorator.tuning_thread_started")); 337 final Thread t = Thread.currentThread(); 338 339 try { 340 while (tunerThread == t) { 341 if (Constants.ITEM_EXPIRATION_ENABLED) 343 currentTime = System.currentTimeMillis(); 344 345 if (Constants.BUILD_STATS_ENABLED) { 346 recordKeeper.startTuneCycle(); 347 348 tuneCache(); 350 } 351 352 if (Constants.ITEM_EXPIRATION_ENABLED) 355 expireItems(); 356 357 policy.performMaintenance(); 359 360 try { 361 Thread.sleep(sleepTime); 363 } catch (final InterruptedException e) { 364 log.info(Messages.getString("CacheDecorator.tuning_thread_interrupted")); } 366 367 if (Constants.BUILD_STATS_ENABLED) { 368 369 if (sleepTime > 0L) { 370 recordKeeper.calculateQueriesPerSecond(sleepTime); 371 } 372 373 logStatistics(); 374 } 375 376 if (log.isDebugEnabled()) 377 log.debug(Messages.getString("CacheDecorator.cache_tuning_complete")); } 379 } catch (final RuntimeException e) { 380 log.fatal(Messages.getString("CacheDecorator.unexpected_shutdown"), e); } 382 383 log.debug(Messages.getString("CacheDecorator.shutting_down")); } 385 386 390 protected void logStatistics() { 391 if (sleepTime > 0L && log.isDebugEnabled()) { 392 final Object [] args = { new Long (recordKeeper.getQueriesPerSecond()) }; 393 log.debug(Messages.getCompoundString("CacheDecorator.query_rate", args)); } 395 396 if (log.isInfoEnabled()) 398 log.info(getEfficiencyReport()); 399 } 400 401 404 protected void setAdaptiveMemorySize(final int adaptiveMemorySize) { 405 this.adaptiveMemorySize = adaptiveMemorySize; 406 } 407 408 412 protected void setManagedCache(final ManagedCache cache) { 413 this.managedCache = cache; 414 log.debug(Messages.getString("CacheDecorator.managing_cache_with_type") + cache.getClass()); } 416 417 420 protected void setMaxSize(final int maxSize) { 421 this.maxSize = maxSize; 422 } 423 424 429 public void setMostlyRead(final boolean _mostlyRead) { 430 if (Constants.BUILD_STATS_ENABLED) 431 recordKeeper.incrementTotalOperations(); 432 433 managedCache.setMostlyRead(_mostlyRead); 434 } 435 436 439 protected void setSleepTime(final long sleepTime) { 440 this.sleepTime = sleepTime; 441 } 442 443 444 public void shutdown() { 445 if (log.isDebugEnabled()) 446 log.debug(Messages.getString("CacheDecorator.shutting_down_cache") + name); if (Constants.BUILD_STATS_ENABLED) { 448 log.info(getEfficiencyReport()); 449 recordKeeper.reset(); 450 } 451 452 final Thread tunerThreadToKill = tunerThread; 453 tunerThread = null; 454 tunerThreadToKill.interrupt(); 455 } 456 457 461 public int size() { 462 if (Constants.BUILD_STATS_ENABLED) 463 recordKeeper.incrementTotalOperations(); 464 465 return managedCache.size(); 466 } 467 468 469 472 public void store(final Cacheable key, final Object value) { 473 internalStore(key, value, -1L); 474 key.onStore(value); 475 } 476 477 478 481 public void store(final Cacheable _key, final Object _value, final long _expiresAfter) { 482 internalStore(_key, _value, _expiresAfter); 483 _key.onStore(_value); 484 } 485 486 489 public void store(final Object _key, final Object _value) { 490 internalStore(_key, _value, -1L); 491 } 492 493 494 497 public void store(final Object _key, final Object _value, final long _expiresAfter) { 498 internalStore(_key, _value, _expiresAfter); 499 } 500 501 507 protected void internalStore(final Object _key, final Object _value, final long _expiresAfter) { 508 if (Constants.BUILD_STATS_ENABLED) 509 recordKeeper.incrementTotalOperations(); 510 511 if (_key != null && _value != null) { 512 final Item cachedValue = new Item(_value, currentTime, _expiresAfter); 513 managedCache.put(_key, cachedValue); 514 515 if (Constants.BUILD_STATS_ENABLED) 516 doAdaptiveAccounting(0); 517 } 518 } 519 520 523 protected void tuneCache() { 524 final float adaptiveRatio = getAdaptiveRatio(); 525 if (adaptiveRatio > 0.5F) { 526 log.debug(Messages.getString("CacheDecorator.read_optimizations_on") + adaptiveRatio); managedCache.setMostlyRead(true); 528 } else { 529 log.debug(Messages.getString("CacheDecorator.read_optimizations_off") + adaptiveRatio); managedCache.setMostlyRead(false); 531 } 532 } 533 534 537 protected void expireItems() { 538 final List entries = new LinkedList (new ConcurrentHashMap(managedCache).entrySet()); 540 CollectionUtils.filter(entries, new ExpirationTimePredicate(currentTime)); 541 final Object [] args = { new Integer (entries.size()) }; 542 log.debug(Messages.getCompoundString("CacheDecorator.expiration_count", args)); for (final Iterator i = entries.iterator(); i.hasNext();) { 544 final Map.Entry entry = (Entry) i.next(); 545 if (entry != null) { 546 managedCache.remove(entry.getKey()); 548 } 549 } 550 } 551 552 }
| Popular Tags
|