1 23 24 29 30 package com.sun.appserv.util.cache; 31 32 import java.text.MessageFormat ; 33 34 import java.util.ArrayList ; 35 import java.util.Enumeration ; 36 import java.util.Vector ; 37 import java.util.Map ; 38 import java.util.HashMap ; 39 import java.util.Properties ; 40 import java.util.Iterator ; 41 import java.util.ResourceBundle ; 42 43 import com.sun.common.util.logging.LogDomains; 44 45 49 public class BaseCache implements Cache { 50 51 54 protected static ResourceBundle _rb = null; 55 56 static final int MAX_ENTRIES = 1 << 30; 57 static final float DEFAULT_LOAD_FACTOR = 0.75f; 58 59 int maxEntries; 61 62 protected int entryCount; 64 private Object entryCountLk = new Object (); 65 66 69 protected int threshold = 0; 70 71 private int hitCount; 73 private Object hitCountLk = new Object (); 74 75 private int missCount; 77 private Object missCountLk = new Object (); 78 79 private int removalCount; 81 private Object removalCountLk = new Object (); 82 83 private int refreshCount; 85 private Object refreshCountLk = new Object (); 86 87 private int addCount; 89 private Object addCountLk = new Object (); 90 91 private int overflowCount; 93 private Object overflowCountLk = new Object (); 94 95 protected int maxBuckets; 97 98 protected CacheItem[] buckets; 100 protected Object [] bucketLocks; 102 103 protected boolean[] refreshFlags; 105 106 protected ArrayList listeners = new ArrayList (); 107 108 111 public BaseCache() { } 112 113 119 public void init(int maxEntries, Properties props) throws Exception { 120 init(maxEntries, DEFAULT_LOAD_FACTOR, props); 121 } 122 123 130 public void init(int maxEntries, float loadFactor, Properties props) { 131 132 _rb = LogDomains.getLogger(LogDomains.CMN_LOGGER).getResourceBundle(); 134 135 if (maxEntries <= 0) { 136 String msg = _rb.getString("cache.BaseCache.illegalMaxEntries"); 137 138 Integer obj = new Integer (maxEntries); 139 Object [] params = { obj }; 140 msg = MessageFormat.format(msg, params); 141 142 throw new IllegalArgumentException (msg); 143 } 144 145 if (maxEntries > MAX_ENTRIES) 146 maxEntries = MAX_ENTRIES; 147 148 this.maxEntries = maxEntries; 149 150 maxBuckets = 1; 152 while (maxBuckets < maxEntries) 153 maxBuckets <<= 1; 154 155 if( loadFactor < 0 ) 157 loadFactor = 0; 158 159 162 if (maxEntries != 0) { 163 threshold = (int)(maxEntries * loadFactor) + 1; 164 } 165 166 entryCount = 0; 168 buckets = new CacheItem[maxBuckets]; 169 bucketLocks = new Object [maxBuckets]; 170 refreshFlags = new boolean[maxBuckets]; 171 172 for (int i=0; i<maxBuckets; i++) { 173 buckets[i] = null; 174 bucketLocks[i] = new Object (); 175 refreshFlags[i] = false; 176 } 177 } 178 179 183 public void addCacheListener(CacheListener listener) { 184 listeners.add(listener); 185 } 186 187 191 protected int hash(Object x) { 192 int h = x.hashCode(); 193 return h - (h << 7); } 195 196 199 protected boolean eq(Object x, Object y) { 200 return x == y || x.equals(y); 201 } 202 203 206 protected void handleOverflow() { 207 threshold = (threshold * 2); 209 incrementOverflowCount(); 210 } 211 212 224 protected CacheItem itemAdded(CacheItem item) { 225 if (isThresholdReached()) { 226 handleOverflow(); 227 } 228 return null; 229 } 230 231 237 protected void itemAccessed(CacheItem item) { } 238 239 245 protected void itemRefreshed(CacheItem item, int oldSize) { } 246 247 253 protected void itemRemoved(CacheItem item) { } 254 255 263 protected Object loadValue(Object key, int hashCode) { 264 return null; 265 } 266 267 276 protected CacheItem createItem(int hashCode, Object key, 277 Object value, int size) { 278 return new CacheItem(hashCode, key, value, size); 279 } 280 281 285 protected boolean isThresholdReached() { 286 return (entryCount > threshold); 287 } 288 289 294 protected final int getIndex(int hashCode) { 295 return (hashCode & (maxBuckets - 1)); 296 } 297 298 303 public final int getIndex(Object key) { 304 return getIndex(hash(key)); 305 } 306 307 312 public Object get(Object key) { 313 int hashCode = hash(key); 314 315 return get(hashCode, key); 316 } 317 318 323 public Object get(int hashCode, Object key) { 324 325 int index = getIndex(hashCode); 326 Object value; 327 CacheItem item = null; 328 329 synchronized (bucketLocks[index]) { 330 item = buckets[index]; 331 332 for (; item != null; item = item.next) { 333 if ( (hashCode == item.hashCode) && eq(key, item.key) ) { 334 break; 335 } 336 } 337 338 if (item != null) { 340 value = item.getValue(); 341 itemAccessed(item); 342 } 343 else 344 value = loadValue(key, hashCode); 345 } 346 347 if (item != null) 348 incrementHitCount(); 349 else 350 incrementMissCount(); 351 352 return value; 353 } 354 355 360 public boolean contains(Object key) { 361 return (get(key) != null); 362 } 363 364 369 public Iterator getAll(Object key) { 370 int hashCode = hash(key); 371 int index = getIndex(hashCode); 372 373 ArrayList valueList = new ArrayList (entryCount); 374 synchronized (bucketLocks[index]) { 375 CacheItem item = buckets[index]; 376 377 for (; item != null; item = item.next) { 378 if ( (hashCode == item.hashCode) && eq(key, item.key) ) { 379 incrementHitCount(); 380 valueList.add(item.getValue()); 381 } 382 } 383 384 } 385 386 return valueList.iterator(); 387 } 388 389 393 public Iterator keys() { 394 ArrayList keyList = new ArrayList (entryCount); 395 396 for (int index=0; index < maxBuckets; index++) { 397 synchronized (bucketLocks[index]) { 398 for (CacheItem item = buckets[index]; item != null; 399 item = item.next) { 400 keyList.add(item.key); 401 } 402 } 403 } 404 405 return keyList.iterator(); 406 } 407 408 413 public Enumeration elements() { 414 Vector keyList = new Vector (); 415 416 for (int index=0; index < maxBuckets; index++) { 417 synchronized (bucketLocks[index]) { 418 for (CacheItem item = buckets[index]; item != null; 419 item = item.next) { 420 keyList.addElement(item.key); 421 } 422 } 423 } 424 425 return keyList.elements(); 426 } 427 428 432 public Iterator values() { 433 ArrayList valueList = new ArrayList (entryCount); 434 435 for (int index=0; index < maxBuckets; index++) { 436 synchronized (bucketLocks[index]) { 437 for (CacheItem item = buckets[index]; item != null; 438 item = item.next) { 439 valueList.add(item.value); 440 } 441 } 442 } 443 444 return valueList.iterator(); 445 } 446 447 454 public Object put(Object key, Object value) { 455 int hashCode = hash(key); 456 457 return _put(hashCode, key, value, -1, false); 458 } 459 460 467 public Object put(Object key, Object value, int size) { 468 int hashCode = hash(key); 469 470 return _put(hashCode, key, value, size, false); 471 } 472 473 478 public void add(Object key, Object value) { 479 int hashCode = hash(key); 480 481 _put(hashCode, key, value, -1, true); 482 } 483 484 492 public void add(Object key, Object value, int size) { 493 int hashCode = hash(key); 494 495 _put(hashCode, key, value, size, true); 496 } 497 498 511 protected Object _put(int hashCode, Object key, 512 Object value, int size, boolean addValue) { 513 int index = getIndex(hashCode); 514 515 CacheItem item, newItem = null, oldItem = null, overflow = null; 516 Object oldValue; 517 int oldSize = 0; 518 519 synchronized (bucketLocks[index]) { 521 for (item = buckets[index]; item != null; item = item.next) { 522 if ((hashCode == item.hashCode) && eq(key, item.key)) { 523 524 oldItem = item; 525 break; 526 } 527 } 528 529 if (addValue || oldItem == null) { 531 newItem = createItem(hashCode, key, value, size); 532 533 newItem.next = buckets[index]; 535 buckets[index] = newItem; 536 537 oldValue = null; 538 overflow = itemAdded(newItem); 539 } 540 else { 541 oldSize = oldItem.getSize(); 542 oldValue = oldItem.refreshValue(value, size); 543 itemRefreshed(oldItem, oldSize); 544 } 545 } 546 547 if (newItem != null) { 548 incrementEntryCount(); 549 incrementAddCount(); 550 551 if (overflow != null) 553 trimItem(overflow); 554 } 555 else 556 incrementRefreshCount(); 557 558 return oldValue; 559 } 560 561 566 public Object remove(Object key) { 567 int hashCode = hash(key); 568 569 Object retVal = null; 570 CacheItem removed = _remove( hashCode, key, null); 571 572 if (removed != null) 573 retVal = removed.getValue(); 574 return retVal; 575 } 576 577 583 public Object remove(int hashCode, Object key) { 584 Object retVal = null; 585 CacheItem removed = _remove( hashCode, key, null); 586 587 if (removed != null) 588 retVal = removed.getValue(); 589 return retVal; 590 } 591 592 598 public Object remove(Object key, Object value) { 599 int hashCode = hash(key); 600 601 Object retVal = null; 602 CacheItem removed = _remove( hashCode, key, value); 603 604 if (removed != null) 605 retVal = removed.getValue(); 606 return retVal; 607 } 608 609 616 protected CacheItem _remove(int hashCode, Object key, Object value) { 617 int index = getIndex(hashCode); 618 619 CacheItem prev = null, item = null; 620 621 synchronized (bucketLocks[index]) { 622 for (item = buckets[index]; item != null; item = item.next) { 623 if (hashCode == item.hashCode && key.equals(item.key)) { 624 625 if (value == null || value == item.value) { 626 627 if (prev == null) { 628 buckets[index] = item.next; 629 } else { 630 prev.next = item.next; 631 } 632 item.next = null; 633 634 itemRemoved(item); 635 break; 636 } 637 } 638 prev = item; 639 } 640 } 641 642 if (item != null) { 643 decrementEntryCount(); 644 incrementRemovalCount(); 645 646 incrementHitCount(); 647 } else 648 incrementMissCount(); 649 650 return item; 651 } 652 653 658 protected CacheItem _removeItem(CacheItem ritem) { 659 660 int index = getIndex(ritem.hashCode); 661 662 CacheItem prev = null, item = null; 663 664 synchronized (bucketLocks[index]) { 665 for (item = buckets[index]; item != null; item = item.next) { 666 if (item == ritem) { 667 if (prev == null) { 668 buckets[index] = item.next; 669 } else { 670 prev.next = item.next; 671 } 672 item.next = null; 673 break; 674 } 675 prev = item; 676 } 677 } 678 679 if (item != null) { 680 decrementEntryCount(); 681 } 682 683 return item; 684 } 685 686 690 public void removeAll(Object key) { 691 int hashCode = hash(key); 692 int index = getIndex(hashCode); 693 694 CacheItem prev = null, item = null; 695 ArrayList items = new ArrayList (entryCount); 696 697 synchronized (bucketLocks[index]) { 698 for (item = buckets[index]; item != null; 699 item = item.next) { 700 if (hashCode == item.hashCode && key.equals(item.key)) { 701 if (prev == null) { 702 buckets[index] = item.next; 703 } else { 704 prev.next = item.next; 705 } 706 item.next = null; 707 708 decrementEntryCount(); 709 incrementRemovalCount(); 710 711 items.add(item); 712 } 713 prev = item; 714 } 715 } 716 717 for (int i = 0; i < items.size(); i++) { 719 itemRemoved((CacheItem)items.get(i)); 720 } 721 } 722 723 727 protected void trimItem(CacheItem item) { 728 CacheItem removed = _removeItem(item); 729 730 if (removed != null) { 731 for (int i = 0; i < listeners.size(); i++) { 732 CacheListener listener = (CacheListener) listeners.get(i); 733 listener.trimEvent(removed.key, removed.value); 734 } 735 } 736 } 737 738 744 public boolean waitRefresh(int index) { 745 synchronized (bucketLocks[index]) { 746 if (refreshFlags[index] == false) { 747 refreshFlags[index] = true; 748 return false; 749 } 750 751 try { 753 bucketLocks[index].wait(); 754 } catch (InterruptedException ie) {} 755 } 756 return true; 757 } 758 759 763 public void notifyRefresh(int index) { 764 synchronized (bucketLocks[index]) { 766 refreshFlags[index] = false; 767 bucketLocks[index].notifyAll(); 768 } 769 } 770 771 775 public int clear() { 776 777 CacheItem item=null, next=null; 778 int count = 0; 779 780 for (int index = 0; index < maxBuckets; index++) { 781 synchronized (bucketLocks[index]) { 782 for (item = buckets[index]; item != null; 783 item = item.next) { 784 next = item.next; 785 item.next = null; 786 787 count++; 788 decrementEntryCount(); 789 itemRemoved(item); 790 791 if (entryCount == 0) 792 break; 793 } 794 buckets[index] = null; 795 } 796 } 797 798 799 return count; 800 } 801 802 809 public void trimExpiredEntries(int maxCount) {} 810 811 815 public int getEntryCount() { 816 return entryCount; 817 } 818 819 820 821 825 public boolean isEmpty() { 826 return (entryCount == 0); 827 } 828 829 832 protected final void incrementEntryCount() { 833 synchronized(entryCountLk) { 834 entryCount++; 835 } 836 } 837 838 protected final void decrementEntryCount() { 839 synchronized(entryCountLk) { 840 entryCount--; 841 } 842 } 843 844 protected final void incrementHitCount() { 845 synchronized (hitCountLk) { 846 hitCount++; 847 } 848 } 849 850 protected final void incrementMissCount() { 851 synchronized (missCountLk) { 852 missCount++; 853 } 854 } 855 856 protected final void incrementRemovalCount() { 857 synchronized (removalCountLk) { 858 removalCount++; 859 } 860 } 861 862 protected final void incrementRefreshCount() { 863 synchronized (refreshCountLk) { 864 refreshCount++; 865 } 866 } 867 868 protected final void incrementAddCount() { 869 synchronized (addCountLk) { 870 addCount++; 871 } 872 } 873 874 protected final void incrementOverflowCount() { 875 synchronized (overflowCountLk) { 876 overflowCount++; 877 } 878 } 879 880 883 884 890 public Object getStatByName(String key) { 891 Object stat = null; 892 893 if (key == null) 894 return null; 895 896 if (key.equals(Constants.STAT_BASECACHE_MAX_ENTRIES)) 897 stat = new Integer (maxEntries); 898 else if (key.equals(Constants.STAT_BASECACHE_THRESHOLD)) 899 stat = new Integer (threshold); 900 else if (key.equals(Constants.STAT_BASECACHE_TABLE_SIZE)) 901 stat = new Integer (maxBuckets); 902 else if (key.equals(Constants.STAT_BASECACHE_ENTRY_COUNT)) 903 stat = new Integer (entryCount); 904 else if (key.equals(Constants.STAT_BASECACHE_HIT_COUNT)) 905 stat = new Integer (hitCount); 906 else if (key.equals(Constants.STAT_BASECACHE_MISS_COUNT)) 907 stat = new Integer (missCount); 908 else if (key.equals(Constants.STAT_BASECACHE_REMOVAL_COUNT)) 909 stat = new Integer (removalCount); 910 else if (key.equals(Constants.STAT_BASECACHE_REFRESH_COUNT)) 911 stat = new Integer (refreshCount); 912 else if (key.equals(Constants.STAT_BASECACHE_OVERFLOW_COUNT)) 913 stat = new Integer (overflowCount); 914 else if (key.equals(Constants.STAT_BASECACHE_ADD_COUNT)) 915 stat = new Integer (addCount); 916 917 return stat; 918 } 919 920 925 public Map getStats() { 926 HashMap stats = new HashMap (); 927 928 stats.put(Constants.STAT_BASECACHE_MAX_ENTRIES, new Integer (maxEntries)); 929 stats.put(Constants.STAT_BASECACHE_THRESHOLD, new Integer (threshold)); 930 stats.put(Constants.STAT_BASECACHE_TABLE_SIZE, new Integer (maxBuckets)); 931 stats.put(Constants.STAT_BASECACHE_ENTRY_COUNT, new Integer (entryCount)); 932 stats.put(Constants.STAT_BASECACHE_HIT_COUNT, new Integer (hitCount)); 933 stats.put(Constants.STAT_BASECACHE_MISS_COUNT, new Integer (missCount)); 934 stats.put(Constants.STAT_BASECACHE_REMOVAL_COUNT, new Integer (removalCount)); 935 stats.put(Constants.STAT_BASECACHE_REFRESH_COUNT, new Integer (refreshCount)); 936 stats.put(Constants.STAT_BASECACHE_OVERFLOW_COUNT, new Integer (overflowCount)); 937 stats.put(Constants.STAT_BASECACHE_ADD_COUNT, new Integer (addCount)); 938 939 return stats; 940 } 941 942 946 public void destroy() { 947 if ((listeners != null) && (buckets != null) && (bucketLocks != null)) { 948 clear(); 949 listeners.clear(); 950 } 951 952 entryCountLk = null; 953 hitCountLk = null; 954 missCountLk = null; 955 removalCountLk = null; 956 refreshCountLk = null; 957 addCountLk = null; 958 overflowCountLk = null; 959 buckets = null; 960 bucketLocks = null; 961 refreshFlags = null; 962 listeners = null; 963 } 964 965 968 public void clearStats() { 969 hitCount = 0; 970 missCount = 0; 971 removalCount = 0; 972 refreshCount = 0; 973 overflowCount = 0; 974 addCount = 0; 975 } 976 977 978 protected static class CacheItem { 979 int hashCode; 980 Object key; 981 Object value; 982 int size; 983 984 CacheItem next; 985 986 protected CacheItem(int hashCode, Object key, Object value, int size) { 987 this.hashCode = hashCode; 988 this.key = key; 989 this.value = value; 990 this.size = size; 991 } 992 993 996 protected int getHashCode() { 997 return hashCode; 998 } 999 1000 1003 protected Object getKey() { 1004 return key; 1005 } 1006 1007 1010 protected Object getValue() { 1011 return value; 1012 } 1013 1014 1018 protected int getSize() { 1019 return size; 1020 } 1021 1022 1027 protected Object refreshValue(Object value, int newSize) { 1028 Object oldValue = this.value; 1029 this.value = value; 1030 this.size = newSize; 1031 1032 return oldValue; 1033 } 1034 1035 public String toString() { 1036 return "key: " + key + "; value: " + value.toString(); 1037 } 1038 } 1039} 1040 | Popular Tags |