1 25 26 package org.objectweb.perseus.cache.lib; 27 28 import org.objectweb.fractal.api.control.BindingController; 29 import org.objectweb.fractal.api.control.LifeCycleController; 30 31 import org.objectweb.perseus.cache.api.CacheAttributeController; 32 import org.objectweb.perseus.cache.api.CacheCapacityEvent; 33 import org.objectweb.perseus.cache.api.CacheCapacityEventListener; 34 import org.objectweb.perseus.cache.api.CacheEntry; 35 import org.objectweb.perseus.cache.api.CacheEntryFactory; 36 import org.objectweb.perseus.cache.api.CacheEvent; 37 import org.objectweb.perseus.cache.api.CacheEventListener; 38 import org.objectweb.perseus.cache.api.CacheException; 39 import org.objectweb.perseus.cache.api.CacheManager; 40 import org.objectweb.perseus.cache.api.FixableCacheEntry; 41 import org.objectweb.perseus.cache.api.InvalidCacheEntryException; 42 import org.objectweb.perseus.cache.api.UnbindManager; 43 import org.objectweb.perseus.cache.api.CacheFullException; 44 import org.objectweb.perseus.cache.replacement.api.ReplacementManager; 45 import org.objectweb.util.monolog.api.Logger; 46 import org.objectweb.util.monolog.api.BasicLevel; 47 import org.objectweb.util.monolog.api.LoggerFactory; 48 49 import java.util.HashMap ; 50 import java.util.Iterator ; 51 import java.util.Map ; 52 import java.util.HashSet ; 53 import java.util.Collection ; 54 import java.util.ArrayList ; 55 import java.util.Collections ; 56 import java.lang.ref.ReferenceQueue ; 57 import java.lang.ref.Reference ; 58 import java.lang.ref.WeakReference ; 59 60 67 public class BasicCacheManager 68 implements 69 CacheManager, 70 UnbindManager, 71 BindingController, 72 LifeCycleController, 73 CacheAttributeController { 74 75 public final static String DEFAULT_AUTO_CLEAN_SIZE = "7%"; 76 public final static String CACHE_ENTRY_FACTORY_BINDING = "cache-entry-factory"; 77 public final static String REPLACEMENT_MANAGER_BINDING = "replacement-manager"; 78 public final static String CACHE_LISTENER_BINDING = "cache-listeners"; 79 public final static String CACHE_CAPACTITY_LISTENER_BINDING = "cache-capacity-listeners"; 80 81 82 85 protected class Event implements CacheEvent { 86 87 public int eventId = 0; 88 public CacheEntry ce = null; 89 public Object oid = null; 90 public int cacheSize = -2; 91 92 public Event(int eventid, Object oid, int cachesize) { 93 eventId = eventid; 94 this.oid = oid; 95 cacheSize = cachesize; 96 } 97 98 public Event(int eventid, CacheEntry _ce, int cachesize) { 99 eventId = eventid; 100 ce = _ce; 101 cacheSize = cachesize; 102 } 103 104 public int getEventId() { 105 return eventId; 106 } 107 108 public Object getCeIdentifier() { 109 return oid; 110 } 111 112 public CacheEntry getEntry() { 113 return ce; 114 } 115 } 116 117 120 protected class CapacityEvent implements CacheCapacityEvent { 121 122 public int eventId = 0; 123 public int oldCacheSize = -2; 124 public int cacheSize = -2; 125 126 public CapacityEvent(int eventid, int oldCacheSize, int cacheSize) { 127 this.eventId = eventid; 128 this.cacheSize = cacheSize; 129 this.oldCacheSize = oldCacheSize; 130 } 131 132 public int getEventId() { 133 return eventId; 134 } 135 136 public int getOldSize() { 137 return oldCacheSize; 138 } 139 140 public int getSize() { 141 return cacheSize; 142 } 143 } 144 145 148 protected CacheEntryFactory cef = null; 149 150 protected ReplacementManager rm = null; 151 152 156 protected Map cache; 157 158 161 protected ReferenceQueue queue; 162 163 166 protected int cacheMaxSize; 167 168 private BackgroundCleaner bgcleaner; 169 protected int cacheThresHold = 0; 170 protected String cacheThresHoldStr; 171 172 protected Logger logger = null; 173 protected Logger bgclogger = null; 174 175 178 protected HashMap cels; 179 180 183 protected HashMap ccels; 184 185 private String autoCleanSizeStr; 186 187 private int autoCleanSize; 188 189 private boolean started = false; 190 191 195 public BasicCacheManager() { 196 cels = new HashMap (); 197 ccels = new HashMap (); 198 cache = new HashMap (); 199 queue = new ReferenceQueue (); 200 cacheMaxSize = NO_LIMIT; 201 started = false; 202 autoCleanSizeStr = null; 203 cacheThresHold = 0; 204 } 205 206 public BackgroundCleaner getBgcleaner() { 207 if (bgcleaner == null && cacheThresHold > 0) { 208 bgcleaner = new BackgroundCleaner(rm, bgclogger); 209 } 210 return bgcleaner; 211 } 212 213 216 public String getFcState() { 217 return started ? STARTED : STOPPED ; 218 } 219 220 public void startFc() { 221 if (!started) { 222 started = true; 223 autoCleanSize = getValue(autoCleanSizeStr); 224 cacheThresHold = getValue(cacheThresHoldStr); 225 CacheCapacityEvent ev = new CapacityEvent( 226 CacheCapacityEvent.NOTIFY_CACHE_RESIZE, NO_LIMIT, this.cacheMaxSize); 227 for (Iterator it = ccels.values().iterator(); it.hasNext();) { 228 ((CacheCapacityEventListener) it.next()).cacheResized(ev); 229 } 230 } 231 } 232 233 public void stopFc() { 234 if (started) { 235 started = false; 236 try { 237 setMaxObjects(0); 239 } catch (CacheException e) { 240 } 241 } 242 } 243 244 247 public String [] listFc() { 248 HashSet itfs = new HashSet (); 249 itfs.add(CACHE_ENTRY_FACTORY_BINDING); 250 itfs.add(REPLACEMENT_MANAGER_BINDING); 251 itfs.add(CACHE_LISTENER_BINDING); 252 itfs.add(CACHE_CAPACTITY_LISTENER_BINDING); 253 return (String [])itfs.toArray(new String [itfs.size()]); 254 } 255 256 public Object lookupFc(String s) { 257 if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) { 258 return cef; 259 } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) { 260 return rm; 261 } else if (s.startsWith(CACHE_LISTENER_BINDING)) { 262 return cels.get(s); 263 } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) { 264 return ccels.get(s); 265 } else { 266 return null; 267 } 268 } 269 270 public void bindFc(String s, Object o) { 271 if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) { 272 cef = (CacheEntryFactory) o; 273 } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) { 274 rm = (ReplacementManager) o; 275 } else if (s.startsWith(CACHE_LISTENER_BINDING)) { 276 cels.put(s, o); 277 } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) { 278 ccels.put(s, o); 279 } else if ("monolog-factory".equals(s)) { 280 bgclogger = ((LoggerFactory) o) 281 .getLogger(logger.getName() + ".bgcleaner"); 282 } else if ("logger".equals(s)) { 283 logger = (Logger) o; 284 } 285 } 286 287 public void unbindFc(String s) { 288 if (CACHE_ENTRY_FACTORY_BINDING.equals(s)) { 289 cef = null; 290 } else if (REPLACEMENT_MANAGER_BINDING.equals(s)) { 291 rm = null; 292 if (bgcleaner != null) { 293 bgcleaner.stop(); 294 bgcleaner = null; 295 } 296 } else if (s.startsWith(CACHE_LISTENER_BINDING)) { 297 cels.remove(s); 298 } else if (s.startsWith(CACHE_CAPACTITY_LISTENER_BINDING)) { 299 ccels.remove(s); 300 } 301 } 302 303 306 public void setMaxObjects(int size) 307 throws IllegalArgumentException , CacheException { 308 if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) 309 logger.log(BasicLevel.DEBUG, "resize the cache: " 310 + (size == NO_LIMIT ? "UNLIMITED" : "" + size)); 311 if (size == NO_LIMIT) { 312 cacheMaxSize = NO_LIMIT; 313 return; 314 } 315 if (size < 0) 316 throw new IllegalArgumentException ( 317 "Impossible to set the cache size to a negative value: " + size); 318 synchronized (cache) { 319 CacheCapacityEvent ev = new CapacityEvent( 320 CacheCapacityEvent.NOTIFY_CACHE_RESIZE, cacheMaxSize, size); 321 if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) 322 logger.log(BasicLevel.DEBUG, 323 "resize the cache: " + cacheMaxSize + " to " + size 324 + " (Nd elem=" + cache.size() + ")"); 325 int delta = size - cache.size(); 326 if (delta < 0) { 327 delta = -delta; 328 int free = rm.forceFree(delta); 329 if (delta > free) 330 throw new CacheException("It stays " + (delta - free) 331 + " resources which have not been free"); 332 } 333 cacheMaxSize = size; 334 if (started) { 335 for (Iterator it = ccels.values().iterator(); it.hasNext();) { 336 ((CacheCapacityEventListener) it.next()).cacheResized(ev); 337 } 338 } else { 339 Map old = cache; 341 cache = new HashMap (cacheMaxSize, 1); 342 if (old.size() > 0) { 343 cache.putAll(cache); 344 } 345 } 346 } 347 } 348 349 public int getMaxObjects() { 350 return cacheMaxSize; 351 } 352 353 public int getCurrentSize() { 354 return cache.size(); 355 } 356 357 public Collection getCurrentEntryIdentifiers() { 358 synchronized(cache) { 359 if (cache.size() == 0) { 360 return Collections.EMPTY_SET; 361 } 362 return Collections.unmodifiableCollection(cache.keySet()); 363 } 364 } 365 366 public void setMemorySize(int size) throws IllegalArgumentException { 367 if (size < 0) 368 throw new IllegalArgumentException ( 369 "Impossible to set the cache size to a negative value: " + size); 370 } 371 372 public int getMemorySize() { 373 return 0; 374 } 375 376 public int getCurrentMemorySize() { 377 return 0; 378 } 379 380 public void setAutoCleanSize(String size) { 381 autoCleanSizeStr = size; 382 autoCleanSize = getValue(autoCleanSizeStr); 383 } 384 385 public String getAutoCleanSize() { 386 return autoCleanSizeStr; 387 } 388 389 public void setAutoCleanThreshold(String size) { 390 cacheThresHoldStr = size; 391 cacheThresHold = getValue(cacheThresHoldStr); 392 } 393 394 public String getAutoCleanThreshold() { 395 return cacheThresHoldStr; 396 } 397 398 private int getValue(String str) { 399 if (cacheMaxSize == NO_LIMIT) { 400 return 0; 401 } 402 if (str == null) { 403 str = "75%"; 404 } 405 int i = str.indexOf('%'); 406 if (i == -1) { 407 return Integer.parseInt(str); 408 } else { 409 int res = Integer.parseInt(str.substring(0, i)); 410 return (cacheMaxSize * res) / 100; 411 } 412 } 413 414 417 public CacheEntry bind(Object id, Object object) throws CacheException { 418 if (id == null || object == null) 419 throw new IllegalArgumentException ( 420 "Impossible to add a cache entry with a null identifier or object"); 421 if (logger.isLoggable(BasicLevel.DEBUG)) 422 logger.log(BasicLevel.DEBUG, "bind an element in the cache, id:" + id); 423 synchronized (cache) { 424 clean(); 425 426 int size = cache.size(); 428 if (cacheMaxSize != NO_LIMIT) { 429 if (size >= cacheMaxSize) { 430 rm.forceFree(autoCleanSize); 432 size = cache.size(); 433 if (size >= cacheMaxSize) { 434 forceClean(); 436 size = cache.size(); 437 if (size >= cacheMaxSize) { 438 throw new CacheFullException(); 440 } 441 } 442 } else if ((size + 1) >= cacheThresHold 443 && getBgcleaner() != null) { 444 getBgcleaner().backgroungCleanup(autoCleanSize); 445 } 446 } 447 448 WeakCacheEntry ref = (WeakCacheEntry) cache.get(id); 450 if (ref != null) { 451 FixableCacheEntry entry = (FixableCacheEntry) ref.get(); 452 if (entry != null && ref.ce != null) { 454 throw new CacheException("Identifier is already bound with " 455 + (entry.getCeObject() == object ? "the same" : "another") 456 + " object: \nid=" + id); 457 } 458 } 459 460 FixableCacheEntry ce = cef.create(id, object); 461 cache.put(id, new WeakCacheEntry(ce, queue)); 462 rm.addForReplacement(ce); 463 CacheEvent ev = 464 new Event(CacheEvent.NOTIFY_BIND, ce, ++size); 465 logger.log(BasicLevel.DEBUG, "notify the bind"); 466 for (Iterator it = cels.values().iterator(); it.hasNext();) { 467 ((CacheEventListener) it.next()).entryBound(ev); 468 } 469 return ce; 470 } 471 } 472 473 public CacheEntry lookup(Object id) { 474 if (id == null) 475 throw new IllegalArgumentException ( 476 "Impossible to find cache entry with a null identifier"); 477 synchronized (cache) { 478 try { 479 CacheEntry ce = get(id); 480 if (logger.isLoggable(BasicLevel.DEBUG)) { 481 logger.log(BasicLevel.DEBUG, "lookup: id=" + id); 482 } 483 return ce; 484 } catch (InvalidCacheEntryException e) { 485 return null; 486 } 487 } 488 } 489 490 public void fix(CacheEntry _ce) throws CacheException { 491 if (_ce == null) 492 throw new IllegalArgumentException ( 493 "Impossible to fix a null cache entry"); 494 synchronized (_ce) { 495 FixableCacheEntry ce = get(_ce.getCeIdentifier()); 496 ce.fixCe(); 497 if (logger.isLoggable(BasicLevel.DEBUG)) 498 logger.log(BasicLevel.DEBUG, 499 "Fix an element in the cache, id:" + ce.getCeIdentifier() 500 + " / count:" + ce.getCeFixCount()); 501 } 502 } 503 504 public void unfix(CacheEntry ce) throws CacheException { 505 if (ce == null) 506 throw new IllegalArgumentException ( 507 "Impossible to unfix a null cache entry"); 508 synchronized (ce) { 509 ((FixableCacheEntry) ce).unfixCe(); 510 if (logger.isLoggable(BasicLevel.DEBUG)) 511 logger.log(BasicLevel.DEBUG, 512 "UnFix an element in the cache, id:" + ce.getCeIdentifier() 513 + " / count:" + ((FixableCacheEntry) ce).getCeFixCount()); 514 } 515 } 516 517 public void touch(CacheEntry ce) throws CacheException { 518 if (ce == null) 519 throw new IllegalArgumentException ( 520 "Impossible to touch a null cache entry"); 521 synchronized (ce) { 522 rm.adjustForReplacement((FixableCacheEntry) ce); 524 } 525 } 526 527 530 public boolean unbind(Object oid, boolean force) throws CacheException { 531 synchronized (cache) { return _unbind(oid, force); 533 } 534 } 535 private boolean _unbind(Object oid, boolean force) throws CacheException { 536 if (logger.isLoggable(BasicLevel.DEBUG)) 537 logger.log(BasicLevel.DEBUG, 538 "Unbind an element from the cache, id:" + oid); 539 clean(); 540 WeakCacheEntry ref = (WeakCacheEntry) cache.get(oid); 541 if (ref == null) { 542 return true; 543 } 544 FixableCacheEntry ce = (FixableCacheEntry) ref.get(); 545 if (ce == null) { 546 throw new InvalidCacheEntryException(); 547 } 548 synchronized(ce) { 549 if (ce.getCeFixCount() > 0) { 550 return false; 551 } 552 ref.ce = null; 555 if (force) { 556 cache.remove(oid); 557 CacheEvent ev = 558 new Event(CacheEvent.NOTIFY_UNBIND, oid, cache.size()); 559 for (Iterator it = cels.values().iterator(); it.hasNext();) { 560 ((CacheEventListener) it.next()).entryUnbound(ev); 561 } 562 rm.removeForReplacement(oid); 563 } 564 return force; 565 } 566 } 567 568 public Collection unbindAll(Collection oids, boolean force) throws CacheException { 569 ArrayList unbound = new ArrayList (oids.size()); 570 synchronized (cache) { if (cache.size() == 0) { 572 unbound.addAll(oids); 573 return unbound; 574 } 575 Iterator it = oids.iterator(); 576 while(it.hasNext()) { 577 Object oid = it.next(); 578 if (!unbound.contains(oid) && _unbind(oid, force)) { 579 unbound.add(oid); 580 } 581 } 582 if (!force) { 583 585 forceClean(); 586 clean(); 587 if (cache.size() == 0) { 588 unbound = new ArrayList (oids); 591 return unbound; 592 } 593 it = oids.iterator(); 595 while(it.hasNext()) { 596 Object oid = it.next(); 597 WeakCacheEntry ref = (WeakCacheEntry) cache.get(oid); 598 if (ref == null) { 599 if (!unbound.contains(oid)) { 600 unbound.add(oid); 601 } 602 } 603 } 604 } 605 } 606 return unbound; 607 } 608 609 public Collection unbindUnfixed(boolean force) throws CacheException { 610 return Collections.EMPTY_SET; 611 } 612 613 615 private FixableCacheEntry get(Object id) throws InvalidCacheEntryException { 616 Reference ref = (Reference ) cache.get(id); 617 if (ref == null) { 618 621 synchronized(cache) { 622 ref = (Reference ) cache.get(id); 623 } 624 if (ref == null) { 625 throw new InvalidCacheEntryException(id); 626 } 627 } 628 FixableCacheEntry entry = (FixableCacheEntry) ref.get(); 629 if (entry == null) { 630 throw new InvalidCacheEntryException(); 631 } 632 ((WeakCacheEntry) ref).ce = entry; 634 return entry; 635 } 636 637 640 private void clean() { 641 while (true) { 642 WeakCacheEntry ref = (WeakCacheEntry) queue.poll(); 643 if (ref != null && ref.ce == null) { 644 if (logger.isLoggable(BasicLevel.DEBUG)) 645 logger.log(BasicLevel.DEBUG, 646 "Really remove from the cache, id:" + ref.getIdentifier()); 647 Object oid = ref.getIdentifier(); 648 cache.remove(oid); 649 CacheEvent ev = new Event(CacheEvent.NOTIFY_UNBIND, 650 oid, cache.size()); 651 for (Iterator it = cels.values().iterator(); it.hasNext();) { 652 ((CacheEventListener) it.next()).entryUnbound(ev); 653 } 654 rm.removeForReplacement(oid); 655 } else { 656 return; 657 } 658 } 659 } 660 661 665 private void forceClean() { 666 logger.log(BasicLevel.DEBUG, "forceClean()"); 667 for (int i = 0; i < 6; ++i) { 668 System.gc(); 669 WeakCacheEntry ref = null; 670 try { 671 ref = (WeakCacheEntry) queue.remove(50); 672 } catch (InterruptedException e) { 673 } 674 if (ref != null) { 675 Object oid = ref.getIdentifier(); 676 cache.remove(oid); 677 CacheEvent ev = new Event(CacheEvent.NOTIFY_UNBIND, 678 oid, cache.size()); 679 for (Iterator it = cels.values().iterator(); it.hasNext();) { 680 ((CacheEventListener) it.next()).entryUnbound(ev); 681 } 682 rm.removeForReplacement(oid); 683 return; 684 } 685 } 686 } 687 688 693 private static class WeakCacheEntry extends WeakReference { 694 695 private final Object id; 696 697 public CacheEntry ce; 700 701 public WeakCacheEntry(CacheEntry ce, ReferenceQueue queue) { 702 super(ce, queue); 703 this.id = ce.getCeIdentifier(); 704 this.ce = ce; 705 } 706 707 public Object getIdentifier() { 708 return id; 709 } 710 } 711 } 712 | Popular Tags |