edu.cornell.mannlib.vitro.webapp.startup.StartupStatus.java Source code

Java tutorial

Introduction

Here is the source code for edu.cornell.mannlib.vitro.webapp.startup.StartupStatus.java

Source

/* $This file is distributed under the terms of the license in /doc/license.txt$ */

package edu.cornell.mannlib.vitro.webapp.startup;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Accumulates a list of messages from the StartupManager, and from the context
 * listeners that the run during startup.
 * 
 * This is thread-safe, with immutable items in the list and synchronized access
 * to the list.
 */
public class StartupStatus {
    private static final Log log = LogFactory.getLog(StartupStatus.class);

    protected static final String ATTRIBUTE_NAME = "STARTUP_STATUS";

    // ----------------------------------------------------------------------
    // static methods
    // ----------------------------------------------------------------------

    public static StartupStatus getBean(ServletContext ctx) {
        StartupStatus ss;

        Object o = ctx.getAttribute(ATTRIBUTE_NAME);
        if (o instanceof StartupStatus) {
            ss = (StartupStatus) o;
        } else {
            ss = new StartupStatus();
            ctx.setAttribute(ATTRIBUTE_NAME, ss);
        }

        return ss;
    }

    // ----------------------------------------------------------------------
    // methods to set status - note that these write to the log also.
    // ----------------------------------------------------------------------

    private SynchronizedStatusItemList itemList = new SynchronizedStatusItemList();

    public void info(ServletContextListener listener, String message) {
        addItem(StatusItem.Level.INFO, listener, message, null);
    }

    public void info(ServletContextListener listener, String message, Throwable cause) {
        addItem(StatusItem.Level.INFO, listener, message, cause);
    }

    public void warning(ServletContextListener listener, String message) {
        addItem(StatusItem.Level.WARNING, listener, message, null);
    }

    public void warning(ServletContextListener listener, String message, Throwable cause) {
        addItem(StatusItem.Level.WARNING, listener, message, cause);
    }

    public void fatal(ServletContextListener listener, String message) {
        addItem(StatusItem.Level.FATAL, listener, message, null);
    }

    public void fatal(ServletContextListener listener, String message, Throwable cause) {
        addItem(StatusItem.Level.FATAL, listener, message, cause);
    }

    /** Say that a previous fatal error prevented this listener from running. */
    public void listenerNotExecuted(ServletContextListener listener) {
        addItem(StatusItem.Level.NOT_EXECUTED, listener,
                "Not executed - startup was aborted by a previous fatal error.", null);
    }

    /** Create a simple item for this listener if no other exists. */
    public void listenerExecuted(ServletContextListener listener) {
        List<StatusItem> itemsForThisListener = getItemsForListener(listener);
        if (itemsForThisListener.isEmpty()) {
            addItem(StatusItem.Level.INFO, listener, "Ran successfully.", null);
        }
    }

    private void addItem(StatusItem.Level level, ServletContextListener source, String message, Throwable cause) {
        StatusItem item = new StatusItem(level, source, message, cause);
        itemList.add(item);

        String logMessage = "From " + item.getShortSourceName() + ": " + item.getMessage();
        if (item.getLevel() == StatusItem.Level.FATAL) {
            if (cause == null) {
                log.fatal(logMessage);
            } else {
                log.fatal(logMessage, cause);
            }
        } else if (item.getLevel() == StatusItem.Level.WARNING) {
            if (cause == null) {
                log.warn(logMessage);
            } else {
                log.warn(logMessage, cause);
            }
        } else {
            if (cause == null) {
                log.info(logMessage);
            } else {
                log.info(logMessage, cause);
            }
        }
    }

    // ----------------------------------------------------------------------
    // methods to query status
    // ----------------------------------------------------------------------

    public boolean allClear() {
        return getErrorItems().isEmpty() && getWarningItems().isEmpty();
    }

    public boolean isStartupAborted() {
        return !getErrorItems().isEmpty();
    }

    public List<StatusItem> getStatusItems() {
        return itemList.filterItems(StatusItemFilter.ALL_ITEMS_FILTER);
    }

    public List<StatusItem> getErrorItems() {
        return itemList.filterItems(StatusItemFilter.ERROR_ITEMS_FILTER);
    }

    public List<StatusItem> getWarningItems() {
        return itemList.filterItems(StatusItemFilter.WARNING_ITEMS_FILTER);
    }

    public List<StatusItem> getItemsForListener(ServletContextListener listener) {
        return itemList.filterItems(StatusItemFilter.listenerFilter(listener));
    }

    // ----------------------------------------------------------------------
    // helper classes
    // ----------------------------------------------------------------------

    /**
     * An immutable item that can't throw an exception during construction and
     * will always contain suitable, non-null values.
     */
    public static class StatusItem {
        public enum Level {
            INFO, WARNING, FATAL, NOT_EXECUTED
        }

        private final Level level;
        private final String sourceName;
        private final String shortSourceName;
        private final String message;
        private final String cause;

        private boolean unexpectedArguments;

        public StatusItem(Level level, ServletContextListener source, String message, Throwable cause) {
            this.level = figureLevel(level);
            this.sourceName = figureSourceName(source);
            this.shortSourceName = figureShortSourceName(source);
            this.message = message;
            this.cause = figureCauseString(cause);

            if (unexpectedArguments) {
                log.error("Unexpected arguments to " + StatusItem.class.getName() + ": level=" + level + ", source="
                        + source + ", message=" + message + ", cause=" + cause);
            }
        }

        /** Level should never be null: we have a problem. */
        private Level figureLevel(Level newLevel) {
            if (newLevel == null) {
                unexpectedArguments = true;
                return Level.FATAL;
            } else {
                return newLevel;
            }
        }

        private String figureSourceName(ServletContextListener source) {
            if (source == null) {
                unexpectedArguments = true;
                return "UNKNOWN SOURCE";
            } else {
                return source.getClass().getName();
            }
        }

        /**
         * Don't just use getSimpleName(): on an inner class we'd like to see
         * the parent also.
         */
        private String figureShortSourceName(ServletContextListener source) {
            if (source == null) {
                unexpectedArguments = true;
                return "UNKNOWN_SOURCE";
            } else {
                String sourceClassName = source.getClass().getName();
                int lastPeriodHere = sourceClassName.lastIndexOf('.');
                return sourceClassName.substring(lastPeriodHere + 1);
            }
        }

        /** Cause may be null - that's not unexpected. */
        private String figureCauseString(Throwable newCause) {
            if (newCause == null) {
                return "";
            } else {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                newCause.printStackTrace(pw);
                return sw.toString();
            }
        }

        public Level getLevel() {
            return level;
        }

        public String getSourceName() {
            return sourceName;
        }

        public String getShortSourceName() {
            return shortSourceName;
        }

        public String getMessage() {
            return message;
        }

        public String getCause() {
            return cause;
        }

    }

    /**
     * A filter class and some basic instances.
     */
    private static abstract class StatusItemFilter {
        public abstract boolean accept(StatusItem item);

        public static final StatusItemFilter ALL_ITEMS_FILTER = new StatusItemFilter() {
            @Override
            public boolean accept(StatusItem item) {
                return true;
            }
        };

        public static final StatusItemFilter ERROR_ITEMS_FILTER = new StatusItemFilter() {
            @Override
            public boolean accept(StatusItem item) {
                return item.level == StatusItem.Level.FATAL;
            }
        };

        public static final StatusItemFilter WARNING_ITEMS_FILTER = new StatusItemFilter() {
            @Override
            public boolean accept(StatusItem item) {
                return item.level == StatusItem.Level.WARNING;
            }
        };

        public static StatusItemFilter listenerFilter(ServletContextListener listener) {
            final String listenerName = listener.getClass().getName();

            return new StatusItemFilter() {
                @Override
                public boolean accept(StatusItem item) {
                    return item.getSourceName().equals(listenerName);
                }
            };

        }
    }

    /**
     * A list, with synchronized methods for adding to it, and for getting a
     * filtered subset of it.
     */
    private class SynchronizedStatusItemList {
        private final List<StatusItem> list = new ArrayList<StatusItem>();

        public void add(StatusItem item) {
            synchronized (list) {
                list.add(item);
            }
        }

        public List<StatusItem> filterItems(StatusItemFilter filter) {
            List<StatusItem> filteredList = new ArrayList<StatusItem>();
            synchronized (list) {
                for (StatusItem item : list) {
                    if (filter.accept(item)) {
                        filteredList.add(item);
                    }
                }
            }
            return filteredList;
        }
    }
}