1 11 12 package org.jivesoftware.util; 13 14 import java.io.IOException ; 15 import java.io.ObjectOutputStream ; 16 import java.io.OutputStream ; 17 import java.util.*; 18 19 44 public class Cache { 45 46 49 protected Map map; 50 51 55 protected org.jivesoftware.util.LinkedList lastAccessedList; 56 57 61 protected LinkedList ageList; 62 63 66 private int maxCacheSize; 67 68 71 private int cacheSize = 0; 72 73 76 protected long maxLifetime; 77 78 86 protected long cacheHits, cacheMisses = 0L; 87 88 91 private String name; 92 93 103 public Cache(String name, int maxSize, long maxLifetime) { 104 this.name = name; 105 this.maxCacheSize = maxSize; 106 this.maxLifetime = maxLifetime; 107 108 map = new HashMap(103); 111 112 lastAccessedList = new LinkedList(); 113 ageList = new LinkedList(); 114 } 115 116 public synchronized Object put(Object key, Object value) { 117 remove(key); 119 120 int objectSize = calculateSize(value); 121 122 if (maxCacheSize > 0 && objectSize > maxCacheSize * .90) { 124 Log.warn("Cache: " + name + " -- object with key " + key + 125 " is too large to fit in cache. Size is " + objectSize); 126 return value; 127 } 128 cacheSize += objectSize; 129 CacheObject cacheObject = new CacheObject(value, objectSize); 130 map.put(key, cacheObject); 131 LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key); 133 cacheObject.lastAccessedListNode = lastAccessedNode; 136 LinkedListNode ageNode = ageList.addFirst(key); 138 ageNode.timestamp = System.currentTimeMillis(); 141 cacheObject.ageListNode = ageNode; 142 143 cullCache(); 146 147 return value; 148 } 149 150 public synchronized Object get(Object key) { 151 deleteExpiredEntries(); 154 155 CacheObject cacheObject = (CacheObject)map.get(key); 156 if (cacheObject == null) { 157 cacheMisses++; 159 return null; 160 } 161 162 cacheHits++; 165 cacheObject.readCount++; 166 167 cacheObject.lastAccessedListNode.remove(); 170 lastAccessedList.addFirst(cacheObject.lastAccessedListNode); 171 172 return cacheObject.object; 173 } 174 175 public synchronized Object remove(Object key) { 176 CacheObject cacheObject = (CacheObject)map.get(key); 177 if (cacheObject == null) { 179 return null; 180 } 181 map.remove(key); 183 cacheObject.lastAccessedListNode.remove(); 185 cacheObject.ageListNode.remove(); 186 cacheObject.ageListNode = null; 188 cacheObject.lastAccessedListNode = null; 189 cacheSize -= cacheObject.size; 191 return cacheObject.object; 192 } 193 194 public synchronized void clear() { 195 Object [] keys = map.keySet().toArray(); 196 for (int i = 0; i < keys.length; i++) { 197 remove(keys[i]); 198 } 199 200 map.clear(); 202 lastAccessedList.clear(); 203 lastAccessedList = new LinkedList(); 204 ageList.clear(); 205 ageList = new LinkedList(); 206 207 cacheSize = 0; 208 cacheHits = 0; 209 cacheMisses = 0; 210 } 211 212 public int size() { 213 deleteExpiredEntries(); 216 217 return map.size(); 218 } 219 220 public boolean isEmpty() { 221 deleteExpiredEntries(); 224 225 return map.isEmpty(); 226 } 227 228 public Collection values() { 229 deleteExpiredEntries(); 232 233 Object [] cacheObjects = map.values().toArray(); 234 Object [] values = new Object [cacheObjects.length]; 235 for (int i = 0; i < cacheObjects.length; i++) { 236 values[i] = ((CacheObject)cacheObjects[i]).object; 237 } 238 return Collections.unmodifiableList(Arrays.asList(values)); 239 } 240 241 public boolean containsKey(Object key) { 242 deleteExpiredEntries(); 245 246 return map.containsKey(key); 247 } 248 249 public void putAll(Map map) { 250 for (Iterator i = map.keySet().iterator(); i.hasNext();) { 251 Object key = i.next(); 252 Object value = map.get(key); 253 put(key, value); 254 } 255 } 256 257 public boolean containsValue(Object value) { 258 deleteExpiredEntries(); 261 262 int objectSize = calculateSize(value); 263 CacheObject cacheObject = new CacheObject(value, objectSize); 264 return map.containsValue(cacheObject); 265 } 266 267 public Set entrySet() { 268 deleteExpiredEntries(); 271 272 return Collections.unmodifiableSet(map.entrySet()); 273 } 274 275 public Set keySet() { 276 deleteExpiredEntries(); 279 280 return Collections.unmodifiableSet(map.keySet()); 281 } 282 283 289 public String getName() { 290 return name; 291 } 292 293 303 public long getCacheHits() { 304 return cacheHits; 305 } 306 307 317 public long getCacheMisses() { 318 return cacheMisses; 319 } 320 321 329 public int getCacheSize() { 330 return cacheSize; 331 } 332 333 340 public int getMaxCacheSize() { 341 return maxCacheSize; 342 } 343 344 351 public void setMaxCacheSize(int maxCacheSize) { 352 this.maxCacheSize = maxCacheSize; 353 cullCache(); 356 } 357 358 366 public long getMaxLifetime() { 367 return maxLifetime; 368 } 369 370 378 public void setMaxLifetime(long maxLifetime) { 379 this.maxLifetime = maxLifetime; 380 } 381 382 388 private int calculateSize(Object object) { 389 if (object instanceof Cacheable) { 391 return ((Cacheable)object).getCachedSize(); 392 } 393 else if (object instanceof Long ) { 395 return CacheSizes.sizeOfLong(); 396 } 397 else if (object instanceof Integer ) { 398 return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt(); 399 } 400 else if (object instanceof Boolean ) { 401 return CacheSizes.sizeOfObject() + CacheSizes.sizeOfBoolean(); 402 } 403 else if (object instanceof long[]) { 404 long[] array = (long[])object; 405 return CacheSizes.sizeOfObject() + array.length * CacheSizes.sizeOfLong(); 406 } 407 else { 409 int size = 1; 410 try { 411 NullOutputStream out = new NullOutputStream(); 413 ObjectOutputStream outObj = new ObjectOutputStream (out); 414 outObj.writeObject(object); 415 size = out.size(); 416 } 417 catch (IOException ioe) { 418 Log.error(ioe); 419 } 420 return size; 421 } 422 } 423 424 428 protected void deleteExpiredEntries() { 429 if (maxLifetime <= 0) { 431 return; 432 } 433 434 LinkedListNode node = ageList.getLast(); 439 if (node == null) { 441 return; 442 } 443 444 long expireTime = System.currentTimeMillis() - maxLifetime; 448 449 while (expireTime > node.timestamp) { 450 remove(node.object); 452 453 node = ageList.getLast(); 455 if (node == null) { 457 return; 458 } 459 } 460 } 461 462 468 protected final void cullCache() { 469 if (maxCacheSize < 0) { 471 return; 472 } 473 474 if (cacheSize >= maxCacheSize * .97) { 477 deleteExpiredEntries(); 479 int desiredSize = (int)(maxCacheSize * .90); 480 while (cacheSize > desiredSize) { 481 remove(lastAccessedList.getLast().object); 483 } 484 } 485 } 486 487 492 private static class CacheObject { 493 494 497 public Object object; 498 499 505 public int size; 506 507 513 public LinkedListNode lastAccessedListNode; 514 515 520 public LinkedListNode ageListNode; 521 522 525 public int readCount = 0; 526 527 535 public CacheObject(Object object, int size) { 536 this.object = object; 537 this.size = size; 538 } 539 } 540 541 545 private static class NullOutputStream extends OutputStream { 546 547 int size = 0; 548 549 public void write(int b) throws IOException { 550 size++; 551 } 552 553 public void write(byte[] b) throws IOException { 554 size += b.length; 555 } 556 557 public void write(byte[] b, int off, int len) { 558 size += len; 559 } 560 561 566 public int size() { 567 return size; 568 } 569 } 570 } | Popular Tags |