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:
j2blake 2011-09-20 21:13:10 +00:00
parent 5a64431b71
commit 65bba272df
6 changed files with 721 additions and 146 deletions

View file

@ -155,6 +155,10 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
<fileset file="${webapp.dir}/config/dwr.xml" /> <fileset file="${webapp.dir}/config/dwr.xml" />
</copy> </copy>
<copy todir="${war-resources.dir}">
<fileset file="${webapp.dir}/config/startup_listeners.txt" />
</copy>
<!-- copy the ontologies and the filegraphs into the war directory. --> <!-- copy the ontologies and the filegraphs into the war directory. -->
<copy todir="${war-webinf.dir}"> <copy todir="${war-webinf.dir}">
<fileset dir="${webapp.dir}" includes="ontologies" /> <fileset dir="${webapp.dir}" includes="ontologies" />

View file

@ -0,0 +1,60 @@
#
# ServletContextListeners for Vitro, to be instantiated and run by the StartupManager.
#
edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesSetup
edu.cornell.mannlib.vitro.webapp.config.RevisionInfoSetup
edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory$Setup
# DefaultThemeSetup needs to run before the JenaDataSourceSetup to allow creation of default portal and tab
edu.cornell.mannlib.vitro.webapp.servlet.setup.DefaultThemeSetup
# Comment out this listener to run Vitro without a database
# If used, this listener must be run before JenaDataSourceSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaPersistentDataSourceSetup
# This listener is required in order to use a Jena triple store (currently the only option)
edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaDataSourceSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup
# Invokes a process to move any uploaded files into the new file storage system.
# Needs to run after FileStorageSetup and JenaDataSourceSetup.
# Should run before Pellet is set up.
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUploadedFiles
# Update to the new UserAccounts model (1.3). Needs to run after JenaDataSourceSetup.
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUserAccounts
# Attaching submodels permits extra RDF files to be made visible without storing the data in the DB.
edu.cornell.mannlib.vitro.webapp.servlet.setup.AttachSubmodels
# Pellet setup enables reasoning. Inferences are cached in a separate DB graph.
# Changing the class name sets the kinds of inferences that are materialized.
# See documentation for details.
# If used, must be run after JenaDataSourceSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.PelletReasonerSetup
edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSetsLoader
edu.cornell.mannlib.vitro.webapp.auth.policy.bean.PropertyRestrictionPolicyHelper$Setup
edu.cornell.mannlib.vitro.webapp.auth.policy.setup.CommonPolicyFamilySetup
edu.cornell.mannlib.vitro.webapp.auth.policy.RootUserPolicy$Setup
edu.cornell.mannlib.vitro.webapp.auth.policy.RestrictHomeMenuItemEditingPolicy$Setup
# The Solr index uses a "public" filter, so the PropertyRestrictionPolicyHelper must already be set up.
edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup
edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerSetup
# On shutdown, this will kill the background thread started by Apache Commons File Upload
org.apache.commons.fileupload.servlet.FileCleanerCleanup
edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache$Setup

View file

@ -41,155 +41,11 @@
<!-- Listeners ****************************************************** --> <!-- Listeners ****************************************************** -->
<!-- Reads deploy.properties. Needs to run before almost all other listeners. --> <!-- StartupManager instantiates and runs the listeners from startup_listeners.txt -->
<listener> <listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesSetup</listener-class> <listener-class>edu.cornell.mannlib.vitro.webapp.startup.StartupManager</listener-class>
</listener>
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.config.RevisionInfoSetup</listener-class>
</listener>
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory$Setup</listener-class>
</listener>
<!-- DefaultThemeSetup needs to run before the JenaDataSourceSetup
to allow creation of default portal and tab -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.DefaultThemeSetup</listener-class>
</listener>
<!-- Comment out this listener to run Vitro without a database -->
<!-- If used, this listener must be run before JenaDataSourceSetup -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaPersistentDataSourceSetup</listener-class>
</listener>
<!-- experimental JenaMulgaraBridge, replaces JenaPersistentDataSourceSetup. -->
<!-- <listener>
-->
<!-- <listener-class>
-->
<!-- edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaMulgaraBridgeSetup
-->
<!-- </listener-class>
-->
<!-- </listener> -->
<!-- This listener is required in order to use a Jena triple store (currently the only option) -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaDataSourceSetup</listener-class>
</listener>
<!-- Invokes process to perform updates to align with ontology changes if needed -->
<!-- Needs to run before submodels are attached and Pellet is set up -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase</listener-class>
</listener>
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup
</listener-class>
</listener>
<!-- Invokes a process to move any uploaded files into the new file storage system. -->
<!-- Needs to run after FileStorageSetup and JenaDataSourceSetup. -->
<!-- Should run before Pellet is set up. -->
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUploadedFiles
</listener-class>
</listener>
<!-- Update to the new UserAccounts model (1.3). Needs to run after JenaDataSourceSetup. -->
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUserAccounts
</listener-class>
</listener>
<!-- Attaching submodels permits extra RDF files to be made visible without storing the data in the DB. -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.AttachSubmodels</listener-class>
</listener>
<!-- Pellet setup enables reasoning. Inferences are cached in a separate DB graph. -->
<!-- Changing the class name sets the kinds of inferences that are materialized. -->
<!-- See documentation for details. -->
<!-- If used, must be run after JenaDataSourceSetup -->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.PelletReasonerSetup</listener-class>
</listener>
<!-- The followng listener records all edit changes, in reified form, to another database model -->
<!-- still at an experimental stage -->
<!-- if used, this listener should be run after all other Jena-related listeners, to avoid logging unnecessary data and slowing the context startup process -->
<!--
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.servlet.setup.ModelAuditorSetup
</listener-class>
</listener>
-->
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSetsLoader</listener-class>
</listener>
<listener>
<listener-class> edu.cornell.mannlib.vitro.webapp.auth.policy.bean.PropertyRestrictionPolicyHelper$Setup
</listener-class>
</listener>
<listener>
<listener-class> edu.cornell.mannlib.vitro.webapp.auth.policy.setup.CommonPolicyFamilySetup
</listener-class>
</listener>
<listener>
<listener-class> edu.cornell.mannlib.vitro.webapp.auth.policy.RootUserPolicy$Setup</listener-class>
</listener>
<listener>
<listener-class> edu.cornell.mannlib.vitro.webapp.auth.policy.RestrictHomeMenuItemEditingPolicy$Setup</listener-class>
</listener>
<!-- The Solr index uses a "public" filter, so the PropertyRestrictionPolicyHelper must already be set up. -->
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup
</listener-class>
</listener> </listener>
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerSetup
</listener-class>
</listener>
<!-- On shutdown, this will kill the background thread started by Apache Commons File Upload -->
<listener>
<listener-class>
org.apache.commons.fileupload.servlet.FileCleanerCleanup
</listener-class>
</listener>
<listener>
<listener-class>edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache$Setup</listener-class>
</listener>
<!--
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.auth.policy.setup.AlwaysAuthorizePolicySetup
</listener-class>
</listener>
-->
<!-- Filters ********************************************************** --> <!-- Filters ********************************************************** -->
<!-- in 2.4 spec, filter chain order is first by filter-mapping <url-pattern> order in web.xml, <!-- in 2.4 spec, filter chain order is first by filter-mapping <url-pattern> order in web.xml,

View file

@ -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);
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,322 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.startup;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import stubs.javax.servlet.ServletContextStub;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus.StatusItem;
/**
* TODO
*/
public class StartupManagerTest extends AbstractTestClass {
private static final Log log = LogFactory.getLog(StartupManagerTest.class);
private ServletContextStub ctx;
private ServletContextEvent sce;
private StartupManager sm;
private StartupStatus ss;
@Before
public void setup() {
ctx = new ServletContextStub();
sce = new ServletContextEvent(ctx);
sm = new StartupManager();
ss = StartupStatus.getBean(ctx);
// setLoggerLevel(this.getClass(), Level.DEBUG);
// setLoggerLevel(StartupManager.class, Level.DEBUG);
}
@After
public void dumpForDebug() {
if (log.isDebugEnabled()) {
dumpStatus();
}
}
@Test
public void noSuchFile() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails((String) null);
}
@Test
public void emptyFile() {
assertStartupSucceeds();
}
@Test
public void blankLine() {
assertStartupSucceeds(" \n");
}
@Test
public void commentLines() {
assertStartupSucceeds("# comment line \n"
+ " # comment line starting with spaces\n");
}
@Test
public void classDoesNotExist() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails("no.such.class\n");
}
@Test
public void classThrowsExceptionWhenLoading() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(ThrowsExceptionWhenLoading.class);
}
@Test
public void classIsPrivate() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(PrivateClass.class);
}
@Test
public void noDefaultConstructor() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(NoDefaultConstructor.class);
}
@Test
public void constructorIsPrivate() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(PrivateConstructor.class);
}
@Test
public void constructorThrowsException() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(ConstructorThrowsException.class);
}
@Test
public void notAServletContextListener() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(NotAListener.class);
}
@Test
public void listenerThrowsException() {
assertStartupFails(InitThrowsException.class);
}
@Test
public void listenerSetsFatalStatus() {
assertStartupFails(InitSetsFatalStatus.class);
}
@Test
public void success() {
String listener1Name = SucceedsWithInfo.class.getName();
String listener2Name = SucceedsWithWarning.class.getName();
assertStartupSucceeds(SucceedsWithInfo.class, SucceedsWithWarning.class);
// Did they initialize in the correct order?
List<StatusItem> items = ss.getStatusItems();
assertEquals("how many", 2, items.size());
assertEquals("init order 1", listener1Name, items.get(0).sourceName);
assertEquals("init order 2", listener2Name, items.get(1).sourceName);
sm.contextDestroyed(sce);
// Did they destroy in reverse order?
items = ss.getStatusItems();
assertEquals("how many", 4, items.size());
assertEquals("destroy order 1", listener2Name, items.get(2).sourceName);
assertEquals("destroy order 2", listener1Name, items.get(3).sourceName);
}
@Test
public void duplicateListeners() {
setLoggerLevel(StartupManager.class, Level.OFF);
assertStartupFails(SucceedsWithInfo.class, SucceedsWithWarning.class,
SucceedsWithInfo.class);
}
@Test
public void dontExecuteAfterFailure() {
assertStartupFails(InitThrowsException.class, SucceedsWithInfo.class);
for (StatusItem item : ss.getStatusItems()) {
if (item.sourceName.equals(SucceedsWithInfo.class.getName())
&& (item.level == StatusItem.Level.NOT_EXECUTED)) {
return;
}
}
fail("'" + SucceedsWithInfo.class.getName()
+ "' should not have been run after '"
+ PrivateConstructor.class.getName() + "' failed.");
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
public static class BasicListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// does nothing
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// does nothing
}
}
public static class ThrowsExceptionWhenLoading extends BasicListener {
static {
if (true) {
throw new IllegalStateException("can't load me.");
}
}
}
private static class PrivateClass extends BasicListener {
// no methods
}
public static class NoDefaultConstructor extends BasicListener {
public NoDefaultConstructor(String bogus) {
bogus.length();
}
}
public static class PrivateConstructor extends BasicListener {
private PrivateConstructor() {
// does nothing
}
}
public static class ConstructorThrowsException extends BasicListener {
public ConstructorThrowsException() {
if (true) {
throw new IllegalStateException("can't load me.");
}
}
}
public static class NotAListener {
// no methods
}
public static class InitThrowsException extends BasicListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
throw new IllegalStateException("Initialization failed.");
}
}
public static class InitSetsFatalStatus extends BasicListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
StartupStatus.getBean(sce.getServletContext()).fatal(this,
"Set fatal status");
}
}
public static class SucceedsWithInfo implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
StartupStatus.getBean(sce.getServletContext()).info(this,
"Set info message on init.");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
StartupStatus.getBean(sce.getServletContext()).info(this,
"Set info message on destroy.");
}
}
public static class SucceedsWithWarning implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
StartupStatus.getBean(sce.getServletContext()).warning(this,
"Set warning message on init.");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
StartupStatus.getBean(sce.getServletContext()).warning(this,
"Set warning message on destroy.");
}
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void assertStartupFails(String fileContents) {
if (fileContents != null) {
ctx.setMockResource(StartupManager.FILE_OF_STARTUP_LISTENERS,
fileContents);
}
sm.contextInitialized(sce);
assertTrue("expecting abort", ss.isStartupAborted());
}
private void assertStartupFails(Class<?>... classes) {
assertStartupFails(joinClassNames(classes));
}
private void assertStartupSucceeds(String fileContents) {
if (fileContents != null) {
ctx.setMockResource(StartupManager.FILE_OF_STARTUP_LISTENERS,
fileContents);
}
sm.contextInitialized(sce);
assertFalse("expecting success", ss.isStartupAborted());
}
private void assertStartupSucceeds(Class<?>... classes) {
assertStartupSucceeds(joinClassNames(classes));
}
private String joinClassNames(Class<?>[] classes) {
if (classes == null) {
return null;
}
if (classes.length == 0) {
return "";
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < classes.length; i++) {
result.append(classes[i].getName()).append('\n');
}
return result.toString();
}
private void dumpStatus() {
List<StatusItem> items = ss.getStatusItems();
log.debug("-------------- " + items.size() + " items");
for (StatusItem item : items) {
log.debug(String.format("%8s %s \n %s \n %s", item.level,
item.sourceName, item.message, item.cause));
}
}
}