1 23 24 package com.sun.ejb.containers.util.cache; 25 26 import com.sun.appserv.util.cache.Cache; 27 import com.sun.appserv.util.cache.CacheListener; 28 import com.sun.appserv.util.cache.Constants; 29 30 import java.text.MessageFormat ; 31 32 import java.util.ArrayList ; 33 import java.util.Enumeration ; 34 import java.util.Vector ; 35 import java.util.Map ; 36 import java.util.HashMap ; 37 import java.util.Properties ; 38 import java.util.Iterator ; 39 import java.util.ResourceBundle ; 40 41 import com.sun.logging.LogDomains; 42 43 47 public class BaseCache implements Cache { 48 49 52 protected static ResourceBundle _rb = null; 53 54 static final int MAX_ENTRIES = 1 << 30; 55 static final float DEFAULT_LOAD_FACTOR = 0.75f; 56 57 int maxEntries; 59 60 protected int entryCount; 62 protected Object entryCountLk = new Object (); 63 64 67 protected int threshold = 0; 68 69 protected int hitCount; 71 protected Object hitCountLk = new Object (); 72 73 protected int missCount; 75 protected Object missCountLk = new Object (); 76 77 protected int removalCount; 79 protected Object removalCountLk = new Object (); 80 81 protected int refreshCount; 83 protected Object refreshCountLk = new Object (); 84 85 protected int addCount; 87 protected Object addCountLk = new Object (); 88 89 protected int overflowCount; 91 protected Object overflowCountLk = new Object (); 92 93 protected int maxBuckets; 95 96 protected CacheItem[] buckets; 98 protected Object [] bucketLocks; 100 101 protected boolean[] refreshFlags; 103 104 protected ArrayList listeners = new ArrayList (); 105 106 109 public BaseCache() { } 110 111 115 public void destroy() { 116 if ((listeners != null) && (buckets != null) && (bucketLocks != null)){ 117 clear(); 118 this.listeners.clear(); 119 } 120 121 this.entryCountLk = null; 122 this.hitCountLk = null; 123 this.missCountLk = null; 124 this.removalCountLk = null; 125 this.refreshCountLk = null; 126 this.addCountLk = null; 127 this.overflowCountLk = null; 128 this.buckets = null; 129 this.bucketLocks = null; 130 this.refreshFlags = null; 131 this.listeners = null; 132 } 133 134 135 141 public void init(int maxEntries, Properties props) throws Exception { 142 init(maxEntries, DEFAULT_LOAD_FACTOR, props); 143 } 144 145 152 public void init(int maxEntries, float loadFactor, Properties props) { 153 154 _rb = LogDomains.getLogger(LogDomains.UTIL_LOGGER).getResourceBundle(); 156 157 if (maxEntries <= 0) { 158 String msg = _rb.getString("cache.BaseCache.illegalMaxEntries"); 159 160 Integer obj = new Integer (maxEntries); 161 Object [] params = { obj }; 162 msg = MessageFormat.format(msg, params); 163 164 throw new IllegalArgumentException (msg); 165 } 166 167 if (maxEntries > MAX_ENTRIES) { 168 maxEntries = MAX_ENTRIES; 169 } 170 171 this.maxEntries = maxEntries; 172 173 maxBuckets = 1; 175 while (maxBuckets < maxEntries) { 176 maxBuckets <<= 1; 177 } 178 179 182 if (maxEntries != 0) { 183 threshold = (int)(maxEntries * loadFactor) + 1; 184 } 185 186 entryCount = 0; 188 buckets = new CacheItem[maxBuckets]; 189 bucketLocks = new Object [maxBuckets]; 190 refreshFlags = new boolean[maxBuckets]; 191 192 for (int i=0; i<maxBuckets; i++) { 193 buckets[i] = null; 194 bucketLocks[i] = new Object (); 195 refreshFlags[i] = false; 196 } 197 } 198 199 203 public void addCacheListener(CacheListener listener) { 204 listeners.add(listener); 205 } 206 207 211 protected int hash(Object x) { 212 int h = x.hashCode(); 213 return h - (h << 7); } 215 216 219 protected boolean eq(Object x, Object y) { 220 return x == y || x.equals(y); 221 } 222 223 226 protected void handleOverflow() { 227 threshold = (threshold * 2); 229 incrementOverflowCount(); 230 } 231 232 244 protected CacheItem itemAdded(CacheItem item) { 245 if (isThresholdReached()) { 246 handleOverflow(); 247 } 248 return null; 249 } 250 251 257 protected void itemAccessed(CacheItem item) { } 258 259 265 protected void itemRefreshed(CacheItem item, int oldSize) { } 266 267 273 protected void itemRemoved(CacheItem item) { } 274 275 283 protected Object loadValue(Object key, int hashCode) { 284 return null; 285 } 286 287 296 protected CacheItem createItem(int hashCode, Object key, 297 Object value, int size) { 298 return new CacheItem(hashCode, key, value, size); 299 } 300 301 305 protected boolean isThresholdReached() { 306 return (entryCount > threshold); 307 } 308 309 314 protected final int getIndex(int hashCode) { 315 return (hashCode & (maxBuckets - 1)); 316 } 317 318 323 public final int getIndex(Object key) { 324 return getIndex(hash(key)); 325 } 326 327 332 public Object get(Object key) { 333 int hashCode = hash(key); 334 335 return get(hashCode, key); 336 } 337 338 343 public Object get(int hashCode, Object key) { 344 345 int index = getIndex(hashCode); 346 Object value; 347 CacheItem item = null; 348 349 synchronized (bucketLocks[index]) { 350 item = buckets[index]; 351 352 for (; item != null; item = item.next) { 353 if ( (hashCode == item.hashCode) && eq(key, item.key) ) { 354 break; 355 } 356 } 357 358 if (item != null) { 360 value = item.getValue(); 361 itemAccessed(item); 362 } 363 else { 364 value = loadValue(key, hashCode); 365 } 366 } 367 368 if (item != null) { 369 incrementHitCount(); 370 } else { 371 incrementMissCount(); 372 } 373 374 return value; 375 } 376 377 382 public boolean contains(Object key) { 383 return (get(key) != null); 384 } 385 386 391 public Iterator getAll(Object key) { 392 int hashCode = hash(key); 393 int index = getIndex(hashCode); 394 395 ArrayList valueList = new ArrayList (entryCount); 396 synchronized (bucketLocks[index]) { 397 CacheItem item = buckets[index]; 398 399 for (; item != null; item = item.next) { 400 if ( (hashCode == item.hashCode) && eq(key, item.key) ) { 401 incrementHitCount(); 402 valueList.add(item.getValue()); 403 } 404 } 405 406 } 407 408 return valueList.iterator(); 409 } 410 411 415 public Iterator keys() { 416 ArrayList keyList = new ArrayList (entryCount); 417 418 for (int index=0; index < maxBuckets; index++) { 419 synchronized (bucketLocks[index]) { 420 for (CacheItem item = buckets[index]; item != null; 421 item = item.next) { 422 keyList.add(item.key); 423 } 424 } 425 } 426 427 return keyList.iterator(); 428 } 429 430 435 public Enumeration elements() { 436 Vector keyList = new Vector (); 437 438 for (int index=0; index < maxBuckets; index++) { 439 synchronized (bucketLocks[index]) { 440 for (CacheItem item = buckets[index]; item != null; 441 item = item.next) { 442 keyList.addElement(item.value); 443 } 444 } 445 } 446 447 return keyList.elements(); 448 } 449 450 454 public Iterator values() { 455 ArrayList valueList = new ArrayList (entryCount); 456 457 for (int index=0; index < maxBuckets; index++) { 458 synchronized (bucketLocks[index]) { 459 for (CacheItem item = buckets[index]; item != null; 460 item = item.next) { 461 valueList.add(item.value); 462 } 463 } 464 } 465 466 return valueList.iterator(); 467 } 468 469 475 public Object put(Object key, Object value) { 476 int hashCode = hash(key); 477 478 return _put(hashCode, key, value, -1, false); 479 } 480 481 488 public Object put(Object key, Object value, int size) { 489 int hashCode = hash(key); 490 491 return _put(hashCode, key, value, size, false); 492 } 493 494 499 public void add(Object key, Object value) { 500 int hashCode = hash(key); 501 502 _put(hashCode, key, value, -1, true); 503 } 504 505 513 public void add(Object key, Object value, int size) { 514 int hashCode = hash(key); 515 516 _put(hashCode, key, value, size, true); 517 } 518 519 532 protected Object _put(int hashCode, Object key, 533 Object value, int size, boolean addValue) { 534 int index = getIndex(hashCode); 535 536 CacheItem item, newItem = null, oldItem = null, overflow = null; 537 Object oldValue; 538 int oldSize = 0; 539 540 synchronized (bucketLocks[index]) { 542 for (item = buckets[index]; item != null; item = item.next) { 543 if ((hashCode == item.hashCode) && eq(key, item.key)) { 544 545 oldItem = item; 546 break; 547 } 548 } 549 550 if (addValue || oldItem == null) { 552 newItem = createItem(hashCode, key, value, size); 553 554 newItem.next = buckets[index]; 556 buckets[index] = newItem; 557 558 oldValue = null; 559 overflow = itemAdded(newItem); 560 } else { 561 oldSize = oldItem.getSize(); 562 oldValue = oldItem.refreshValue(value, size); 563 itemRefreshed(oldItem, oldSize); 564 } 565 } 566 567 if (newItem != null) { 568 incrementEntryCount(); 569 incrementAddCount(); 570 571 if (overflow != null) { 573 trimItem(overflow); 574 } 575 } else { 576 incrementRefreshCount(); 577 } 578 579 return oldValue; 580 } 581 582 587 public Object remove(Object key) { 588 int hashCode = hash(key); 589 590 Object retVal = null; 591 CacheItem removed = _remove( hashCode, key, null); 592 593 if (removed != null) { 594 retVal = removed.getValue(); 595 } 596 return retVal; 597 } 598 599 605 public Object remove(int hashCode, Object key) { 606 Object retVal = null; 607 CacheItem removed = _remove( hashCode, key, null); 608 609 if (removed != null) { 610 retVal = removed.getValue(); 611 } 612 return retVal; 613 } 614 615 621 public Object remove(Object key, Object value) { 622 int hashCode = hash(key); 623 624 Object retVal = null; 625 CacheItem removed = _remove( hashCode, key, value); 626 627 if (removed != null) { 628 retVal = removed.getValue(); 629 } 630 return retVal; 631 } 632 633 640 protected CacheItem _remove(int hashCode, Object key, Object value) { 641 int index = getIndex(hashCode); 642 643 CacheItem prev = null, item = null; 644 645 synchronized (bucketLocks[index]) { 646 for (item = buckets[index]; item != null; item = item.next) { 647 if (hashCode == item.hashCode && key.equals(item.key)) { 648 649 if (value == null || value == item.value) { 650 651 if (prev == null) { 652 buckets[index] = item.next; 653 } else { 654 prev.next = item.next; 655 } 656 item.next = null; 657 658 itemRemoved(item); 659 break; 660 } 661 } 662 prev = item; 663 } 664 } 665 666 if (item != null) { 667 decrementEntryCount(); 668 incrementRemovalCount(); 669 670 incrementHitCount(); 671 } else { 672 incrementMissCount(); 673 } 674 675 return item; 676 } 677 678 683 protected CacheItem _removeItem(CacheItem ritem) { 684 685 int index = getIndex(ritem.hashCode); 686 687 CacheItem prev = null, item = null; 688 689 synchronized (bucketLocks[index]) { 690 for (item = buckets[index]; item != null; item = item.next) { 691 if (item == ritem) { 692 if (prev == null) { 693 buckets[index] = item.next; 694 } else { 695 prev.next = item.next; 696 } 697 item.next = null; 698 break; 699 } 700 prev = item; 701 } 702 } 703 704 if (item != null) { 705 decrementEntryCount(); 706 } 707 708 return item; 709 } 710 711 715 public void removeAll(Object key) { 716 int hashCode = hash(key); 717 int index = getIndex(hashCode); 718 719 CacheItem prev = null, item = null; 720 ArrayList items = new ArrayList (entryCount); 721 722 synchronized (bucketLocks[index]) { 723 for (item = buckets[index]; item != null; 724 item = item.next) { 725 if (hashCode == item.hashCode && key.equals(item.key)) { 726 if (prev == null) { 727 buckets[index] = item.next; 728 } else { 729 prev.next = item.next; 730 } 731 item.next = null; 732 733 decrementEntryCount(); 734 incrementRemovalCount(); 735 736 items.add(item); 737 } 738 prev = item; 739 } 740 } 741 742 for (int i = 0; i < items.size(); i++) { 744 itemRemoved((CacheItem)items.get(i)); 745 } 746 } 747 748 752 protected void trimItem(CacheItem item) { 753 CacheItem removed = _removeItem(item); 754 755 if (removed != null) { 756 for (int i = 0; i < listeners.size(); i++) { 757 CacheListener listener = (CacheListener) listeners.get(i); 758 listener.trimEvent(removed.key, removed.value); 759 } 760 } 761 } 762 763 769 public boolean waitRefresh(int index) { 770 synchronized (bucketLocks[index]) { 771 if (refreshFlags[index] == false) { 772 refreshFlags[index] = true; 773 return false; 774 } 775 776 try { 778 bucketLocks[index].wait(); 779 } catch (InterruptedException ie) {} 780 } 781 return true; 782 } 783 784 789 public void notifyRefresh(int index) { 790 synchronized (bucketLocks[index]) { 792 refreshFlags[index] = false; 793 bucketLocks[index].notifyAll(); 794 } 795 } 796 797 801 public int clear() { 802 803 CacheItem item=null, next=null; 804 int count = 0; 805 806 for (int index = 0; index < maxBuckets; index++) { 807 synchronized (bucketLocks[index]) { 808 for (item = buckets[index]; item != null; 809 item = item.next) { 810 next = item.next; 811 item.next = null; 812 813 count++; 814 decrementEntryCount(); 815 itemRemoved(item); 816 817 if (entryCount == 0) { 818 break; 819 } 820 } 821 buckets[index] = null; 822 } 823 } 824 825 return count; 826 } 827 828 835 public void trimExpiredEntries(int maxCount) {} 836 837 841 public int getEntryCount() { 842 return entryCount; 843 } 844 845 846 847 851 public boolean isEmpty() { 852 return (entryCount == 0); 853 } 854 855 858 protected final void incrementEntryCount() { 859 synchronized(entryCountLk) { 860 entryCount++; 861 } 862 } 863 864 protected final void decrementEntryCount() { 865 synchronized(entryCountLk) { 866 entryCount--; 867 } 868 } 869 870 protected final void incrementHitCount() { 871 synchronized (hitCountLk) { 872 hitCount++; 873 } 874 } 875 876 protected final void incrementMissCount() { 877 synchronized (missCountLk) { 878 missCount++; 879 } 880 } 881 882 protected final void incrementRemovalCount() { 883 synchronized (removalCountLk) { 884 removalCount++; 885 } 886 } 887 888 protected final void incrementRefreshCount() { 889 synchronized (refreshCountLk) { 890 refreshCount++; 891 } 892 } 893 894 protected final void incrementAddCount() { 895 synchronized (addCountLk) { 896 addCount++; 897 } 898 } 899 900 protected final void incrementOverflowCount() { 901 synchronized (overflowCountLk) { 902 overflowCount++; 903 } 904 } 905 906 909 910 916 public Object getStatByName(String key) { 917 Object stat = null; 918 919 if (key == null) 920 return null; 921 922 if (key.equals(Constants.STAT_BASECACHE_MAX_ENTRIES)) 923 stat = new Integer (maxEntries); 924 else if (key.equals(Constants.STAT_BASECACHE_THRESHOLD)) 925 stat = new Integer (threshold); 926 else if (key.equals(Constants.STAT_BASECACHE_TABLE_SIZE)) 927 stat = new Integer (maxBuckets); 928 else if (key.equals(Constants.STAT_BASECACHE_ENTRY_COUNT)) 929 stat = new Integer (entryCount); 930 else if (key.equals(Constants.STAT_BASECACHE_HIT_COUNT)) 931 stat = new Integer (hitCount); 932 else if (key.equals(Constants.STAT_BASECACHE_MISS_COUNT)) 933 stat = new Integer (missCount); 934 else if (key.equals(Constants.STAT_BASECACHE_REMOVAL_COUNT)) 935 stat = new Integer (removalCount); 936 else if (key.equals(Constants.STAT_BASECACHE_REFRESH_COUNT)) 937 stat = new Integer (refreshCount); 938 else if (key.equals(Constants.STAT_BASECACHE_OVERFLOW_COUNT)) 939 stat = new Integer (overflowCount); 940 else if (key.equals(Constants.STAT_BASECACHE_ADD_COUNT)) 941 stat = new Integer (addCount); 942 943 return stat; 944 } 945 946 951 public Map getStats() { 952 HashMap stats = new HashMap (); 953 954 stats.put(Constants.STAT_BASECACHE_MAX_ENTRIES, 955 new Integer (maxEntries)); 956 stats.put(Constants.STAT_BASECACHE_THRESHOLD, 957 new Integer (threshold)); 958 stats.put(Constants.STAT_BASECACHE_TABLE_SIZE, 959 new Integer (maxBuckets)); 960 stats.put(Constants.STAT_BASECACHE_ENTRY_COUNT, 961 new Integer (entryCount)); 962 stats.put(Constants.STAT_BASECACHE_HIT_COUNT, 963 new Integer (hitCount)); 964 stats.put(Constants.STAT_BASECACHE_MISS_COUNT, 965 new Integer (missCount)); 966 stats.put(Constants.STAT_BASECACHE_REMOVAL_COUNT, 967 new Integer (removalCount)); 968 stats.put(Constants.STAT_BASECACHE_REFRESH_COUNT, 969 new Integer (refreshCount)); 970 stats.put(Constants.STAT_BASECACHE_OVERFLOW_COUNT, 971 new Integer (overflowCount)); 972 stats.put(Constants.STAT_BASECACHE_ADD_COUNT, 973 new Integer (addCount)); 974 975 return stats; 976 } 977 978 981 public void clearStats() { 982 hitCount = 0; 983 missCount = 0; 984 removalCount = 0; 985 refreshCount = 0; 986 overflowCount = 0; 987 addCount = 0; 988 } 989 990 991 protected static class CacheItem { 992 int hashCode; 993 Object key; 994 Object value; 995 int size; 996 997 CacheItem next; 998 999 protected CacheItem(int hashCode, Object key, Object value, int size) { 1000 this.hashCode = hashCode; 1001 this.key = key; 1002 this.value = value; 1003 this.size = size; 1004 } 1005 1006 1009 protected int getHashCode() { 1010 return hashCode; 1011 } 1012 1013 1016 protected Object getKey() { 1017 return key; 1018 } 1019 1020 1023 protected Object getValue() { 1024 return value; 1025 } 1026 1027 1031 protected int getSize() { 1032 return size; 1033 } 1034 1035 1040 protected Object refreshValue(Object value, int newSize) { 1041 Object oldValue = this.value; 1042 this.value = value; 1043 this.size = newSize; 1044 1045 return oldValue; 1046 } 1047 1048 public String toString() { 1049 return "key: " + key + "; value: " + value.toString(); 1050 } 1051 } 1052} 1053 | Popular Tags |