Path: blob/jdk8u272-b10-aarch32-20201026/jdk/src/share/classes/sun/security/util/Cache.java
83409 views
/*1* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import java.util.*;28import java.lang.ref.*;2930/**31* Abstract base class and factory for caches. A cache is a key-value mapping.32* It has properties that make it more suitable for caching than a Map.33*34* The factory methods can be used to obtain two different implementations.35* They have the following properties:36*37* . keys and values reside in memory38*39* . keys and values must be non-null40*41* . maximum size. Replacements are made in LRU order.42*43* . optional lifetime, specified in seconds.44*45* . safe for concurrent use by multiple threads46*47* . values are held by either standard references or via SoftReferences.48* SoftReferences have the advantage that they are automatically cleared49* by the garbage collector in response to memory demand. This makes it50* possible to simple set the maximum size to a very large value and let51* the GC automatically size the cache dynamically depending on the52* amount of available memory.53*54* However, note that because of the way SoftReferences are implemented in55* HotSpot at the moment, this may not work perfectly as it clears them fairly56* eagerly. Performance may be improved if the Java heap size is set to larger57* value using e.g. java -ms64M -mx128M foo.Test58*59* Cache sizing: the memory cache is implemented on top of a LinkedHashMap.60* In its current implementation, the number of buckets (NOT entries) in61* (Linked)HashMaps is always a power of two. It is recommended to set the62* maximum cache size to value that uses those buckets fully. For example,63* if a cache with somewhere between 500 and 1000 entries is desired, a64* maximum size of 750 would be a good choice: try 1024 buckets, with a65* load factor of 0.75f, the number of entries can be calculated as66* buckets / 4 * 3. As mentioned above, with a SoftReference cache, it is67* generally reasonable to set the size to a fairly large value.68*69* @author Andreas Sterbenz70*/71public abstract class Cache<K,V> {7273protected Cache() {74// empty75}7677/**78* Return the number of currently valid entries in the cache.79*/80public abstract int size();8182/**83* Remove all entries from the cache.84*/85public abstract void clear();8687/**88* Add an entry to the cache.89*/90public abstract void put(K key, V value);9192/**93* Get a value from the cache.94*/95public abstract V get(Object key);9697/**98* Remove an entry from the cache.99*/100public abstract void remove(Object key);101102/**103* Set the maximum size.104*/105public abstract void setCapacity(int size);106107/**108* Set the timeout(in seconds).109*/110public abstract void setTimeout(int timeout);111112/**113* accept a visitor114*/115public abstract void accept(CacheVisitor<K,V> visitor);116117/**118* Return a new memory cache with the specified maximum size, unlimited119* lifetime for entries, with the values held by SoftReferences.120*/121public static <K,V> Cache<K,V> newSoftMemoryCache(int size) {122return new MemoryCache<>(true, size);123}124125/**126* Return a new memory cache with the specified maximum size, the127* specified maximum lifetime (in seconds), with the values held128* by SoftReferences.129*/130public static <K,V> Cache<K,V> newSoftMemoryCache(int size, int timeout) {131return new MemoryCache<>(true, size, timeout);132}133134/**135* Return a new memory cache with the specified maximum size, unlimited136* lifetime for entries, with the values held by standard references.137*/138public static <K,V> Cache<K,V> newHardMemoryCache(int size) {139return new MemoryCache<>(false, size);140}141142/**143* Return a dummy cache that does nothing.144*/145@SuppressWarnings("unchecked")146public static <K,V> Cache<K,V> newNullCache() {147return (Cache<K,V>) NullCache.INSTANCE;148}149150/**151* Return a new memory cache with the specified maximum size, the152* specified maximum lifetime (in seconds), with the values held153* by standard references.154*/155public static <K,V> Cache<K,V> newHardMemoryCache(int size, int timeout) {156return new MemoryCache<>(false, size, timeout);157}158159/**160* Utility class that wraps a byte array and implements the equals()161* and hashCode() contract in a way suitable for Maps and caches.162*/163public static class EqualByteArray {164165private final byte[] b;166private volatile int hash;167168public EqualByteArray(byte[] b) {169this.b = b;170}171172public int hashCode() {173int h = hash;174if (h == 0) {175h = b.length + 1;176for (int i = 0; i < b.length; i++) {177h += (b[i] & 0xff) * 37;178}179hash = h;180}181return h;182}183184public boolean equals(Object obj) {185if (this == obj) {186return true;187}188if (obj instanceof EqualByteArray == false) {189return false;190}191EqualByteArray other = (EqualByteArray)obj;192return Arrays.equals(this.b, other.b);193}194}195196public interface CacheVisitor<K,V> {197public void visit(Map<K,V> map);198}199200}201202class NullCache<K,V> extends Cache<K,V> {203204final static Cache<Object,Object> INSTANCE = new NullCache<>();205206private NullCache() {207// empty208}209210public int size() {211return 0;212}213214public void clear() {215// empty216}217218public void put(K key, V value) {219// empty220}221222public V get(Object key) {223return null;224}225226public void remove(Object key) {227// empty228}229230public void setCapacity(int size) {231// empty232}233234public void setTimeout(int timeout) {235// empty236}237238public void accept(CacheVisitor<K,V> visitor) {239// empty240}241242}243244class MemoryCache<K,V> extends Cache<K,V> {245246private final static float LOAD_FACTOR = 0.75f;247248// XXXX249private final static boolean DEBUG = false;250251private final Map<K, CacheEntry<K,V>> cacheMap;252private int maxSize;253private long lifetime;254255// ReferenceQueue is of type V instead of Cache<K,V>256// to allow SoftCacheEntry to extend SoftReference<V>257private final ReferenceQueue<V> queue;258259public MemoryCache(boolean soft, int maxSize) {260this(soft, maxSize, 0);261}262263public MemoryCache(boolean soft, int maxSize, int lifetime) {264this.maxSize = maxSize;265this.lifetime = lifetime * 1000;266if (soft)267this.queue = new ReferenceQueue<>();268else269this.queue = null;270271cacheMap = new LinkedHashMap<>(1, LOAD_FACTOR, true);272}273274/**275* Empty the reference queue and remove all corresponding entries276* from the cache.277*278* This method should be called at the beginning of each public279* method.280*/281private void emptyQueue() {282if (queue == null) {283return;284}285int startSize = cacheMap.size();286while (true) {287@SuppressWarnings("unchecked")288CacheEntry<K,V> entry = (CacheEntry<K,V>)queue.poll();289if (entry == null) {290break;291}292K key = entry.getKey();293if (key == null) {294// key is null, entry has already been removed295continue;296}297CacheEntry<K,V> currentEntry = cacheMap.remove(key);298// check if the entry in the map corresponds to the expired299// entry. If not, readd the entry300if ((currentEntry != null) && (entry != currentEntry)) {301cacheMap.put(key, currentEntry);302}303}304if (DEBUG) {305int endSize = cacheMap.size();306if (startSize != endSize) {307System.out.println("*** Expunged " + (startSize - endSize)308+ " entries, " + endSize + " entries left");309}310}311}312313/**314* Scan all entries and remove all expired ones.315*/316private void expungeExpiredEntries() {317emptyQueue();318if (lifetime == 0) {319return;320}321int cnt = 0;322long time = System.currentTimeMillis();323for (Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();324t.hasNext(); ) {325CacheEntry<K,V> entry = t.next();326if (entry.isValid(time) == false) {327t.remove();328cnt++;329}330}331if (DEBUG) {332if (cnt != 0) {333System.out.println("Removed " + cnt334+ " expired entries, remaining " + cacheMap.size());335}336}337}338339public synchronized int size() {340expungeExpiredEntries();341return cacheMap.size();342}343344public synchronized void clear() {345if (queue != null) {346// if this is a SoftReference cache, first invalidate() all347// entries so that GC does not have to enqueue them348for (CacheEntry<K,V> entry : cacheMap.values()) {349entry.invalidate();350}351while (queue.poll() != null) {352// empty353}354}355cacheMap.clear();356}357358public synchronized void put(K key, V value) {359emptyQueue();360long expirationTime = (lifetime == 0) ? 0 :361System.currentTimeMillis() + lifetime;362CacheEntry<K,V> newEntry = newEntry(key, value, expirationTime, queue);363CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);364if (oldEntry != null) {365oldEntry.invalidate();366return;367}368if (maxSize > 0 && cacheMap.size() > maxSize) {369expungeExpiredEntries();370if (cacheMap.size() > maxSize) { // still too large?371Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();372CacheEntry<K,V> lruEntry = t.next();373if (DEBUG) {374System.out.println("** Overflow removal "375+ lruEntry.getKey() + " | " + lruEntry.getValue());376}377t.remove();378lruEntry.invalidate();379}380}381}382383public synchronized V get(Object key) {384emptyQueue();385CacheEntry<K,V> entry = cacheMap.get(key);386if (entry == null) {387return null;388}389long time = (lifetime == 0) ? 0 : System.currentTimeMillis();390if (entry.isValid(time) == false) {391if (DEBUG) {392System.out.println("Ignoring expired entry");393}394cacheMap.remove(key);395return null;396}397return entry.getValue();398}399400public synchronized void remove(Object key) {401emptyQueue();402CacheEntry<K,V> entry = cacheMap.remove(key);403if (entry != null) {404entry.invalidate();405}406}407408public synchronized void setCapacity(int size) {409expungeExpiredEntries();410if (size > 0 && cacheMap.size() > size) {411Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();412for (int i = cacheMap.size() - size; i > 0; i--) {413CacheEntry<K,V> lruEntry = t.next();414if (DEBUG) {415System.out.println("** capacity reset removal "416+ lruEntry.getKey() + " | " + lruEntry.getValue());417}418t.remove();419lruEntry.invalidate();420}421}422423maxSize = size > 0 ? size : 0;424425if (DEBUG) {426System.out.println("** capacity reset to " + size);427}428}429430public synchronized void setTimeout(int timeout) {431emptyQueue();432lifetime = timeout > 0 ? timeout * 1000L : 0L;433434if (DEBUG) {435System.out.println("** lifetime reset to " + timeout);436}437}438439// it is a heavyweight method.440public synchronized void accept(CacheVisitor<K,V> visitor) {441expungeExpiredEntries();442Map<K,V> cached = getCachedEntries();443444visitor.visit(cached);445}446447private Map<K,V> getCachedEntries() {448Map<K,V> kvmap = new HashMap<>(cacheMap.size());449450for (CacheEntry<K,V> entry : cacheMap.values()) {451kvmap.put(entry.getKey(), entry.getValue());452}453454return kvmap;455}456457protected CacheEntry<K,V> newEntry(K key, V value,458long expirationTime, ReferenceQueue<V> queue) {459if (queue != null) {460return new SoftCacheEntry<>(key, value, expirationTime, queue);461} else {462return new HardCacheEntry<>(key, value, expirationTime);463}464}465466private static interface CacheEntry<K,V> {467468boolean isValid(long currentTime);469470void invalidate();471472K getKey();473474V getValue();475476}477478private static class HardCacheEntry<K,V> implements CacheEntry<K,V> {479480private K key;481private V value;482private long expirationTime;483484HardCacheEntry(K key, V value, long expirationTime) {485this.key = key;486this.value = value;487this.expirationTime = expirationTime;488}489490public K getKey() {491return key;492}493494public V getValue() {495return value;496}497498public boolean isValid(long currentTime) {499boolean valid = (currentTime <= expirationTime);500if (valid == false) {501invalidate();502}503return valid;504}505506public void invalidate() {507key = null;508value = null;509expirationTime = -1;510}511}512513private static class SoftCacheEntry<K,V>514extends SoftReference<V>515implements CacheEntry<K,V> {516517private K key;518private long expirationTime;519520SoftCacheEntry(K key, V value, long expirationTime,521ReferenceQueue<V> queue) {522super(value, queue);523this.key = key;524this.expirationTime = expirationTime;525}526527public K getKey() {528return key;529}530531public V getValue() {532return get();533}534535public boolean isValid(long currentTime) {536boolean valid = (currentTime <= expirationTime) && (get() != null);537if (valid == false) {538invalidate();539}540return valid;541}542543public void invalidate() {544clear();545key = null;546expirationTime = -1;547}548}549550}551552553