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
18 import java.util.*;
19
20
21
22
23
24
25
26
27
28
29
30
31
32 abstract public class AbstractComponentTracker<C> implements ComponentTracker<C> {
33 private static final boolean ACCESS_ORDERED = true;
34
35
36 final public static long LINGERING_TIMEOUT = 10 * CoreConstants.MILLIS_IN_ONE_SECOND;
37
38
39
40
41
42 final public static long WAIT_BETWEEN_SUCCESSIVE_REMOVAL_ITERATIONS = CoreConstants.MILLIS_IN_ONE_SECOND;
43
44 protected int maxComponents = DEFAULT_MAX_COMPONENTS;
45 protected long timeout = DEFAULT_TIMEOUT;
46
47
48
49 LinkedHashMap<String, Entry<C>> liveMap = new LinkedHashMap<String, Entry<C>>(32, .75f, ACCESS_ORDERED);
50
51
52
53 LinkedHashMap<String, Entry<C>> lingerersMap = new LinkedHashMap<String, Entry<C>>(16, .75f, ACCESS_ORDERED);
54 long lastCheck = 0;
55
56
57
58
59
60
61 abstract protected void processPriorToRemoval(C component);
62
63
64
65
66
67
68
69 abstract protected C buildComponent(String key);
70
71
72
73
74
75
76
77
78 protected abstract boolean isComponentStale(C c);
79
80 public int getComponentCount() {
81 return liveMap.size() + lingerersMap.size();
82 }
83
84
85
86
87
88
89
90 private Entry<C> getFromEitherMap(String key) {
91 Entry<C> entry = liveMap.get(key);
92 if (entry != null)
93 return entry;
94 else {
95 return lingerersMap.get(key);
96 }
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110 public synchronized C find(String key) {
111 Entry<C> entry = getFromEitherMap(key);
112 if (entry == null)
113 return null;
114 else
115 return entry.component;
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129 public synchronized C getOrCreate(String key, long timestamp) {
130 Entry<C> entry = getFromEitherMap(key);
131 if (entry == null) {
132 C c = buildComponent(key);
133 entry = new Entry<C>(key, c, timestamp);
134
135 liveMap.put(key, entry);
136 } else {
137 entry.setTimestamp(timestamp);
138 }
139 return entry.component;
140 }
141
142
143
144
145
146
147 public void endOfLife(String key) {
148 Entry<C> entry = liveMap.remove(key);
149 if (entry == null)
150 return;
151 lingerersMap.put(key, entry);
152 }
153
154
155
156
157
158
159
160 public synchronized void removeStaleComponents(long now) {
161 if (isTooSoonForRemovalIteration(now))
162 return;
163 removeExcedentComponents();
164 removeStaleComponentsFromMainMap(now);
165 removeStaleComponentsFromLingerersMap(now);
166 }
167
168 private void removeExcedentComponents() {
169 genericStaleComponentRemover(liveMap, 0, byExcedent);
170 }
171
172 private void removeStaleComponentsFromMainMap(long now) {
173 genericStaleComponentRemover(liveMap, now, byTimeout);
174 }
175
176 private void removeStaleComponentsFromLingerersMap(long now) {
177 genericStaleComponentRemover(lingerersMap, now, byLingering);
178 }
179
180 private void genericStaleComponentRemover(LinkedHashMap<String, Entry<C>> map, long now,
181 RemovalPredicator<C> removalPredicator) {
182 Iterator<Map.Entry<String, Entry<C>>> iter = map.entrySet().iterator();
183 while (iter.hasNext()) {
184 Map.Entry<String, Entry<C>> mapEntry = iter.next();
185 Entry<C> entry = mapEntry.getValue();
186 if (removalPredicator.isSlatedForRemoval(entry, now)) {
187 iter.remove();
188 C c = entry.component;
189 processPriorToRemoval(c);
190 } else {
191 break;
192 }
193 }
194 }
195
196 private RemovalPredicator<C> byExcedent = new RemovalPredicator<C>() {
197 public boolean isSlatedForRemoval(Entry<C> entry, long timestamp) {
198 return (liveMap.size() > maxComponents);
199 }
200 };
201
202 private RemovalPredicator<C> byTimeout = new RemovalPredicator<C>() {
203 public boolean isSlatedForRemoval(Entry<C> entry, long timestamp) {
204 return isEntryStale(entry, timestamp);
205 }
206 };
207 private RemovalPredicator<C> byLingering = new RemovalPredicator<C>() {
208 public boolean isSlatedForRemoval(Entry<C> entry, long timestamp) {
209 return isEntryDoneLingering(entry, timestamp);
210 }
211 };
212
213 private boolean isTooSoonForRemovalIteration(long now) {
214 if (lastCheck + WAIT_BETWEEN_SUCCESSIVE_REMOVAL_ITERATIONS > now) {
215 return true;
216 }
217 lastCheck = now;
218 return false;
219 }
220
221 private boolean isEntryStale(Entry<C> entry, long now) {
222
223
224 C c = entry.component;
225 if (isComponentStale(c))
226 return true;
227
228 return ((entry.timestamp + timeout) < now);
229 }
230
231 private boolean isEntryDoneLingering(Entry<C> entry, long now) {
232 return ((entry.timestamp + LINGERING_TIMEOUT) < now);
233 }
234
235 public Set<String> allKeys() {
236 HashSet<String> allKeys = new HashSet<String>(liveMap.keySet());
237 allKeys.addAll(lingerersMap.keySet());
238 return allKeys;
239 }
240
241 public Collection<C> allComponents() {
242 List<C> allComponents = new ArrayList<C>();
243 for (Entry<C> e : liveMap.values())
244 allComponents.add(e.component);
245 for (Entry<C> e : lingerersMap.values())
246 allComponents.add(e.component);
247
248 return allComponents;
249 }
250
251 public long getTimeout() {
252 return timeout;
253 }
254
255 public void setTimeout(long timeout) {
256 this.timeout = timeout;
257 }
258
259 public int getMaxComponents() {
260 return maxComponents;
261 }
262
263 public void setMaxComponents(int maxComponents) {
264 this.maxComponents = maxComponents;
265 }
266
267
268 private interface RemovalPredicator<C> {
269 boolean isSlatedForRemoval(Entry<C> entry, long timestamp);
270 }
271
272
273 private static class Entry<C> {
274 String key;
275 C component;
276 long timestamp;
277
278 Entry(String k, C c, long timestamp) {
279 this.key = k;
280 this.component = c;
281 this.timestamp = timestamp;
282 }
283
284 public void setTimestamp(long timestamp) {
285 this.timestamp = timestamp;
286 }
287
288 @Override
289 public int hashCode() {
290 return key.hashCode();
291 }
292
293 @Override
294 public boolean equals(Object obj) {
295 if (this == obj)
296 return true;
297 if (obj == null)
298 return false;
299 if (getClass() != obj.getClass())
300 return false;
301 @SuppressWarnings("unchecked")
302 final Entry<C> other = (Entry<C>) obj;
303 if (key == null) {
304 if (other.key != null)
305 return false;
306 } else if (!key.equals(other.key))
307 return false;
308 if (component == null) {
309 if (other.component != null)
310 return false;
311 } else if (!component.equals(other.component))
312 return false;
313 return true;
314 }
315
316 @Override
317 public String toString() {
318 return "(" + key + ", " + component + ")";
319 }
320 }
321 }