1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.spi;
15
16 import ch.qos.logback.core.CoreConstants;
17 import ch.qos.logback.core.helpers.CyclicBuffer;
18
19 import java.util.HashMap;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23
24
25
26
27 public class CyclicBufferTrackerImpl<E> implements CyclicBufferTracker<E> {
28
29 int bufferSize = DEFAULT_BUFFER_SIZE;
30 int maxNumBuffers = DEFAULT_NUMBER_OF_BUFFERS;
31 int bufferCount = 0;
32
33
34 static final int DELAY_BETWEEN_CLEARING_STALE_BUFFERS = 300 * CoreConstants.MILLIS_IN_ONE_SECOND;
35
36
37 boolean isStarted = false;
38
39 private Map<String, Entry> map = new HashMap<String, Entry>();
40
41 private Entry head;
42 private Entry tail;
43 long lastCheck = 0;
44
45
46 public CyclicBufferTrackerImpl() {
47 head = new Entry(null, null, 0);
48 tail = head;
49 }
50
51 public int getBufferSize() {
52 return bufferSize;
53 }
54
55 public void setBufferSize(int bufferSize) {
56 this.bufferSize = bufferSize;
57 }
58
59 public int getMaxNumberOfBuffers() {
60 return maxNumBuffers;
61 }
62
63 public void setMaxNumberOfBuffers(int maxNumBuffers) {
64 this.maxNumBuffers = maxNumBuffers;
65 }
66
67 public CyclicBuffer<E> getOrCreate(String key, long timestamp) {
68 Entry existing = map.get(key);
69 if (existing == null) {
70 CyclicBuffer<E> cb = processNewEntry(key, timestamp);
71 return cb;
72 } else {
73 existing.setTimestamp(timestamp);
74 moveToTail(existing);
75 return existing.value;
76 }
77 }
78
79 public void removeBuffer(String key) {
80 Entry existing = map.get(key);
81 if (existing != null) {
82 bufferCount--;
83 map.remove(key);
84 unlink(existing);
85 CyclicBuffer<E> cb = existing.value;
86 if(cb != null) {
87 cb.clear();
88 }
89 }
90 }
91
92 private CyclicBuffer<E> processNewEntry(String key, long timestamp) {
93 CyclicBuffer<E> cb = new CyclicBuffer<E>(bufferSize);
94 Entry entry = new Entry(key, cb, timestamp);
95 map.put(key, entry);
96 bufferCount++;
97 linkBeforeTail(entry);
98 if (bufferCount >= maxNumBuffers) {
99 removeHead();
100 }
101 return cb;
102 }
103
104 private void removeHead() {
105 CyclicBuffer cb = head.value;
106 if (cb != null) {
107 cb.clear();
108 }
109 map.remove(head.key);
110 bufferCount--;
111 head = head.next;
112 head.prev = null;
113 }
114
115 private void moveToTail(Entry e) {
116 unlink(e);
117 linkBeforeTail(e);
118 }
119
120 private void unlink(Entry e) {
121 if (e.prev != null) {
122 e.prev.next = e.next;
123 }
124 if (e.next != null) {
125 e.next.prev = e.prev;
126 }
127 if (head == e) {
128 head = e.next;
129 }
130 }
131
132
133 public synchronized void clearStaleBuffers(long now) {
134 if (lastCheck + DELAY_BETWEEN_CLEARING_STALE_BUFFERS > now) {
135 return;
136 }
137 lastCheck = now;
138
139 while (head.value != null && isEntryStale(head, now)) {
140 removeHead();
141 }
142 }
143
144 public int size() {
145 return map.size();
146 }
147
148 final private boolean isEntryStale(Entry entry, long now) {
149 return ((entry.timestamp + THRESHOLD) < now);
150 }
151
152 List<String> keyList() {
153 List<String> result = new LinkedList<String>();
154 Entry e = head;
155 while (e != tail) {
156 result.add(e.key);
157 e = e.next;
158 }
159 return result;
160 }
161
162 private void linkBeforeTail(Entry e) {
163 if (head == tail) {
164 head = e;
165 }
166 Entry preTail = tail.prev;
167 if (preTail != null) {
168 preTail.next = e;
169 }
170 e.prev = preTail;
171 e.next = tail;
172 tail.prev = e;
173 }
174
175
176
177 private class Entry {
178 Entry next;
179 Entry prev;
180
181 String key;
182 CyclicBuffer<E> value;
183 long timestamp;
184
185 Entry(String k, CyclicBuffer<E> v, long timestamp) {
186 this.key = k;
187 this.value = v;
188 this.timestamp = timestamp;
189 }
190
191 public void setTimestamp(long timestamp) {
192 this.timestamp = timestamp;
193 }
194
195 @Override
196 public int hashCode() {
197 final int prime = 31;
198 int result = 1;
199 result = prime * result + ((key == null) ? 0 : key.hashCode());
200 return result;
201 }
202
203 @Override
204 public boolean equals(Object obj) {
205 if (this == obj)
206 return true;
207 if (obj == null)
208 return false;
209 if (getClass() != obj.getClass())
210 return false;
211 final Entry other = (Entry) obj;
212 if (key == null) {
213 if (other.key != null)
214 return false;
215 } else if (!key.equals(other.key))
216 return false;
217 if (value == null) {
218 if (other.value != null)
219 return false;
220 } else if (!value.equals(other.value))
221 return false;
222 return true;
223 }
224
225 @Override
226 public String toString() {
227 return "(" + key + ", " + value + ")";
228 }
229 }
230 }