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;
015
016import java.util.ArrayList;
017import java.util.List;
018
019import ch.qos.logback.core.helpers.CyclicBuffer;
020import ch.qos.logback.core.spi.LogbackLock;
021import ch.qos.logback.core.status.OnConsoleStatusListener;
022import ch.qos.logback.core.status.Status;
023import ch.qos.logback.core.status.StatusListener;
024import ch.qos.logback.core.status.StatusManager;
025
026public class BasicStatusManager implements StatusManager {
027
028    public static final int MAX_HEADER_COUNT = 150;
029    public static final int TAIL_SIZE = 150;
030
031    int count = 0;
032
033    // protected access was requested in http://jira.qos.ch/browse/LBCORE-36
034    final protected List<Status> statusList = new ArrayList<Status>();
035    final protected CyclicBuffer<Status> tailBuffer = new CyclicBuffer<Status>(TAIL_SIZE);
036    final protected LogbackLock statusListLock = new LogbackLock();
037
038    int level = Status.INFO;
039
040    // protected access was requested in http://jira.qos.ch/browse/LBCORE-36
041    final protected List<StatusListener> statusListenerList = new ArrayList<StatusListener>();
042    final protected LogbackLock statusListenerListLock = new LogbackLock();
043
044    // Note on synchronization
045    // This class contains two separate locks statusListLock and
046    // statusListenerListLock guarding respectively the statusList+tailBuffer and
047    // statusListenerList fields. The locks are used internally
048    // without cycles. They are exposed to derived classes which should be careful
049    // not to create deadlock cycles.
050
051    /**
052     * Add a new status object.
053     * 
054     * @param newStatus
055     *                the status message to add
056     */
057    public void add(Status newStatus) {
058        // LBCORE-72: fire event before the count check
059        fireStatusAddEvent(newStatus);
060
061        count++;
062        if (newStatus.getLevel() > level) {
063            level = newStatus.getLevel();
064        }
065
066        synchronized (statusListLock) {
067            if (statusList.size() < MAX_HEADER_COUNT) {
068                statusList.add(newStatus);
069            } else {
070                tailBuffer.add(newStatus);
071            }
072        }
073
074    }
075
076    public List<Status> getCopyOfStatusList() {
077        synchronized (statusListLock) {
078            List<Status> tList = new ArrayList<Status>(statusList);
079            tList.addAll(tailBuffer.asList());
080            return tList;
081        }
082    }
083
084    private void fireStatusAddEvent(Status status) {
085        synchronized (statusListenerListLock) {
086            for (StatusListener sl : statusListenerList) {
087                sl.addStatusEvent(status);
088            }
089        }
090    }
091
092    public void clear() {
093        synchronized (statusListLock) {
094            count = 0;
095            statusList.clear();
096            tailBuffer.clear();
097        }
098    }
099
100    public int getLevel() {
101        return level;
102    }
103
104    public int getCount() {
105        return count;
106    }
107
108    /**
109     * This implementation does not allow duplicate installations of OnConsoleStatusListener
110     * @param listener
111     */
112    public boolean add(StatusListener listener) {
113        synchronized (statusListenerListLock) {
114            if (listener instanceof OnConsoleStatusListener) {
115                boolean alreadyPresent = checkForPresence(statusListenerList, listener.getClass());
116                if (alreadyPresent)
117                    return false;
118            }
119            statusListenerList.add(listener);
120        }
121        return true;
122    }
123
124    private boolean checkForPresence(List<StatusListener> statusListenerList, Class<?> aClass) {
125        for (StatusListener e : statusListenerList) {
126            if (e.getClass() == aClass)
127                return true;
128        }
129        return false;
130    }
131
132    public void remove(StatusListener listener) {
133        synchronized (statusListenerListLock) {
134            statusListenerList.remove(listener);
135        }
136    }
137
138    public List<StatusListener> getCopyOfStatusListenerList() {
139        synchronized (statusListenerListLock) {
140            return new ArrayList<StatusListener>(statusListenerList);
141        }
142    }
143
144}