Make StartupStatus thread-safe, so we can add items at any time.

This commit is contained in:
j2blake 2011-11-23 20:10:53 +00:00
parent 7439e02964
commit 350faa50fa

View file

@ -5,7 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.startup;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -17,6 +16,9 @@ import org.apache.commons.logging.LogFactory;
/** /**
* Accumulates a list of messages from the StartupManager, and from the context * Accumulates a list of messages from the StartupManager, and from the context
* listeners that the run during startup. * 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 { public class StartupStatus {
private static final Log log = LogFactory.getLog(StartupStatus.class); private static final Log log = LogFactory.getLog(StartupStatus.class);
@ -45,7 +47,7 @@ public class StartupStatus {
// methods to set status - note that these write to the log also. // methods to set status - note that these write to the log also.
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
private List<StatusItem> itemList = new ArrayList<StatusItem>(); private SynchronizedStatusItemList itemList = new SynchronizedStatusItemList();
public void info(ServletContextListener listener, String message) { public void info(ServletContextListener listener, String message) {
addItem(StatusItem.Level.INFO, listener, message, null); addItem(StatusItem.Level.INFO, listener, message, null);
@ -84,13 +86,11 @@ public class StartupStatus {
/** Create a simple item for this listener if no other exists. */ /** Create a simple item for this listener if no other exists. */
public void listenerExecuted(ServletContextListener listener) { public void listenerExecuted(ServletContextListener listener) {
for (StatusItem item : itemList) { List<StatusItem> itemsForThisListener = getItemsForListener(listener);
if (item.getSourceName().equals(listener.getClass().getName())) { if (itemsForThisListener.isEmpty()) {
return;
}
}
addItem(StatusItem.Level.INFO, listener, "Ran successfully.", null); addItem(StatusItem.Level.INFO, listener, "Ran successfully.", null);
} }
}
private void addItem(StatusItem.Level level, ServletContextListener source, private void addItem(StatusItem.Level level, ServletContextListener source,
String message, Throwable cause) { String message, Throwable cause) {
@ -133,33 +133,29 @@ public class StartupStatus {
} }
public List<StatusItem> getStatusItems() { public List<StatusItem> getStatusItems() {
return Collections.unmodifiableList(itemList); return itemList.filterItems(StatusItemFilter.ALL_ITEMS_FILTER);
} }
public List<StatusItem> getErrorItems() { public List<StatusItem> getErrorItems() {
List<StatusItem> list = new ArrayList<StartupStatus.StatusItem>(); return itemList.filterItems(StatusItemFilter.ERROR_ITEMS_FILTER);
for (StatusItem item : itemList) {
if (item.level == StatusItem.Level.FATAL) {
list.add(item);
}
}
return list;
} }
public List<StatusItem> getWarningItems() { public List<StatusItem> getWarningItems() {
List<StatusItem> list = new ArrayList<StartupStatus.StatusItem>(); return itemList.filterItems(StatusItemFilter.WARNING_ITEMS_FILTER);
for (StatusItem item : itemList) {
if (item.level == StatusItem.Level.WARNING) {
list.add(item);
} }
}
return list; public List<StatusItem> getItemsForListener(ServletContextListener listener) {
return itemList.filterItems(StatusItemFilter.listenerFilter(listener));
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// helper classes // 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 static class StatusItem {
public enum Level { public enum Level {
INFO, WARNING, FATAL, NOT_EXECUTED INFO, WARNING, FATAL, NOT_EXECUTED
@ -257,4 +253,70 @@ public class StartupStatus {
} }
/**
* 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;
}
}
} }