LRUCache.java

  1package de.measite.minidns;
  2
  3import java.util.LinkedHashMap;
  4import java.util.Map.Entry;
  5
  6/**
  7 * LRU based DNSCache backed by a LinkedHashMap.
  8 */
  9public class LRUCache implements DNSCache {
 10
 11    /**
 12     * Internal miss count.
 13     */
 14    protected long missCount = 0l;
 15
 16    /**
 17     * Internal expire count (subset of misses that was caused by expire).
 18     */
 19    protected long expireCount = 0l;
 20
 21    /**
 22     * Internal hit count.
 23     */
 24    protected long hitCount = 0l;
 25
 26    /**
 27     * The internal capacity of the backend cache.
 28     */
 29    protected int capacity;
 30
 31    /**
 32     * The upper bound of the ttl. All longer TTLs will be capped by this ttl.
 33     */
 34    protected long maxTTL;
 35
 36    /**
 37     * The backend cache.
 38     */
 39    protected LinkedHashMap<Question, DNSMessage> backend;
 40
 41    /**
 42     * Create a new LRUCache with given capacity and upper bound ttl.
 43     * @param capacity The internal capacity.
 44     * @param maxTTL The upper bound for any ttl.
 45     */
 46    @SuppressWarnings("serial")
 47    public LRUCache(final int capacity, final long maxTTL) {
 48        this.capacity = capacity;
 49        this.maxTTL = maxTTL;
 50        backend = new LinkedHashMap<Question,DNSMessage>(
 51                Math.min(capacity + (capacity + 3) / 4 + 2, 11), 0.75f, true)
 52            {
 53                @Override
 54                protected boolean removeEldestEntry(
 55                        Entry<Question, DNSMessage> eldest) {
 56                    return size() > capacity;
 57                }
 58            };
 59    }
 60
 61    /**
 62     * Create a new LRUCache with given capacity.
 63     * @param capacity The capacity of this cache.
 64     */
 65    public LRUCache(final int capacity) {
 66        this(capacity, Long.MAX_VALUE);
 67    }
 68
 69    @Override
 70    public synchronized void put(Question q, DNSMessage message) {
 71        if (message.getReceiveTimestamp() <= 0l) {
 72            return;
 73        }
 74        backend.put(q, message);
 75    }
 76
 77    @Override
 78    public synchronized DNSMessage get(Question q) {
 79        DNSMessage message = backend.get(q);
 80        if (message == null) {
 81            missCount++;
 82            return null;
 83        }
 84
 85        long ttl = maxTTL;
 86        for (Record r : message.getAnswers()) {
 87            ttl = Math.min(ttl, r.ttl);
 88        }
 89        for (Record r : message.getAdditionalResourceRecords()) {
 90            ttl = Math.min(ttl, r.ttl);
 91        }
 92        if (message.getReceiveTimestamp() + ttl > System.currentTimeMillis()) {
 93            missCount++;
 94            expireCount++;
 95            backend.remove(q);
 96            return null;
 97        } else {
 98            hitCount++;
 99            return message;
100        }
101    }
102
103    /**
104     * Clear all entries in this cache.
105     */
106    public synchronized void clear() {
107        backend.clear();
108        missCount = 0l;
109        hitCount = 0l;
110        expireCount = 0l;
111    }
112
113    /**
114     * Get the miss count of this cache which is the number of fruitless
115     * get calls since this cache was last resetted.
116     * @return The number of cache misses.
117     */
118    public long getMissCount() {
119        return missCount;
120    }
121
122    /**
123     * The number of expires (cache hits that have had a ttl to low to be
124     * retrieved).
125     * @return The expire count.
126     */
127    public long getExpireCount() {
128        return expireCount;
129    }
130
131    /**
132     * The cache hit count (all sucessful calls to get).
133     * @return The hit count.
134     */
135    public long getHitCount() {
136        return hitCount;
137    }
138
139}