View Javadoc

1   /**
2    * Logback: the generic, reliable, fast and flexible logging framework.
3    * 
4    * Copyright (C) 2000-2008, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.core.sift;
11  
12  import java.util.HashMap;
13  import java.util.LinkedList;
14  import java.util.List;
15  import java.util.Map;
16  
17  import ch.qos.logback.core.Appender;
18  
19  /**
20   * Track appenders by a key. When an appender is not used for
21   * longer than THRESHOLD, stop it.
22   * @author Ceki Gulcu
23   */
24  public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
25  
26    Map<String, Entry> map = new HashMap<String, Entry>();
27   
28    Entry head; // least recently used entries are towards the head
29    Entry tail; // most recently used entries are towards the tail
30  
31    long lastCheck = 0;
32  
33    AppenderTrackerImpl() {
34      head = new Entry(null, null, 0);
35      tail = head;
36    }
37  
38  
39    public synchronized void put(String key, Appender<E> value, long timestamp) {
40      Entry entry = map.get(key);
41      if (entry == null) {
42        entry = new Entry(key, value, timestamp);
43        map.put(key, entry);
44      }
45      moveToTail(entry);
46    }
47  
48    public synchronized Appender<E> get(String key, long timestamp) {
49      Entry existing = map.get(key);
50      if (existing == null) {
51        return null;
52      } else {
53        existing.setTimestamp(timestamp);
54        moveToTail(existing);
55        return existing.value;
56      }
57    }
58  
59    
60    public synchronized void stopStaleAppenders(long now) {
61      if (lastCheck + MILLIS_IN_ONE_SECOND > now) {
62        return;
63      }
64      lastCheck = now;
65      while (head.value != null && isEntryStale(head,now)) {
66        Appender appender = head.value;
67        //System.out.println("  stopping "+appender);
68        appender.stop();
69        removeHead();
70      }
71    } 
72  
73    public List<String> keyList() {
74      List<String> result = new LinkedList<String>();
75      Entry e = head;
76      while (e != tail) {
77        result.add(e.key);
78        e = e.next;
79      }
80      return result;
81    }
82    
83    
84    final private boolean isEntryStale(Entry entry, long now) {
85      return ((entry.timestamp + THRESHOLD) < now);
86    }
87  
88    
89    private void removeHead() {
90      // System.out.println("RemoveHead called");
91      map.remove(head.key);
92      head = head.next;
93      head.prev = null;
94    }
95  
96    private void moveToTail(Entry e) {
97      rearrangePreexistingLinks(e);
98      rearrangeTailLinks(e);
99    }
100 
101   private void rearrangePreexistingLinks(Entry e) {
102     if (e.prev != null) {
103       e.prev.next = e.next;
104     }
105     if (e.next != null) {
106       e.next.prev = e.prev;
107     }
108     if (head == e) {
109       head = e.next;
110     }
111   }
112 
113   private void rearrangeTailLinks(Entry e) {
114     if (head == tail) {
115       head = e;
116     }
117     Entry preTail = tail.prev;
118     if (preTail != null) {
119       preTail.next = e;
120     }
121     e.prev = preTail;
122     e.next = tail;
123     tail.prev = e;
124   }
125 
126   public void dump() {
127     Entry e = head;
128     System.out.print("N:");
129     while (e != null) {
130       // System.out.print(e+"->");
131       System.out.print(e.key + ", ");
132       e = e.next;
133     }
134     System.out.println();
135   }
136 
137 
138 
139   public List<Appender<E>> valueList() {
140     List<Appender<E>> result = new LinkedList<Appender<E>>();
141     Entry e = head;
142     while (e != tail) {
143       result.add(e.value);
144       e = e.next;
145     }
146     return result;
147   }
148   
149   // ================================================================
150   private class Entry {
151     Entry next;
152     Entry prev;
153 
154     String key;
155     Appender<E> value;
156     long timestamp;
157 
158     Entry(String k, Appender<E> v, long timestamp) {
159       this.key = k;
160       this.value = v;
161       this.timestamp = timestamp;
162     }
163 
164     public long getTimestamp() {
165       return timestamp;
166     }
167 
168     public void setTimestamp(long timestamp) {
169       this.timestamp = timestamp;
170     }
171 
172     @Override
173     public int hashCode() {
174       final int prime = 31;
175       int result = 1;
176       result = prime * result + ((key == null) ? 0 : key.hashCode());
177       return result;
178     }
179 
180     @Override
181     public boolean equals(Object obj) {
182       if (this == obj)
183         return true;
184       if (obj == null)
185         return false;
186       if (getClass() != obj.getClass())
187         return false;
188       final Entry other = (Entry) obj;
189       if (key == null) {
190         if (other.key != null)
191           return false;
192       } else if (!key.equals(other.key))
193         return false;
194       if (value == null) {
195         if (other.value != null)
196           return false;
197       } else if (!value.equals(other.value))
198         return false;
199       return true;
200     }
201 
202     @Override
203     public String toString() {
204       return "(" + key + ", " + value + ")";
205     }
206   }
207 }