/*
* (c) Rob Gordon 2005
*/
package org.oddjob.logging.cache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.oddjob.logging.LogArchive;
import org.oddjob.logging.LogEvent;
import org.oddjob.logging.LogEventSink;
import org.oddjob.logging.LogLevel;
import org.oddjob.logging.LogListener;
/**
* A log archive. This archives events and supports listeners.
*
* @author Rob Gordon
*/
public class LogArchiveImpl implements LogArchive, LogEventSink {
/** Maximum archived lines */
private final int maxHistory;
private final String archive;
/** Archives are stored as a linked list with new message at the beginning
* (first) and old messages at the end (last).
*/
private final LinkedList<LogEvent> events = new LinkedList<LogEvent>();
/** Map of listeners to level. */
private final Map<LogListener, LogLevel> listeners =
new HashMap<LogListener, LogLevel>();
/**
* Constructor.
*
* @param maxHistory The maximum history lines.
*/
public LogArchiveImpl(String archive, int maxHistory) {
if (archive == null) {
throw new NullPointerException("Logger Name must not be null.");
}
this.archive = archive;
this.maxHistory = maxHistory;
}
/**
* Get the last message number in this archive.
*
* @return The last message number.
*/
public long getLastMessageNumber() {
synchronized (events) {
if (events.size() == 0) {
return -1;
}
LogEvent logEvent = (LogEvent) events.getFirst();
return logEvent.getNumber();
}
}
/**
* Add an event to this archive.
*
* @param level The level.
* @param line The message.
*/
public void addEvent(LogLevel level, String line) {
synchronized (events) {
LogEvent event = new LogEvent(archive, getLastMessageNumber() + 1, level,
line);
events.addFirst(event);
while (events.size() > maxHistory) {
events.removeLast();
}
// send event to listeners
for (Map.Entry<LogListener, LogLevel> entry : listeners.entrySet()) {
LogListener listener = (LogListener) entry.getKey();
LogLevel listenerLevel = (LogLevel) entry.getValue();
if (level.isLessThan(listenerLevel)) {
continue;
}
listener.logEvent(event);
}
}
}
/**
* Retrieve events from the archive. The most recent events are retrieved
* first.
*
* @param from From message number
* @param max The maximum number to retreive.
*
* @return The events.
*/
public LogEvent[] retrieveEvents(long from, int max) {
synchronized (events) {
List<LogEvent> missed = new ArrayList<LogEvent>();
int count = 0;
// work out what has been missed.
for (Iterator<LogEvent> it = events.iterator(); it.hasNext() && count < max; count++) {
LogEvent event = it.next();
if (event.getNumber() == from) {
break;
}
missed.add(event);
}
Collections.reverse(missed);
return (LogEvent[]) missed.toArray(new LogEvent[0]);
}
}
/**
* Add a listener.
*
* @param l The listener.
* @param level The level.
* @param last The last message number this listener requires.
* @param history The maximum lines this listener requires.
*/
public void addListener(LogListener l,
LogLevel level, long last, int history) {
synchronized (events) {
Stack<LogEvent> missed = new Stack<LogEvent>();
int count = 0;
// work out what messages listener has missed.
for (Iterator<LogEvent> it = events.iterator(); it.hasNext() && count < history; count++) {
LogEvent event = it.next();
if (event.getNumber() <= last) {
break;
}
if (event.getLevel().isLessThan(level)) {
continue;
}
missed.push(event);
}
// send missed messages
while (!missed.empty()) {
LogEvent event = (LogEvent) missed.pop();
l.logEvent(event);
}
listeners.put(l, level);
}
}
/**
* Remove a listener.
*
* @param l The listener.
*/
public boolean removeListener(LogListener l) {
synchronized (events) {
return !(listeners.remove(l) == null);
}
}
/**
* Get the archive name.
*
* @return The archive name.
*/
public String getArchive() {
return archive;
}
/**
* Get the naximum number archive history lines supported.
*
* @return The number of lines.
*/
public int getMaxHistory() {
return maxHistory;
}
}