/* * (c) Rob Gordon 2005 */ package org.oddjob.logging.cache; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.oddjob.logging.ArchiveNameResolver; import org.oddjob.logging.LogArchiver; import org.oddjob.logging.LogEvent; import org.oddjob.logging.LogLevel; import org.oddjob.logging.LogListener; /** * A LogArchiver which maintains it's log archives by polling. * <p> * This archiver will remove a component from it's list of components to poll * when no more listeners are listening to it. * <p> * This archiver will only poll the first component where many components share * the same archive. * * @author Rob Gordon */ public class PollingLogArchiver implements LogArchiver { private static final Logger logger = Logger.getLogger(PollingLogArchiver.class); /** A local LogArchiver we delegate to. */ private final LogArchiverCache cache; /** Components we want events for and there id */ private final Map<Object, String> components = new HashMap<Object, String>(); /** last message numbers */ private final Map<String, Long> lastMessageNumbers = new HashMap<String, Long>(); /** How many listeners are listening to a component. */ private SimpleCounter listenerCounter = new SimpleCounter(); private final LogEventSource source; private final ArchiveNameResolver resolver; /** * Constructor with default history. * */ public PollingLogArchiver( ArchiveNameResolver resolver, LogEventSource source) { this(LogArchiver.MAX_HISTORY, resolver, source); } /** * Constructor that accepts a history count. * */ public PollingLogArchiver(int history, ArchiveNameResolver resolver, LogEventSource source) { this.source = source; this.resolver = resolver; this.cache = new LazyArchiverCache(history, resolver); } /** * Add a LogListener for the given component. * * @param l The LogListener. * @param component The component. * @param level The level. * @param last The last message number required. * @param max The maximum history. */ public void addLogListener(LogListener l, Object component, LogLevel level, long last, int max) { String archive = resolver.resolveName(component); logger.debug("Adding LogListener for [" + archive + "]"); if (archive == null) { l.logEvent(LogArchiver.NO_LOG_AVAILABLE); return; } synchronized (components) { components.put(component, archive); listenerCounter.add(component); cache.addLogListener(l, component, level, last, max); poll(); } } /** * Remove the LogListener for the given component. * * @param l The LogListener. * @param component The component. */ public void removeLogListener(LogListener l, final Object component) { synchronized (components) { final String archiveName = (String) components.get(component); if (archiveName == null) { return; } cache.removeLogListener(l, component); // if the cache had the listener then decrease the count of // things for that component. The runnable runs within // the synchronized method call ensuring serialized removal // from the components. listenerCounter.remove(component, new Runnable() { public void run() { components.remove(component); } }); } } /** * Poll for Log Messages. * */ public void poll() { synchronized (components) { Set<Object> polled = new HashSet<Object>(); Set<Map.Entry<Object, String>> copy = new HashSet<Map.Entry<Object,String>>(components.entrySet()); for (Map.Entry<Object, String> entry : copy) { Object component = entry.getKey(); String archiveName = entry.getValue(); // stops polling the same archive when components share the same // logger/console if (polled.contains(archiveName)) { continue; } Long lastMessageNumber = lastMessageNumbers.get(archiveName); if (lastMessageNumber == null) { lastMessageNumber = -1L; } LogEvent[] events = null; try { // this could fail if the remote node has gone or the connection // has dropped. events = source.retrieveEvents(component, lastMessageNumber, cache.getMaxHistory()); } catch (Exception e) { logger.debug("Failed to retrieve events for [" + component + "]", e); components.remove(component); continue; } for (int i = 0; i < events.length; ++i) { cache.addEvent(archiveName, events[i].getLevel(), events[i].getMessage()); } if (events.length > 0) { lastMessageNumbers.put(archiveName, events[events.length - 1].getNumber()); } polled.add(archiveName); } } } public void onDestroy() { cache.destroy(); } }