< prev index next >

src/java.base/share/classes/sun/security/util/Cache.java

Print this page




  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.util;
  27 
  28 import java.util.*;
  29 import java.lang.ref.*;




  30 
  31 /**
  32  * Abstract base class and factory for caches. A cache is a key-value mapping.
  33  * It has properties that make it more suitable for caching than a Map.
  34  *
  35  * The factory methods can be used to obtain two different implementations.
  36  * They have the following properties:
  37  *
  38  *  . keys and values reside in memory
  39  *
  40  *  . keys and values must be non-null
  41  *
  42  *  . maximum size. Replacements are made in LRU order.
  43  *
  44  *  . optional lifetime, specified in seconds.
  45  *
  46  *  . safe for concurrent use by multiple threads
  47  *
  48  *  . values are held by either standard references or via SoftReferences.
  49  *    SoftReferences have the advantage that they are automatically cleared


 223     public void remove(Object key) {
 224         // empty
 225     }
 226 
 227     public void setCapacity(int size) {
 228         // empty
 229     }
 230 
 231     public void setTimeout(int timeout) {
 232         // empty
 233     }
 234 
 235     public void accept(CacheVisitor<K,V> visitor) {
 236         // empty
 237     }
 238 
 239 }
 240 
 241 class MemoryCache<K,V> extends Cache<K,V> {
 242 
 243     private static final float LOAD_FACTOR = 0.75f;
 244 
 245     // XXXX
 246     private static final boolean DEBUG = false;
 247 
 248     private final Map<K, CacheEntry<K,V>> cacheMap;
 249     private int maxSize;
 250     private long lifetime;

 251 
 252     // ReferenceQueue is of type V instead of Cache<K,V>
 253     // to allow SoftCacheEntry to extend SoftReference<V>
 254     private final ReferenceQueue<V> queue;
 255 
 256     public MemoryCache(boolean soft, int maxSize) {
 257         this(soft, maxSize, 0);
 258     }
 259 
 260     public MemoryCache(boolean soft, int maxSize, int lifetime) {
 261         this.maxSize = maxSize;
 262         this.lifetime = lifetime * 1000;
 263         if (soft)
 264             this.queue = new ReferenceQueue<>();
 265         else
 266             this.queue = null;
 267 
 268         int buckets = (int)(maxSize / LOAD_FACTOR) + 1;
 269         cacheMap = new LinkedHashMap<>(buckets, LOAD_FACTOR, true);
 270     }
 271 
 272     /**
 273      * Empty the reference queue and remove all corresponding entries
 274      * from the cache.
 275      *
 276      * This method should be called at the beginning of each public
 277      * method.
 278      */
 279     private void emptyQueue() {
 280         if (queue == null) {
 281             return;
 282         }
 283         int startSize = cacheMap.size();
 284         while (true) {
 285             @SuppressWarnings("unchecked")
 286             CacheEntry<K,V> entry = (CacheEntry<K,V>)queue.poll();
 287             if (entry == null) {
 288                 break;
 289             }
 290             K key = entry.getKey();
 291             if (key == null) {
 292                 // key is null, entry has already been removed
 293                 continue;
 294             }
 295             CacheEntry<K,V> currentEntry = cacheMap.remove(key);
 296             // check if the entry in the map corresponds to the expired
 297             // entry. If not, readd the entry
 298             if ((currentEntry != null) && (entry != currentEntry)) {
 299                 cacheMap.put(key, currentEntry);
 300             }
 301         }
 302         if (DEBUG) {
 303             int endSize = cacheMap.size();
 304             if (startSize != endSize) {
 305                 System.out.println("*** Expunged " + (startSize - endSize)
 306                         + " entries, " + endSize + " entries left");
 307             }
 308         }
 309     }
 310 
 311     /**
 312      * Scan all entries and remove all expired ones.




 313      */
 314     private void expungeExpiredEntries() {
 315         emptyQueue();



 316         if (lifetime == 0) {
 317             return;
 318         }
 319         int cnt = 0;
 320         long time = System.currentTimeMillis();
 321         for (Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
 322                 t.hasNext(); ) {
 323             CacheEntry<K,V> entry = t.next();
 324             if (entry.isValid(time) == false) {
 325                 t.remove();
 326                 cnt++;
 327             }


 328         }
 329         if (DEBUG) {
 330             if (cnt != 0) {
 331                 System.out.println("Removed " + cnt
 332                         + " expired entries, remaining " + cacheMap.size());
 333             }
 334         }
 335     }
 336 
 337     public synchronized int size() {
 338         expungeExpiredEntries();
 339         return cacheMap.size();












 340     }
 341 
 342     public synchronized void clear() {
 343         if (queue != null) {
 344             // if this is a SoftReference cache, first invalidate() all
 345             // entries so that GC does not have to enqueue them
 346             for (CacheEntry<K,V> entry : cacheMap.values()) {
 347                 entry.invalidate();
 348             }
 349             while (queue.poll() != null) {
 350                 // empty
 351             }
 352         }
 353         cacheMap.clear();
 354     }
 355 
 356     public synchronized void put(K key, V value) {
 357         emptyQueue();
 358         long expirationTime = (lifetime == 0) ? 0 :
 359                                         System.currentTimeMillis() + lifetime;
 360         CacheEntry<K,V> newEntry = newEntry(key, value, expirationTime, queue);
 361         CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);
 362         if (oldEntry != null) {
 363             oldEntry.invalidate();
 364             return;
 365         }
 366         if (maxSize > 0 && cacheMap.size() > maxSize) {
 367             expungeExpiredEntries();
 368             if (cacheMap.size() > maxSize) { // still too large?
 369                 Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
 370                 CacheEntry<K,V> lruEntry = t.next();
 371                 if (DEBUG) {
 372                     System.out.println("** Overflow removal "
 373                         + lruEntry.getKey() + " | " + lruEntry.getValue());
 374                 }
 375                 t.remove();
 376                 lruEntry.invalidate();
 377             }










 378         }

 379     }
 380 
 381     public synchronized V get(Object key) {
 382         emptyQueue();
 383         CacheEntry<K,V> entry = cacheMap.get(key);
 384         if (entry == null) {
 385             return null;
 386         }
 387         long time = (lifetime == 0) ? 0 : System.currentTimeMillis();
 388         if (entry.isValid(time) == false) {
 389             if (DEBUG) {
 390                 System.out.println("Ignoring expired entry");
 391             }
 392             cacheMap.remove(key);




 393             return null;
 394         }
 395         return entry.getValue();
 396     }
 397 
 398     public synchronized void remove(Object key) {
 399         emptyQueue();
 400         CacheEntry<K,V> entry = cacheMap.remove(key);
 401         if (entry != null) {



 402             entry.invalidate();
 403         }
 404     }
 405 
 406     public synchronized void setCapacity(int size) {
 407         expungeExpiredEntries();
 408         if (size > 0 && cacheMap.size() > size) {
 409             Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
 410             for (int i = cacheMap.size() - size; i > 0; i--) {
 411                 CacheEntry<K,V> lruEntry = t.next();
 412                 if (DEBUG) {
 413                     System.out.println("** capacity reset removal "
 414                         + lruEntry.getKey() + " | " + lruEntry.getValue());
 415                 }
 416                 t.remove();
 417                 lruEntry.invalidate();
 418             }
 419         }
 420 
 421         maxSize = size > 0 ? size : 0;




 422 
 423         if (DEBUG) {
 424             System.out.println("** capacity reset to " + size);
 425         }
 426     }
 427 
 428     public synchronized void setTimeout(int timeout) {
 429         emptyQueue();
 430         lifetime = timeout > 0 ? timeout * 1000L : 0L;
 431 
 432         if (DEBUG) {
 433             System.out.println("** lifetime reset to " + timeout);
 434         }
 435     }
 436 
 437     // it is a heavyweight method.
 438     public synchronized void accept(CacheVisitor<K,V> visitor) {
 439         expungeExpiredEntries();
 440         Map<K,V> cached = getCachedEntries();
 441 
 442         visitor.visit(cached);
 443     }
 444 

 445     private Map<K,V> getCachedEntries() {

 446         Map<K,V> kvmap = new HashMap<>(cacheMap.size());
 447 
 448         for (CacheEntry<K,V> entry : cacheMap.values()) {
 449             kvmap.put(entry.getKey(), entry.getValue());




 450         }
 451 
 452         return kvmap;
 453     }
 454 
 455     protected CacheEntry<K,V> newEntry(K key, V value,
 456             long expirationTime, ReferenceQueue<V> queue) {
 457         if (queue != null) {
 458             return new SoftCacheEntry<>(key, value, expirationTime, queue);
 459         } else {
 460             return new HardCacheEntry<>(key, value, expirationTime);
 461         }
 462     }
 463 
 464     private static interface CacheEntry<K,V> {
 465 
 466         boolean isValid(long currentTime);
 467 
 468         void invalidate();
 469 
 470         K getKey();
 471 
 472         V getValue();
 473 































































 474     }
 475 
 476     private static class HardCacheEntry<K,V> implements CacheEntry<K,V> {
 477 
 478         private K key;
 479         private V value;
 480         private long expirationTime;

 481 
 482         HardCacheEntry(K key, V value, long expirationTime) {
 483             this.key = key;
 484             this.value = value;
 485             this.expirationTime = expirationTime;
 486         }
 487 
 488         public K getKey() {
 489             return key;
 490         }
 491 
 492         public V getValue() {
 493             return value;
 494         }
 495 
 496         public boolean isValid(long currentTime) {
 497             boolean valid = (currentTime <= expirationTime);
 498             if (valid == false) {
 499                 invalidate();
 500             }
 501             return valid;
 502         }
 503 
 504         public void invalidate() {
 505             key = null;
 506             value = null;
 507             expirationTime = -1;



























 508         }
 509     }
 510 
 511     private static class SoftCacheEntry<K,V>
 512             extends SoftReference<V>
 513             implements CacheEntry<K,V> {
 514 
 515         private K key;
 516         private long expirationTime;

 517 
 518         SoftCacheEntry(K key, V value, long expirationTime,
 519                 ReferenceQueue<V> queue) {
 520             super(value, queue);
 521             this.key = key;
 522             this.expirationTime = expirationTime;
 523         }
 524 
 525         public K getKey() {
 526             return key;
 527         }
 528 
 529         public V getValue() {
 530             return get();
 531         }
 532 
 533         public boolean isValid(long currentTime) {
 534             boolean valid = (currentTime <= expirationTime) && (get() != null);
 535             if (valid == false) {
 536                 invalidate();
 537             }
 538             return valid;
 539         }
 540 
 541         public void invalidate() {
 542             clear();
 543             key = null;
 544             expirationTime = -1;




























 545         }
 546     }
 547 
 548 }


  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.util;
  27 
  28 import java.util.*;
  29 import java.lang.ref.*;
  30 import java.util.concurrent.ConcurrentHashMap;
  31 import java.util.concurrent.ConcurrentMap;
  32 import java.util.concurrent.TimeUnit;
  33 import java.util.function.Predicate;
  34 
  35 /**
  36  * Abstract base class and factory for caches. A cache is a key-value mapping.
  37  * It has properties that make it more suitable for caching than a Map.
  38  *
  39  * The factory methods can be used to obtain two different implementations.
  40  * They have the following properties:
  41  *
  42  *  . keys and values reside in memory
  43  *
  44  *  . keys and values must be non-null
  45  *
  46  *  . maximum size. Replacements are made in LRU order.
  47  *
  48  *  . optional lifetime, specified in seconds.
  49  *
  50  *  . safe for concurrent use by multiple threads
  51  *
  52  *  . values are held by either standard references or via SoftReferences.
  53  *    SoftReferences have the advantage that they are automatically cleared


 227     public void remove(Object key) {
 228         // empty
 229     }
 230 
 231     public void setCapacity(int size) {
 232         // empty
 233     }
 234 
 235     public void setTimeout(int timeout) {
 236         // empty
 237     }
 238 
 239     public void accept(CacheVisitor<K,V> visitor) {
 240         // empty
 241     }
 242 
 243 }
 244 
 245 class MemoryCache<K,V> extends Cache<K,V> {
 246 


 247     // XXXX
 248     private static final boolean DEBUG = false;
 249 
 250     private final ConcurrentMap<K, CacheEntry<K,V>> cacheMap;
 251     private CacheEntry<K, V> lruEntry, mruEntry;
 252     private volatile int maxSize;
 253     private volatile long lifetime;
 254 
 255     // ReferenceQueue is of type V instead of Cache<K,V>
 256     // to allow SoftCacheEntry to extend SoftReference<V>
 257     private final ReferenceQueue<V> queue;
 258 
 259     public MemoryCache(boolean soft, int maxSize) {
 260         this(soft, maxSize, 0 /* no time out */);
 261     }
 262 
 263     public MemoryCache(boolean soft, int maxSize, int timeout) {
 264         this.maxSize = maxSize;
 265         this.lifetime = timeout < 0 ? 0 : TimeUnit.SECONDS.toNanos(timeout);
 266         if (soft)
 267             this.queue = new ReferenceQueue<>();
 268         else
 269             this.queue = null;
 270 
 271         cacheMap = new ConcurrentHashMap<>(maxSize);

 272     }
 273 
 274     /**
 275      * Drain the reference queue and remove all corresponding entries
 276      * from the cache.
 277      *
 278      * This method should be called at the beginning of each public
 279      * method.
 280      */
 281     private void drainQueue() {
 282         if (queue == null) {
 283             return;
 284         }
 285         int removedCount = 0;
 286         while (true) {
 287             @SuppressWarnings("unchecked")
 288             CacheEntry<K,V> ce = (CacheEntry<K,V>)queue.poll();
 289             if (ce == null) {
 290                 break;
 291             }
 292             if (ce.unlink(this)) {
 293                 if (cacheMap.remove(ce.getKey(), ce)) {
 294                     removedCount++;
 295                 }
 296                 // the one who unlinks is responsible for invalidating
 297                 ce.invalidate();




 298             }
 299         }
 300         if (DEBUG) {
 301             if (removedCount > 0) {
 302                 System.out.println("*** Expunged " + removedCount
 303                         + " entries, " + cacheMap.size() + " entries left");

 304             }
 305         }
 306     }
 307 
 308     /**
 309      * Drain the reference queue and remove all corresponding entries
 310      * from the cache. Then expunge all expired entries.
 311      *
 312      * This method should be called just before doing any decisions based
 313      * on the number of entries remaining in cache (i.e. cacheMap.size()).
 314      */
 315     private void drainQueueExpungeExpired() {
 316         drainQueue();
 317         int cnt = 0;
 318         long currentTime = System.nanoTime();
 319         long lifetime = this.lifetime;
 320         if (lifetime == 0) {
 321             return;
 322         }
 323         Predicate<CacheEntry<K, V>> entryIsInvalid =
 324             ce -> !ce.isValid(currentTime, lifetime);
 325         CacheEntry<K,V> lru;
 326         while ((lru = CacheEntry.unlinkLruIf(this, entryIsInvalid)) != null) {
 327             if (cacheMap.remove(lru.getKey(), lru)) {


 328                 cnt++;
 329             }
 330             // the one who unlinks is responsible for invalidating
 331             lru.invalidate();
 332         }
 333         if (DEBUG) {
 334             if (cnt != 0) {
 335                 System.out.println("Removed " + cnt
 336                         + " expired entries, remaining " + cacheMap.size());
 337             }
 338         }
 339     }
 340 
 341      /**
 342      * Remove LRU entries while cache size is greater than given {@code maxSize}.
 343      *
 344      * @param maxSize pack cache to size no more than
 345      */
 346     private void reduceToMaxSize(int maxSize) {
 347         if (maxSize > 0 && cacheMap.size() > maxSize) {
 348             // 1st get rid of all cleared and expired entries
 349             drainQueueExpungeExpired();
 350             while (cacheMap.size() > maxSize) { // while still too large
 351                 CacheEntry<K, V> lru = CacheEntry.unlinkLruIf(this, ce -> true);
 352                 if (lru != null) {
 353                     if (DEBUG) {
 354                         System.out.println("** Overflow removal "
 355                             + lru.getKey() + " | " + lru.getValue());
 356                     }
 357                     cacheMap.remove(lru.getKey(), lru);
 358                     // the one who unlinks is responsible for invalidating
 359                     lru.invalidate();




 360                 }


 361             }
 362         }

 363     }
 364 
 365    public int size() {
 366         drainQueueExpungeExpired();
 367         return cacheMap.size();






 368     }
 369 
 370     public void clear() {
 371         CacheEntry<K, V> lru;
 372         while ((lru = CacheEntry.unlinkLruIf(this, ce -> true)) != null) {
 373             cacheMap.remove(lru.getKey(), lru);
 374             // the one who unlinks is responsible for invalidating
 375             lru.invalidate();

 376         }


 377     }
 378 
 379     public void put(K key, V value) {
 380         drainQueue();
 381         CacheEntry<K,V> newEntry = newEntry(key, value, queue);
 382         newEntry.link(this);
 383         CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);
 384         if (oldEntry != null && oldEntry.unlink(this)) {
 385             // the one who unlinks is responsible for invalidating
 386             oldEntry.invalidate();
 387             return;
 388         }
 389         reduceToMaxSize(maxSize);
 390     }
 391 
 392     public V get(Object key) {
 393         drainQueue();
 394         CacheEntry<K,V> entry = cacheMap.get(key);
 395         if (entry == null) {
 396             return null;
 397         }
 398         // harmless data race: entry.isValid() vs. entry.invalidate()
 399         if (!entry.isValid(System.nanoTime(), lifetime)) {
 400             if (DEBUG) {
 401                 System.out.println("Ignoring expired entry");
 402             }
 403             if (entry.unlink(this)) {
 404                 cacheMap.remove(entry.getKey(), entry);
 405                 // the one who unlinks is responsible for invalidating
 406                 entry.invalidate();
 407             }
 408             return null;
 409         }
 410         return entry.getValue(); // harmless data race with entry.invalidate()
 411     }
 412 
 413     public void remove(Object key) {
 414         drainQueue();
 415         CacheEntry<K,V> entry = cacheMap.get(key);
 416         if (entry != null) {
 417             if (entry.unlink(this)) {
 418                 cacheMap.remove(entry.getKey(), entry);
 419                 // the one who unlinks is responsible for invalidating
 420                 entry.invalidate();
 421             }
 422         }














 423     }
 424 
 425     public void setCapacity(int size) {
 426         if (size < 0) size = 0;
 427         maxSize = size;
 428         // in case maxSize was reduces, immediately reduce the cache too
 429         reduceToMaxSize(size);
 430 
 431         if (DEBUG) {
 432             System.out.println("** capacity reset to " + size);
 433         }
 434     }
 435 
 436     public void setTimeout(int timeout) {
 437         this.lifetime = timeout < 0 ? 0 : TimeUnit.SECONDS.toNanos(timeout);
 438         // in case timeout was shortened, immediately expunge newly expired entries
 439         drainQueueExpungeExpired();
 440         if (DEBUG) {
 441             System.out.println("** lifetime reset to " + lifetime + " nanos");
 442         }
 443     }
 444 
 445     // it is a heavyweight method.
 446     public void accept(CacheVisitor<K,V> visitor) {

 447         Map<K,V> cached = getCachedEntries();

 448         visitor.visit(cached);
 449     }
 450 
 451     // return a snapshot of the valid/non-expired part of the cache
 452     private Map<K,V> getCachedEntries() {
 453         drainQueueExpungeExpired();
 454         Map<K,V> kvmap = new HashMap<>(cacheMap.size());
 455 
 456         for (CacheEntry<K,V> entry : cacheMap.values()) {
 457             K key = entry.getKey(); // harmless data race with entry.invalidate()
 458             V val = entry.getValue(); // harmless data race with entry.invalidate()
 459             if (key != null && val != null) {
 460                 kvmap.put(key, val);
 461             }
 462         }
 463 
 464         return kvmap;
 465     }
 466 
 467     protected CacheEntry<K,V> newEntry(K key, V value, ReferenceQueue<V> queue) {

 468         if (queue != null) {
 469             return new SoftCacheEntry<>(key, value, queue);
 470         } else {
 471             return new HardCacheEntry<>(key, value);
 472         }
 473     }
 474 
 475     private interface CacheEntry<K, V> {
 476 
 477         boolean isValid(long currentTime, long lifetime);
 478 
 479         void invalidate();
 480 
 481         K getKey();
 482 
 483         V getValue();
 484 
 485         // double-linked-list management
 486 
 487         CacheEntry<K,V> prev();
 488 
 489         CacheEntry<K,V> next();
 490 
 491         void setPrev(CacheEntry<K,V> newPrev);
 492 
 493         void setNext(CacheEntry<K,V> newNext);
 494 
 495         void entryLinked();
 496 
 497         default void link(MemoryCache<K, V> memoryCache) {
 498             synchronized (memoryCache) {
 499                 assert prev() == this && next() == this : "Entry already linked";
 500                 if (memoryCache.lruEntry == null) {
 501                     memoryCache.lruEntry = memoryCache.mruEntry = this;
 502                     setPrev(null); setNext(null);
 503                 } else {
 504                     setPrev(memoryCache.mruEntry);
 505                     memoryCache.mruEntry.setNext(this);
 506                     memoryCache.mruEntry = this;
 507                     setNext(null);
 508                 }
 509                 entryLinked();
 510             }
 511         }
 512 
 513         default boolean unlink(MemoryCache<K, V> memoryCache) {
 514             synchronized (memoryCache) {
 515                 CacheEntry<K, V> next = next();
 516                 CacheEntry<K, V> prev = prev();
 517                 if (next == this && prev == this) {
 518                     // not linked
 519                     return false;
 520                 }
 521                 if (memoryCache.lruEntry == this) {
 522                     memoryCache.lruEntry = next;
 523                 }
 524                 if (memoryCache.mruEntry == this) {
 525                     memoryCache.mruEntry = prev;
 526                 }
 527                 if (prev != null) {
 528                     prev.setNext(next);
 529                 }
 530                 if (next != null) {
 531                     next.setPrev(prev);
 532                 }
 533                 setPrev(this);
 534                 setNext(this);
 535                 return true;
 536             }
 537         }
 538 
 539         static <K, V> CacheEntry<K, V> unlinkLruIf(MemoryCache<K, V> memoryCache, Predicate<CacheEntry<K, V>> predicate) {
 540             synchronized (memoryCache) {
 541                 CacheEntry<K, V> lru = memoryCache.lruEntry;
 542                 if (lru == null || !predicate.test(lru)) {
 543                     return null;
 544                 }
 545                 return lru.unlink(memoryCache) ? lru : null;
 546             }
 547         }
 548     }
 549 
 550     private static class HardCacheEntry<K,V> implements CacheEntry<K,V> {
 551 
 552         private K key;
 553         private V value;
 554         private long linkTime;
 555         private CacheEntry<K, V> prev = this, next = this;
 556 
 557         HardCacheEntry(K key, V value) {
 558             this.key = key;
 559             this.value = value;

 560         }
 561 
 562         public K getKey() {
 563             return key;
 564         }
 565 
 566         public V getValue() {
 567             return value;
 568         }
 569 
 570         public boolean isValid(long currentTime, long lifetime) {
 571             return value != null &&
 572                    (lifetime == 0 || (currentTime - linkTime) <= lifetime);



 573         }
 574 
 575         public void invalidate() {
 576             key = null;
 577             value = null;
 578         }
 579 
 580         @Override
 581         public CacheEntry<K, V> prev() {
 582             return prev;
 583         }
 584 
 585         @Override
 586         public CacheEntry<K, V> next() {
 587             return next;
 588         }
 589 
 590         @Override
 591         public void setPrev(CacheEntry<K, V> newPrev) {
 592             prev = newPrev;
 593         }
 594 
 595         @Override
 596         public void setNext(CacheEntry<K, V> newNext) {
 597             next = newNext;
 598         }
 599 
 600         @Override
 601         public void entryLinked() {
 602             // sample link time while synchronized which guarantees
 603             // monotonic time increment (no decrement), so dl-list
 604             // is kept sorted by linkTime
 605             linkTime = System.nanoTime();
 606         }
 607     }
 608 
 609     private static class SoftCacheEntry<K,V>
 610             extends SoftReference<V>
 611             implements CacheEntry<K,V> {
 612 
 613         private K key;
 614         private long linkTime;
 615         private CacheEntry<K, V> prev = this, next = this;
 616 
 617         SoftCacheEntry(K key, V value, ReferenceQueue<V> queue) {

 618             super(value, queue);
 619             this.key = key;

 620         }
 621 
 622         public K getKey() {
 623             return key;
 624         }
 625 
 626         public V getValue() {
 627             return get();
 628         }
 629 
 630         public boolean isValid(long currentTime, long lifetime) {
 631             return get() != null &&
 632                    (lifetime == 0 || (currentTime - linkTime) <= lifetime);



 633         }
 634 
 635         public void invalidate() {

 636             key = null;
 637             clear();
 638         }
 639 
 640         @Override
 641         public CacheEntry<K, V> prev() {
 642             return prev;
 643         }
 644 
 645         @Override
 646         public CacheEntry<K, V> next() {
 647             return next;
 648         }
 649 
 650         @Override
 651         public void setPrev(CacheEntry<K, V> newPrev) {
 652             prev = newPrev;
 653         }
 654 
 655         @Override
 656         public void setNext(CacheEntry<K, V> newNext) {
 657             next = newNext;
 658         }
 659 
 660         @Override
 661         public void entryLinked() {
 662             // sample link time while synchronized which guarantees
 663             // monotonic time increment (no decrement), so dl-list
 664             // is kept sorted by linkTime
 665             linkTime = System.nanoTime();
 666         }
 667     }
 668 
 669 }
< prev index next >