001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.spi;
015
016import ch.qos.logback.core.CoreConstants;
017import ch.qos.logback.core.helpers.CyclicBuffer;
018
019import java.util.*;
020
021/**
022 * Another tracker implementtion for testing purposes only.
023 *
024 * @author Ceki Gülcü
025 */
026public class CyclicBufferTrackerT<E> implements ComponentTracker<CyclicBuffer<E>> {
027
028    int bufferSize = CyclicBufferTracker.DEFAULT_BUFFER_SIZE;
029    int maxComponents = CyclicBufferTracker.DEFAULT_NUMBER_OF_BUFFERS;
030
031    List<TEntry<E>> liveList = new LinkedList<TEntry<E>>();
032    List<TEntry<E>> lingererList = new LinkedList<TEntry<E>>();
033
034    long lastCheck = 0;
035
036    private TEntry<E> getEntry(List<TEntry<E>> list, String k) {
037        for (int i = 0; i < list.size(); i++) {
038            TEntry<E> te = list.get(i);
039            if (te.key.equals(k)) {
040                return te;
041            }
042        }
043        return null;
044    }
045
046    private TEntry<E> getFromEitherList(String key) {
047        TEntry<E> entry = getEntry(liveList, key);
048        if (entry != null)
049            return entry;
050        else {
051            return getEntry(lingererList, key);
052        }
053    }
054
055    private List<String> keysAsOrderedList(List<TEntry<E>> list) {
056        Collections.sort(list);
057        List<String> result = new LinkedList<String>();
058        for (int i = 0; i < list.size(); i++) {
059            TEntry<E> te = list.get(i);
060            result.add(te.key);
061        }
062        return result;
063    }
064
065    List<String> liveKeysAsOrderedList() {
066        return keysAsOrderedList(liveList);
067    }
068
069    List<String> lingererKeysAsOrderedList() {
070        return keysAsOrderedList(lingererList);
071    }
072
073    public Set<String> allKeys() {
074        HashSet<String> allKeys = new HashSet<String>();
075        for (TEntry<E> e : liveList)
076            allKeys.add(e.key);
077        for (TEntry<E> e : lingererList)
078            allKeys.add(e.key);
079        return allKeys;
080    }
081
082    public Collection<CyclicBuffer<E>> allComponents() {
083        List<CyclicBuffer<E>> allComponents = new ArrayList<CyclicBuffer<E>>();
084        for (TEntry<E> e : liveList)
085            allComponents.add(e.value);
086        for (TEntry<E> e : lingererList)
087            allComponents.add(e.value);
088
089        return allComponents;
090    }
091
092    public CyclicBuffer<E> find(String key) {
093        TEntry<E> te = getFromEitherList(key);
094        if (te == null)
095            return null;
096        else
097            return te.value;
098    }
099
100    public CyclicBuffer<E> getOrCreate(String key, long timestamp) {
101        TEntry<E> te = getFromEitherList(key);
102        if (te == null) {
103            CyclicBuffer<E> cb = new CyclicBuffer<E>(bufferSize);
104            te = new TEntry<E>(key, cb, timestamp);
105            liveList.add(te);
106            if (liveList.size() > maxComponents) {
107                Collections.sort(liveList);
108                liveList.remove(0);
109            }
110        } else {
111            te.timestamp = timestamp;
112            Collections.sort(liveList);
113        }
114        return te.value;
115    }
116
117    public void endOfLife(String k) {
118        TEntry<E> te = null;
119        boolean found = false;
120        for (int i = 0; i < liveList.size(); i++) {
121            te = liveList.get(i);
122            if (te.key.equals(k)) {
123                liveList.remove(i);
124                found = true;
125                break;
126            }
127        }
128        if (found) {
129            lingererList.add(te);
130        }
131    }
132
133    private boolean isEntryStale(TEntry<E> entry, long now) {
134        return ((entry.timestamp + DEFAULT_TIMEOUT) < now);
135    }
136
137    private boolean isEntryDoneLingering(TEntry<E> tEntry, long now) {
138        return ((tEntry.timestamp + AbstractComponentTracker.LINGERING_TIMEOUT) < now);
139    }
140
141    public void removeStaleComponents(long now) {
142        if (isTooSoonForRemovalIteration(now))
143            return;
144        // both list should be sorted before removal attempts
145        Collections.sort(liveList);
146        Collections.sort(lingererList);
147        removeComponentsInExcessFromMainList();
148        removeStaleComponentsFromMainList(now);
149        removeStaleComponentsFromLingerersList(now);
150    }
151
152    private void removeComponentsInExcessFromMainList() {
153        while (liveList.size() > maxComponents) {
154            liveList.remove(0);
155        }
156    }
157
158    private void removeStaleComponentsFromMainList(long now) {
159        while (liveList.size() != 0 && isEntryStale(liveList.get(0), now)) {
160            liveList.remove(0);
161        }
162    }
163
164    private void removeStaleComponentsFromLingerersList(long now) {
165        while (lingererList.size() != 0 && isEntryDoneLingering(lingererList.get(0), now)) {
166            lingererList.remove(0);
167        }
168    }
169
170    private boolean isTooSoonForRemovalIteration(long now) {
171        if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > now) {
172            return true;
173        }
174        lastCheck = now;
175        return false;
176    }
177
178    public int getComponentCount() {
179        return liveList.size() + lingererList.size();
180    }
181
182    // ==================================================================
183
184    private class TEntry<X> implements Comparable<TEntry<?>> {
185
186        String key;
187        CyclicBuffer<E> value;
188        long timestamp;
189
190        TEntry(String k, CyclicBuffer<E> v, long timestamp) {
191            this.key = k;
192            this.value = v;
193            this.timestamp = timestamp;
194        }
195
196        @Override
197        public int hashCode() {
198            final int prime = 31;
199            int result = 1;
200            result = prime * result + ((key == null) ? 0 : key.hashCode());
201            return result;
202        }
203
204        public int compareTo(TEntry<?> o) {
205            if (!(o instanceof TEntry)) {
206                throw new IllegalArgumentException("arguments must be of type " + TEntry.class);
207            }
208
209            TEntry<?> other = (TEntry<?>) o;
210            if (timestamp > other.timestamp) {
211                return 1;
212            }
213            if (timestamp == other.timestamp) {
214                return 0;
215            }
216            return -1;
217        }
218
219        @Override
220        public boolean equals(Object obj) {
221            if (this == obj)
222                return true;
223            if (obj == null)
224                return false;
225            if (getClass() != obj.getClass())
226                return false;
227            @SuppressWarnings("unchecked")
228            final TEntry<?> other = (TEntry<?>) obj;
229            if (key == null) {
230                if (other.key != null)
231                    return false;
232            } else if (!key.equals(other.key))
233                return false;
234            if (value == null) {
235                if (other.value != null)
236                    return false;
237            } else if (!value.equals(other.value))
238                return false;
239            return true;
240        }
241
242        @Override
243        public String toString() {
244            return "(" + key + ", " + value + ")";
245        }
246    }
247}