/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2013, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core.spi;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.helpers.CyclicBuffer;
import java.util.*;
/**
* Another tracker implementtion for testing purposes only.
*
* @author Ceki Gücü
*/
public class CyclicBufferTrackerT<E> implements ComponentTracker<CyclicBuffer<E>> {
int bufferSize = CyclicBufferTracker.DEFAULT_BUFFER_SIZE;
int maxComponents = CyclicBufferTracker.DEFAULT_NUMBER_OF_BUFFERS;
List<TEntry<E>> liveList = new LinkedList<TEntry<E>>();
List<TEntry<E>> lingererList = new LinkedList<TEntry<E>>();
long lastCheck = 0;
private TEntry<E> getEntry(List<TEntry<E>> list,String k) {
for (int i = 0; i < list.size(); i++) {
TEntry<E> te = list.get(i);
if (te.key.equals(k)) {
return te;
}
}
return null;
}
private TEntry getFromEitherList(String key) {
TEntry entry = getEntry(liveList, key);
if(entry != null)
return entry;
else {
return getEntry(lingererList, key);
}
}
private List<String> keysAsOrderedList(List<TEntry<E>> list) {
Collections.sort(list);
List<String> result = new LinkedList<String>();
for (int i = 0; i < list.size(); i++) {
TEntry<E> te = list.get(i);
result.add(te.key);
}
return result;
}
List<String> liveKeysAsOrderedList() {
return keysAsOrderedList(liveList);
}
List<String> lingererKeysAsOrderedList() {
return keysAsOrderedList(lingererList);
}
public Set<String> allKeys() {
HashSet<String> allKeys = new HashSet<String>();
for (TEntry e : liveList)
allKeys.add(e.key);
for (TEntry e : lingererList)
allKeys.add(e.key);
return allKeys;
}
public Collection<CyclicBuffer<E>> allComponents() {
List<CyclicBuffer<E>> allComponents = new ArrayList<CyclicBuffer<E>>();
for (TEntry e : liveList)
allComponents.add(e.value);
for (TEntry e : lingererList)
allComponents.add(e.value);
return allComponents;
}
public CyclicBuffer<E> find(String key) {
TEntry<E> te = getFromEitherList(key);
if(te == null) return null;
else return te.value;
}
public CyclicBuffer<E> getOrCreate(String key, long timestamp) {
TEntry<E> te = getFromEitherList(key);
if (te == null) {
CyclicBuffer<E> cb = new CyclicBuffer<E>(bufferSize);
te = new TEntry<E>(key, cb, timestamp);
liveList.add(te);
if (liveList.size() > maxComponents) {
Collections.sort(liveList);
liveList.remove(0);
}
} else {
te.timestamp = timestamp;
Collections.sort(liveList);
}
return te.value;
}
public void endOfLife(String k) {
TEntry<E> te = null;
boolean found = false;
for (int i = 0; i < liveList.size(); i++) {
te = liveList.get(i);
if (te.key.equals(k)) {
liveList.remove(i);
found = true;
break;
}
}
if(found) {
lingererList.add(te);
}
}
private boolean isEntryStale(TEntry<E> entry, long now) {
return ((entry.timestamp + DEFAULT_TIMEOUT) < now);
}
private boolean isEntryDoneLingering(TEntry<E> tEntry, long now) {
return ((tEntry.timestamp + AbstractComponentTracker.LINGERING_TIMEOUT) < now);
}
public void removeStaleComponents(long now) {
if (isTooSoonForRemovalIteration(now)) return;
// both list should be sorted before removal attempts
Collections.sort(liveList);
Collections.sort(lingererList);
removeComponentsInExcessFromMainList();
removeStaleComponentsFromMainList(now);
removeStaleComponentsFromLingerersList(now);
}
private void removeComponentsInExcessFromMainList() {
while (liveList.size() > maxComponents) {
liveList.remove(0);
}
}
private void removeStaleComponentsFromMainList(long now) {
while (liveList.size() != 0 && isEntryStale(liveList.get(0), now)) {
liveList.remove(0);
}
}
private void removeStaleComponentsFromLingerersList(long now) {
while (lingererList.size() != 0 && isEntryDoneLingering(lingererList.get(0), now)) {
lingererList.remove(0);
}
}
private boolean isTooSoonForRemovalIteration(long now) {
if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > now) {
return true;
}
lastCheck = now;
return false;
}
public int getComponentCount() {
return liveList.size() + lingererList.size();
}
// ==================================================================
private class TEntry<X> implements Comparable<TEntry<?>> {
String key;
CyclicBuffer<E> value;
long timestamp;
TEntry(String k, CyclicBuffer<E> v, long timestamp) {
this.key = k;
this.value = v;
this.timestamp = timestamp;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
public int compareTo(TEntry<?> o) {
if (!(o instanceof TEntry)) {
throw new IllegalArgumentException("arguments must be of type " + TEntry.class);
}
TEntry<?> other = (TEntry<?>) o;
if (timestamp > other.timestamp) {
return 1;
}
if (timestamp == other.timestamp) {
return 0;
}
return -1;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
final TEntry<?> other = (TEntry<?>) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public String toString() {
return "(" + key + ", " + value + ")";
}
}
}