| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.util;  | 
 | 
 | 
 | 
import java.util.*;  | 
 | 
import java.lang.ref.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public abstract class Cache<K,V> { | 
 | 
 | 
 | 
    protected Cache() { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract int size();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void clear();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void put(K key, V value);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract V get(Object key);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void remove(Object key);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void setCapacity(int size);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void setTimeout(int timeout);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public abstract void accept(CacheVisitor<K,V> visitor);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static <K,V> Cache<K,V> newSoftMemoryCache(int size) { | 
 | 
        return new MemoryCache<>(true, size);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static <K,V> Cache<K,V> newSoftMemoryCache(int size, int timeout) { | 
 | 
        return new MemoryCache<>(true, size, timeout);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static <K,V> Cache<K,V> newHardMemoryCache(int size) { | 
 | 
        return new MemoryCache<>(false, size);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public static <K,V> Cache<K,V> newNullCache() { | 
 | 
        return (Cache<K,V>) NullCache.INSTANCE;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static <K,V> Cache<K,V> newHardMemoryCache(int size, int timeout) { | 
 | 
        return new MemoryCache<>(false, size, timeout);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static class EqualByteArray { | 
 | 
 | 
 | 
        private final byte[] b;  | 
 | 
        private volatile int hash;  | 
 | 
 | 
 | 
        public EqualByteArray(byte[] b) { | 
 | 
            this.b = b;  | 
 | 
        }  | 
 | 
 | 
 | 
        public int hashCode() { | 
 | 
            int h = hash;  | 
 | 
            if (h == 0) { | 
 | 
                h = b.length + 1;  | 
 | 
                for (int i = 0; i < b.length; i++) { | 
 | 
                    h += (b[i] & 0xff) * 37;  | 
 | 
                }  | 
 | 
                hash = h;  | 
 | 
            }  | 
 | 
            return h;  | 
 | 
        }  | 
 | 
 | 
 | 
        public boolean equals(Object obj) { | 
 | 
            if (this == obj) { | 
 | 
                return true;  | 
 | 
            }  | 
 | 
            if (obj instanceof EqualByteArray == false) { | 
 | 
                return false;  | 
 | 
            }  | 
 | 
            EqualByteArray other = (EqualByteArray)obj;  | 
 | 
            return Arrays.equals(this.b, other.b);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public interface CacheVisitor<K,V> { | 
 | 
        public void visit(Map<K,V> map);  | 
 | 
    }  | 
 | 
 | 
 | 
}  | 
 | 
 | 
 | 
class NullCache<K,V> extends Cache<K,V> { | 
 | 
 | 
 | 
    final static Cache<Object,Object> INSTANCE = new NullCache<>();  | 
 | 
 | 
 | 
    private NullCache() { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public int size() { | 
 | 
        return 0;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void clear() { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public void put(K key, V value) { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public V get(Object key) { | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void remove(Object key) { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setCapacity(int size) { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setTimeout(int timeout) { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
    public void accept(CacheVisitor<K,V> visitor) { | 
 | 
        // empty  | 
 | 
    }  | 
 | 
 | 
 | 
}  | 
 | 
 | 
 | 
class MemoryCache<K,V> extends Cache<K,V> { | 
 | 
 | 
 | 
    private final static float LOAD_FACTOR = 0.75f;  | 
 | 
 | 
 | 
      | 
 | 
    private final static boolean DEBUG = false;  | 
 | 
 | 
 | 
    private final Map<K, CacheEntry<K,V>> cacheMap;  | 
 | 
    private int maxSize;  | 
 | 
    private long lifetime;  | 
 | 
    private long nextExpirationTime = Long.MAX_VALUE;  | 
 | 
 | 
 | 
    // ReferenceQueue is of type V instead of Cache<K,V>  | 
 | 
      | 
 | 
    private final ReferenceQueue<V> queue;  | 
 | 
 | 
 | 
    public MemoryCache(boolean soft, int maxSize) { | 
 | 
        this(soft, maxSize, 0);  | 
 | 
    }  | 
 | 
 | 
 | 
    public MemoryCache(boolean soft, int maxSize, int lifetime) { | 
 | 
        this.maxSize = maxSize;  | 
 | 
        this.lifetime = lifetime * 1000;  | 
 | 
        if (soft)  | 
 | 
            this.queue = new ReferenceQueue<>();  | 
 | 
        else  | 
 | 
            this.queue = null;  | 
 | 
 | 
 | 
        cacheMap = new LinkedHashMap<>(1, LOAD_FACTOR, true);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private void emptyQueue() { | 
 | 
        if (queue == null) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
        int startSize = cacheMap.size();  | 
 | 
        while (true) { | 
 | 
            @SuppressWarnings("unchecked") | 
 | 
            CacheEntry<K,V> entry = (CacheEntry<K,V>)queue.poll();  | 
 | 
            if (entry == null) { | 
 | 
                break;  | 
 | 
            }  | 
 | 
            K key = entry.getKey();  | 
 | 
            if (key == null) { | 
 | 
                  | 
 | 
                continue;  | 
 | 
            }  | 
 | 
            CacheEntry<K,V> currentEntry = cacheMap.remove(key);  | 
 | 
            // check if the entry in the map corresponds to the expired  | 
 | 
              | 
 | 
            if ((currentEntry != null) && (entry != currentEntry)) { | 
 | 
                cacheMap.put(key, currentEntry);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (DEBUG) { | 
 | 
            int endSize = cacheMap.size();  | 
 | 
            if (startSize != endSize) { | 
 | 
                System.out.println("*** Expunged " + (startSize - endSize) | 
 | 
                        + " entries, " + endSize + " entries left");  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private void expungeExpiredEntries() { | 
 | 
        emptyQueue();  | 
 | 
        if (lifetime == 0) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
        int cnt = 0;  | 
 | 
        long time = System.currentTimeMillis();  | 
 | 
        if (nextExpirationTime > time) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
        nextExpirationTime = Long.MAX_VALUE;  | 
 | 
        for (Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();  | 
 | 
                t.hasNext(); ) { | 
 | 
            CacheEntry<K,V> entry = t.next();  | 
 | 
            if (entry.isValid(time) == false) { | 
 | 
                t.remove();  | 
 | 
                cnt++;  | 
 | 
            } else if (nextExpirationTime > entry.getExpirationTime()) { | 
 | 
                nextExpirationTime = entry.getExpirationTime();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (DEBUG) { | 
 | 
            if (cnt != 0) { | 
 | 
                System.out.println("Removed " + cnt | 
 | 
                        + " expired entries, remaining " + cacheMap.size());  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized int size() { | 
 | 
        expungeExpiredEntries();  | 
 | 
        return cacheMap.size();  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized void clear() { | 
 | 
        if (queue != null) { | 
 | 
            // if this is a SoftReference cache, first invalidate() all  | 
 | 
              | 
 | 
            for (CacheEntry<K,V> entry : cacheMap.values()) { | 
 | 
                entry.invalidate();  | 
 | 
            }  | 
 | 
            while (queue.poll() != null) { | 
 | 
                // empty  | 
 | 
            }  | 
 | 
        }  | 
 | 
        cacheMap.clear();  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized void put(K key, V value) { | 
 | 
        emptyQueue();  | 
 | 
        long expirationTime = (lifetime == 0) ? 0 :  | 
 | 
                                        System.currentTimeMillis() + lifetime;  | 
 | 
        if (expirationTime < nextExpirationTime) { | 
 | 
            nextExpirationTime = expirationTime;  | 
 | 
        }  | 
 | 
        CacheEntry<K,V> newEntry = newEntry(key, value, expirationTime, queue);  | 
 | 
        CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);  | 
 | 
        if (oldEntry != null) { | 
 | 
            oldEntry.invalidate();  | 
 | 
            return;  | 
 | 
        }  | 
 | 
        if (maxSize > 0 && cacheMap.size() > maxSize) { | 
 | 
            expungeExpiredEntries();  | 
 | 
            if (cacheMap.size() > maxSize) {  | 
 | 
                Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();  | 
 | 
                CacheEntry<K,V> lruEntry = t.next();  | 
 | 
                if (DEBUG) { | 
 | 
                    System.out.println("** Overflow removal " | 
 | 
                        + lruEntry.getKey() + " | " + lruEntry.getValue());  | 
 | 
                }  | 
 | 
                t.remove();  | 
 | 
                lruEntry.invalidate();  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized V get(Object key) { | 
 | 
        emptyQueue();  | 
 | 
        CacheEntry<K,V> entry = cacheMap.get(key);  | 
 | 
        if (entry == null) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        long time = (lifetime == 0) ? 0 : System.currentTimeMillis();  | 
 | 
        if (entry.isValid(time) == false) { | 
 | 
            if (DEBUG) { | 
 | 
                System.out.println("Ignoring expired entry"); | 
 | 
            }  | 
 | 
            cacheMap.remove(key);  | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        return entry.getValue();  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized void remove(Object key) { | 
 | 
        emptyQueue();  | 
 | 
        CacheEntry<K,V> entry = cacheMap.remove(key);  | 
 | 
        if (entry != null) { | 
 | 
            entry.invalidate();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized void setCapacity(int size) { | 
 | 
        expungeExpiredEntries();  | 
 | 
        if (size > 0 && cacheMap.size() > size) { | 
 | 
            Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();  | 
 | 
            for (int i = cacheMap.size() - size; i > 0; i--) { | 
 | 
                CacheEntry<K,V> lruEntry = t.next();  | 
 | 
                if (DEBUG) { | 
 | 
                    System.out.println("** capacity reset removal " | 
 | 
                        + lruEntry.getKey() + " | " + lruEntry.getValue());  | 
 | 
                }  | 
 | 
                t.remove();  | 
 | 
                lruEntry.invalidate();  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        maxSize = size > 0 ? size : 0;  | 
 | 
 | 
 | 
        if (DEBUG) { | 
 | 
            System.out.println("** capacity reset to " + size); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized void setTimeout(int timeout) { | 
 | 
        emptyQueue();  | 
 | 
        lifetime = timeout > 0 ? timeout * 1000L : 0L;  | 
 | 
 | 
 | 
        if (DEBUG) { | 
 | 
            System.out.println("** lifetime reset to " + timeout); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public synchronized void accept(CacheVisitor<K,V> visitor) { | 
 | 
        expungeExpiredEntries();  | 
 | 
        Map<K,V> cached = getCachedEntries();  | 
 | 
 | 
 | 
        visitor.visit(cached);  | 
 | 
    }  | 
 | 
 | 
 | 
    private Map<K,V> getCachedEntries() { | 
 | 
        Map<K,V> kvmap = new HashMap<>(cacheMap.size());  | 
 | 
 | 
 | 
        for (CacheEntry<K,V> entry : cacheMap.values()) { | 
 | 
            kvmap.put(entry.getKey(), entry.getValue());  | 
 | 
        }  | 
 | 
 | 
 | 
        return kvmap;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected CacheEntry<K,V> newEntry(K key, V value,  | 
 | 
            long expirationTime, ReferenceQueue<V> queue) { | 
 | 
        if (queue != null) { | 
 | 
            return new SoftCacheEntry<>(key, value, expirationTime, queue);  | 
 | 
        } else { | 
 | 
            return new HardCacheEntry<>(key, value, expirationTime);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static interface CacheEntry<K,V> { | 
 | 
 | 
 | 
        boolean isValid(long currentTime);  | 
 | 
 | 
 | 
        void invalidate();  | 
 | 
 | 
 | 
        K getKey();  | 
 | 
 | 
 | 
        V getValue();  | 
 | 
 | 
 | 
        long getExpirationTime();  | 
 | 
    }  | 
 | 
 | 
 | 
    private static class HardCacheEntry<K,V> implements CacheEntry<K,V> { | 
 | 
 | 
 | 
        private K key;  | 
 | 
        private V value;  | 
 | 
        private long expirationTime;  | 
 | 
 | 
 | 
        HardCacheEntry(K key, V value, long expirationTime) { | 
 | 
            this.key = key;  | 
 | 
            this.value = value;  | 
 | 
            this.expirationTime = expirationTime;  | 
 | 
        }  | 
 | 
 | 
 | 
        public K getKey() { | 
 | 
            return key;  | 
 | 
        }  | 
 | 
 | 
 | 
        public V getValue() { | 
 | 
            return value;  | 
 | 
        }  | 
 | 
 | 
 | 
        public long getExpirationTime() { | 
 | 
            return expirationTime;  | 
 | 
        }  | 
 | 
 | 
 | 
        public boolean isValid(long currentTime) { | 
 | 
            boolean valid = (currentTime <= expirationTime);  | 
 | 
            if (valid == false) { | 
 | 
                invalidate();  | 
 | 
            }  | 
 | 
            return valid;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void invalidate() { | 
 | 
            key = null;  | 
 | 
            value = null;  | 
 | 
            expirationTime = -1;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static class SoftCacheEntry<K,V>  | 
 | 
            extends SoftReference<V>  | 
 | 
            implements CacheEntry<K,V> { | 
 | 
 | 
 | 
        private K key;  | 
 | 
        private long expirationTime;  | 
 | 
 | 
 | 
        SoftCacheEntry(K key, V value, long expirationTime,  | 
 | 
                ReferenceQueue<V> queue) { | 
 | 
            super(value, queue);  | 
 | 
            this.key = key;  | 
 | 
            this.expirationTime = expirationTime;  | 
 | 
        }  | 
 | 
 | 
 | 
        public K getKey() { | 
 | 
            return key;  | 
 | 
        }  | 
 | 
 | 
 | 
        public V getValue() { | 
 | 
            return get();  | 
 | 
        }  | 
 | 
 | 
 | 
        public long getExpirationTime() { | 
 | 
            return expirationTime;  | 
 | 
        }  | 
 | 
 | 
 | 
        public boolean isValid(long currentTime) { | 
 | 
            boolean valid = (currentTime <= expirationTime) && (get() != null);  | 
 | 
            if (valid == false) { | 
 | 
                invalidate();  | 
 | 
            }  | 
 | 
            return valid;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void invalidate() { | 
 | 
            clear();  | 
 | 
            key = null;  | 
 | 
            expirationTime = -1;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
}  |