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}