1 52 53 package com.go.trove.util; 54 55 import java.util.*; 56 import com.go.trove.util.tq.*; 57 58 91 public class Depot { 92 private Factory mDefaultFactory; 93 private Map mValidCache; 94 private Map mInvalidCache; 95 private TransactionQueue mQueue; 96 private long mTimeout; 97 98 private Map mRetrievers; 99 100 private final Object mExpireLock = new Object (); 101 private Map mExpirations; 102 103 111 public Depot(Factory factory, Map validCache, Map invalidCache, 112 TransactionQueue tq, long timeout) { 113 init(factory, validCache, invalidCache, tq, timeout); 114 } 115 116 124 public Depot(Factory factory, int cacheSize, 125 TransactionQueue tq, long timeout) { 126 Map valid, invalid; 127 128 if (cacheSize < 0) { 129 valid = Utils.VOID_MAP; 130 invalid = Utils.VOID_MAP; 131 } 132 else { 133 valid = new Cache(cacheSize); 134 invalid = new Cache((Cache)valid); 135 } 136 137 init(factory, valid, invalid, tq, timeout); 138 } 139 140 private void init(Factory factory, Map validCache, Map invalidCache, 141 TransactionQueue tq, long timeout) { 142 mDefaultFactory = factory; 143 mValidCache = Collections.synchronizedMap(validCache); 144 mInvalidCache = Collections.synchronizedMap(invalidCache); 145 mQueue = tq; 146 mTimeout = timeout; 147 148 mRetrievers = Collections.synchronizedMap(new HashMap()); 149 } 150 151 154 public int size() { 155 synchronized (mValidCache) { 156 return mValidCache.size() + mInvalidCache.size(); 157 } 158 } 159 160 public boolean isEmpty() { 161 synchronized (mValidCache) { 162 return mValidCache.isEmpty() && mInvalidCache.isEmpty(); 163 } 164 } 165 166 169 public int validSize() { 170 return mValidCache.size(); 171 } 172 173 176 public int invalidSize() { 177 return mInvalidCache.size(); 178 } 179 180 191 public Object get(Object key) { 192 return get(mDefaultFactory, key, mTimeout); 193 } 194 195 209 public Object get(Object key, long timeout) { 210 return get(mDefaultFactory, key, timeout); 211 } 212 213 225 public Object get(Factory factory, Object key) { 226 return get(factory, key, mTimeout); 227 } 228 229 244 public Object get(Factory factory, Object key, long timeout) { 245 Retriever r; 246 247 key = Utils.intern(key); 248 synchronized (key) { 249 Object value = mValidCache.get(key); 250 if (value != null || mValidCache.containsKey(key)) { 251 validTest: { 252 if (value instanceof Perishable) { 253 if (!((Perishable)value).isValid()) { 254 break validTest; 255 } 256 } 257 258 synchronized (mExpireLock) { 259 if (mExpirations == null) { 260 return value; 261 } 262 Long expire = (Long )mExpirations.get(key); 263 if (expire == null || 264 System.currentTimeMillis() <= expire.longValue()) { 265 return value; 267 } 268 269 mExpirations.remove(key); 271 } 272 } 273 274 mValidCache.remove(key); 275 mInvalidCache.put(key, value); 276 277 r = retrieve(factory, key, value, false); 278 } 279 else { 280 value = mInvalidCache.get(key); 281 282 if (value != null || mInvalidCache.containsKey(key)) { 283 r = retrieve(factory, key, value, false); 284 } 285 else { 286 timeout = -1; 288 r = retrieve(factory, key, value, true); 289 } 290 } 291 292 if (r == null) { 293 return value; 294 } 295 else { 296 return r.waitForValue(timeout); 297 } 298 } 299 } 300 301 309 public void invalidate(Object key) { 310 key = Utils.intern(key); 311 synchronized (key) { 312 if (mValidCache.containsKey(key)) { 313 Object value = mValidCache.remove(key); 314 mInvalidCache.put(key, value); 315 } 316 } 317 } 318 319 323 public void invalidateAll(Filter filter) { 324 synchronized (mValidCache) { 325 Iterator it = mValidCache.entrySet().iterator(); 326 while (it.hasNext()) { 327 Map.Entry entry = (Map.Entry)it.next(); 328 Object key = entry.getKey(); 329 if (filter.accept(key)) { 330 it.remove(); 331 mInvalidCache.put(key, entry.getValue()); 332 } 333 } 334 } 335 } 336 337 340 public void invalidateAll() { 341 synchronized (mValidCache) { 342 synchronized (mInvalidCache) { 343 mInvalidCache.putAll(mValidCache); 344 mValidCache.clear(); 345 } 346 } 347 } 348 349 358 public void put(Object key, Object value) { 359 key = Utils.intern(key); 360 synchronized (key) { 361 mInvalidCache.remove(key); 362 mValidCache.put(key, value); 363 Retriever r = (Retriever)mRetrievers.get(key); 364 if (r != null) { 365 r.bypassValue(value); 368 } 369 } 370 } 371 372 377 public Object remove(Object key) { 378 key = Utils.intern(key); 379 synchronized (key) { 380 if (mValidCache.containsKey(key)) { 381 return mValidCache.remove(key); 382 } 383 if (mInvalidCache.containsKey(key)) { 384 return mInvalidCache.remove(key); 385 } 386 } 387 return null; 388 } 389 390 394 public void removeAll(Filter filter) { 395 synchronized (mValidCache) { 396 synchronized (mInvalidCache) { 397 Iterator it = mValidCache.keySet().iterator(); 398 while (it.hasNext()) { 399 Object key = it.next(); 400 if (filter.accept(key)) { 401 it.remove(); 402 } 403 } 404 it = mInvalidCache.keySet().iterator(); 405 while (it.hasNext()) { 406 Object key = it.next(); 407 if (filter.accept(key)) { 408 it.remove(); 409 } 410 } 411 } 412 } 413 } 414 415 420 public void clear() { 421 synchronized (mValidCache) { 422 synchronized (mInvalidCache) { 423 mValidCache.clear(); 424 mInvalidCache.clear(); 425 } 426 } 427 } 428 429 void setExpiration(Object key, long duration) { 430 Long expire = new Long 431 (System.currentTimeMillis() + duration); 432 synchronized (mExpireLock) { 433 if (mExpirations == null) { 434 mExpirations = new IdentityMap(); 435 } 436 mExpirations.put(key, expire); 437 } 438 } 439 440 443 private Retriever retrieve(Factory factory, 444 Object key, 445 Object originalValue, 446 boolean priority) 447 { 448 Retriever r = (Retriever)mRetrievers.get(key); 449 450 if (r == null) { 451 r = new Retriever(factory, key, originalValue); 452 if (mQueue.enqueue(r)) { 453 mRetrievers.put(key, r); 454 } 455 else if (priority) { 456 mRetrievers.put(key, r); 458 r.service(); 459 } 460 else { 461 return null; 462 } 463 } 464 465 return r; 466 } 467 468 474 public interface Factory { 475 489 public Object create(Object key) throws InterruptedException ; 490 } 491 492 496 public interface PerishablesFactory extends Factory { 497 502 public long getValidDuration(); 503 } 504 505 509 public interface Perishable { 510 515 public boolean isValid(); 516 } 517 518 public interface Filter { 519 523 public boolean accept(Object key); 524 } 525 526 private class Retriever implements Transaction { 527 private final Factory mFactory; 528 private final Object mKey; 529 private Object mValue; 530 private boolean mDone; 531 532 535 public Retriever(Factory factory, Object key, Object originalValue) { 536 mFactory = factory; 537 mKey = key; 538 mValue = originalValue; 539 } 540 541 public Object waitForValue(long timeout) { 542 try { 543 synchronized (mKey) { 544 if (mDone || timeout == 0) { 545 return mValue; 546 } 547 548 if (timeout < 0) { 549 timeout = 0; 550 } 551 552 mKey.wait(timeout); 553 } 554 } 555 catch (InterruptedException e) { 556 } 557 558 return mValue; 559 } 560 561 public void bypassValue(Object value) { 562 synchronized (mKey) { 563 mValue = value; 564 mKey.notifyAll(); 565 } 566 } 567 568 public void service() { 569 try { 570 Thread t = Thread.currentThread(); 571 String originalName = t.getName(); 572 t.setName(originalName + ' ' + mKey); 573 try { 574 mValue = mFactory.create(mKey); 575 synchronized (mKey) { 576 if (mFactory instanceof PerishablesFactory) { 577 long duration = ((PerishablesFactory)mFactory) 578 .getValidDuration(); 579 if (duration <= 0) { 580 mInvalidCache.put(mKey, mValue); 581 mValidCache.remove(mKey); 582 } 583 else { 584 mInvalidCache.remove(mKey); 585 mValidCache.put(mKey, mValue); 586 setExpiration(mKey, duration); 587 } 588 } 589 else { 590 mInvalidCache.remove(mKey); 591 mValidCache.put(mKey, mValue); 592 } 593 } 594 } 595 catch (InterruptedException e) { 596 } 597 finally { 598 t.setName(originalName); 599 } 600 } 601 finally { 602 done(); 603 } 604 } 605 606 public void cancel() { 607 done(); 608 } 609 610 private void done() { 611 mDone = true; 612 mRetrievers.remove(mKey); 613 synchronized (mKey) { 614 mKey.notifyAll(); 615 } 616 } 617 } 618 } 619 | Popular Tags |