NIHVIVO-336 Create a StartupManager to run all of the context listeners, and accumulate the results in a StartupStatus bean.
This commit is contained in:
parent
5a64431b71
commit
65bba272df
6 changed files with 721 additions and 146 deletions
|
@ -0,0 +1,230 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.startup;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
public class StartupManager implements ServletContextListener {
|
||||
private static final Log log = LogFactory.getLog(StartupManager.class);
|
||||
|
||||
public static final String FILE_OF_STARTUP_LISTENERS = "/WEB-INF/resources/startup_listeners.txt";
|
||||
|
||||
private final List<ServletContextListener> initializeList = new ArrayList<ServletContextListener>();
|
||||
|
||||
/**
|
||||
* These can be instance variables without risk, since contextInitialized()
|
||||
* will only be called once per instance.
|
||||
*/
|
||||
private ServletContext ctx;
|
||||
private StartupStatus ss;
|
||||
|
||||
/**
|
||||
* Build a list of the listeners, and run contextInitialized() on each of
|
||||
* them, at least until we get a fatal error.
|
||||
*
|
||||
* Each step of this should handle its own exceptions, but we'll wrap the
|
||||
* whole thing in a try/catch just in case.
|
||||
*/
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
ctx = sce.getServletContext();
|
||||
ss = StartupStatus.getBean(ctx);
|
||||
|
||||
try {
|
||||
findAndInstantiateListeners();
|
||||
|
||||
for (ServletContextListener listener : initializeList) {
|
||||
if (ss.isStartupAborted()) {
|
||||
ss.listenerNotExecuted(listener);
|
||||
} else {
|
||||
initialize(listener, sce);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
recordFatal("Startup threw an unexpected exception.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the file and instantiate build a list of listener instances.
|
||||
*
|
||||
* If there is a problem, it will occur and be handled in a sub-method.
|
||||
*/
|
||||
private void findAndInstantiateListeners() {
|
||||
List<String> classNames = readFileOfListeners();
|
||||
|
||||
for (String className : classNames) {
|
||||
ServletContextListener listener = instantiateListener(className);
|
||||
if (listener != null) {
|
||||
initializeList.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
checkForDuplicateListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the names of the listener classes.
|
||||
*
|
||||
* If there is a problem, set a fatal error, and return an empty list.
|
||||
*/
|
||||
private List<String> readFileOfListeners() {
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
InputStream is = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
is = ctx.getResourceAsStream(FILE_OF_STARTUP_LISTENERS);
|
||||
br = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
String line;
|
||||
while (null != (line = br.readLine())) {
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
||||
list.add(trimmed);
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
recordFatal("Unable to locate the list of startup listeners: "
|
||||
+ FILE_OF_STARTUP_LISTENERS);
|
||||
} catch (IOException e) {
|
||||
recordFatal(
|
||||
"Failed while processing the list of startup listeners: "
|
||||
+ FILE_OF_STARTUP_LISTENERS, e);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Classnames of listeners = " + list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a context listener from this class name.
|
||||
*
|
||||
* If there is a problem, set a fatal error, and return null.
|
||||
*/
|
||||
private ServletContextListener instantiateListener(String className) {
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
Object o = c.newInstance();
|
||||
return (ServletContextListener) o;
|
||||
} catch (ClassNotFoundException e) {
|
||||
recordFatal("Failed to instantiate listener: '" + className + "'",
|
||||
e);
|
||||
return null;
|
||||
} catch (InstantiationException e) {
|
||||
recordFatal("Failed to instantiate listener: '" + className + "'",
|
||||
e);
|
||||
return null;
|
||||
} catch (IllegalAccessException e) {
|
||||
recordFatal("Failed to instantiate listener: '" + className + "'",
|
||||
e);
|
||||
return null;
|
||||
} catch (ClassCastException e) {
|
||||
recordFatal("Instance of '" + className
|
||||
+ "' is not a ServletContextListener", e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
recordFatal("Failed to instantiate listener: '" + className + "'",
|
||||
e);
|
||||
return null;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
recordFatal("Failed to instantiate listener: '" + className + "'",
|
||||
e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call contextInitialized() on the listener.
|
||||
*
|
||||
* If there is an unexpected exception, set a fatal error.
|
||||
*/
|
||||
private void initialize(ServletContextListener listener,
|
||||
ServletContextEvent sce) {
|
||||
try {
|
||||
log.debug("Initializing '" + listener.getClass().getName() + "'");
|
||||
listener.contextInitialized(sce);
|
||||
} catch (Exception e) {
|
||||
ss.fatal(listener, "Threw unexpected exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have more than one listener from the same class, set a fatal error.
|
||||
*/
|
||||
private void checkForDuplicateListeners() {
|
||||
for (int i = 0; i < initializeList.size(); i++) {
|
||||
for (int j = i + 1; j < initializeList.size(); j++) {
|
||||
ServletContextListener iListener = initializeList.get(i);
|
||||
ServletContextListener jListener = initializeList.get(j);
|
||||
if (iListener.getClass().equals(jListener.getClass())) {
|
||||
recordFatal("File contains duplicate listener classes: '"
|
||||
+ iListener.getClass().getName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void recordFatal(String message) {
|
||||
ss.fatal(this, message);
|
||||
log.error(message);
|
||||
}
|
||||
|
||||
private void recordFatal(String message, Throwable cause) {
|
||||
ss.fatal(this, message, cause);
|
||||
log.error(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the listeners that the context is being destroyed, in the reverse
|
||||
* order from how they were notified at initialization.
|
||||
*/
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
List<ServletContextListener> destroyList = new ArrayList<ServletContextListener>(
|
||||
initializeList);
|
||||
Collections.reverse(destroyList);
|
||||
|
||||
for (ServletContextListener listener : destroyList) {
|
||||
try {
|
||||
log.debug("Destroying '" + listener.getClass().getName() + "'");
|
||||
listener.contextDestroyed(sce);
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception from contextDestroyed() on '"
|
||||
+ listener.getClass().getName() + "'", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.startup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
public class StartupStatus {
|
||||
private static final String ATTRIBUTE_NAME = "STARTUP_STATUS";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private List<StatusItem> itemList = new ArrayList<StatusItem>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void listenerNotExecuted(ServletContextListener listener) {
|
||||
addItem(StatusItem.Level.NOT_EXECUTED, listener, "Not executed", null);
|
||||
}
|
||||
|
||||
public boolean isStartupAborted() {
|
||||
for (StatusItem item : itemList) {
|
||||
if (item.level == StatusItem.Level.FATAL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<StatusItem> getStatusItems() {
|
||||
return Collections.unmodifiableList(itemList);
|
||||
}
|
||||
|
||||
private void addItem(StatusItem.Level level, ServletContextListener source,
|
||||
String message, Throwable cause) {
|
||||
itemList.add(new StatusItem(level, source, message, cause));
|
||||
}
|
||||
|
||||
public static class StatusItem {
|
||||
public enum Level {
|
||||
INFO, WARNING, FATAL, NOT_EXECUTED
|
||||
}
|
||||
|
||||
final Level level;
|
||||
final String sourceName;
|
||||
final String message;
|
||||
final Throwable cause;
|
||||
|
||||
public StatusItem(Level level, ServletContextListener source,
|
||||
String message, Throwable cause) {
|
||||
this.level = level;
|
||||
this.sourceName = source.getClass().getName();
|
||||
this.message = message;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue