View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
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   * @author Ceki Gücü
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    // 5 minutes
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; // least recently used entries are towards the head
42    private Entry tail; // most recently used entries are towards the 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 }