Index: stripes/src/net/sourceforge/stripes/controller/StripesFilter.java =================================================================== --- stripes/src/net/sourceforge/stripes/controller/StripesFilter.java (revision 1407) +++ stripes/src/net/sourceforge/stripes/controller/StripesFilter.java (revision ) @@ -15,6 +15,7 @@ package net.sourceforge.stripes.controller; import net.sourceforge.stripes.config.BootstrapPropertyResolver; +import net.sourceforge.stripes.config.ComponentLifeCycleManager; import net.sourceforge.stripes.config.Configuration; import net.sourceforge.stripes.config.RuntimeConfiguration; import net.sourceforge.stripes.exception.StripesRuntimeException; @@ -77,7 +78,7 @@ * for the VM, and if so return it even when the Configuration isn't set in the thread local. */ private static final Set> configurations = - new HashSet>(); + new HashSet>(); /** * Some operations should only be done if the current invocation of @@ -91,6 +92,7 @@ return true; } }; + private ComponentLifeCycleManager componentLifeCycleManager; /** * Performs the necessary initialization for the StripesFilter. Mainly this involves deciding @@ -122,7 +124,9 @@ } this.configuration.setBootstrapPropertyResolver(bootstrap); - this.configuration.init(); + // This line is replaced by the life cycle manager + // this.configuration.init(); + componentLifeCycleManager.init(configuration); StripesFilter.configurations.add(new WeakReference(this.configuration)); this.servletContext = filterConfig.getServletContext(); @@ -312,6 +316,7 @@ /** Calls the cleanup() method on the log to release resources held by commons logging. */ public void destroy() { + componentLifeCycleManager.dispose(configuration); this.servletContext.removeAttribute(StripesFilter.class.getName()); Log.cleanup(); Introspector.flushCaches(); // Not 100% sure this is necessary, but it doesn't hurt Index: stripes/src/net/sourceforge/stripes/config/DisposableConfigurableComponent.java =================================================================== --- stripes/src/net/sourceforge/stripes/config/DisposableConfigurableComponent.java (revision ) +++ stripes/src/net/sourceforge/stripes/config/DisposableConfigurableComponent.java (revision ) @@ -0,0 +1,12 @@ +package net.sourceforge.stripes.config; + +public interface DisposableConfigurableComponent extends ConfigurableComponent { + /** + * Invoked after the application server perform a clean shutdown. In a terminate method would be + * possible to commit several data uncommitted Components are not expected to fail with a + * exception. Instead the should handle any failure on their own + * + * @param configuration the Configuration object being used by Stripes + */ + void dispose(Configuration configuration); +} Index: stripes/src/net/sourceforge/stripes/config/ComponentLifeCycleManager.java =================================================================== --- stripes/src/net/sourceforge/stripes/config/ComponentLifeCycleManager.java (revision ) +++ stripes/src/net/sourceforge/stripes/config/ComponentLifeCycleManager.java (revision ) @@ -0,0 +1,91 @@ +package net.sourceforge.stripes.config; + +import net.sourceforge.stripes.controller.Interceptor; +import net.sourceforge.stripes.controller.LifecycleStage; +import net.sourceforge.stripes.exception.StripesRuntimeException; +import net.sourceforge.stripes.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ComponentLifeCycleManager { + private static final Log log = Log.getInstance(ComponentLifeCycleManager.class); + private final List components; + + public ComponentLifeCycleManager() { + this.components = new ArrayList(); + } + + public void init(Configuration configuration) { + configure(configuration); + for (ConfigurableComponent component : components) { + try { + log.debug("initializing component ", getComponentName(component)); + component.init(configuration); + } + catch (Exception e) { + String componentName = getComponentName(component); + String message = "Problem initializing configurable component: " + componentName; + throw new StripesRuntimeException(message, e); + } + } + } + + void configure(Configuration configuration) { + add(configuration.getObjectFactory()); + add(configuration.getActionResolver()); + add(configuration.getActionBeanPropertyBinder()); + add(configuration.getActionBeanContextFactory()); + add(configuration.getTypeConverterFactory()); + add(configuration.getLocalizationBundleFactory()); + add(configuration.getLocalePicker()); + add(configuration.getFormatterFactory()); + add(configuration.getTagErrorRendererFactory()); + add(configuration.getPopulationStrategy()); + add(configuration.getExceptionHandler()); + add(configuration.getMultipartWrapperFactory()); + add(configuration.getValidationMetadataProvider()); + addInterceptors(configuration); + } + + private void addInterceptors(final Configuration configuration) {LifecycleStage[] lifeCycleStages = LifecycleStage.values(); + for (LifecycleStage lifeCycleStage : lifeCycleStages) { + Collection interceptors = configuration.getInterceptors(lifeCycleStage); + for (Interceptor interceptor : interceptors) { + if (interceptor instanceof ConfigurableComponent) { + add((ConfigurableComponent) interceptor); + } + } + } + } + + void add(ConfigurableComponent configurableComponent) { + log.trace("managing life cycle of component ", getComponentName(configurableComponent)); + components.add(configurableComponent); + } + + public void dispose(Configuration configuration) { + for (ConfigurableComponent component : components) { + if (component instanceof DisposableConfigurableComponent) { + tryDispose(configuration, (DisposableConfigurableComponent) component); + } + } + } + + private void tryDispose(Configuration configuration, DisposableConfigurableComponent component) { + DisposableConfigurableComponent disposable = (DisposableConfigurableComponent) component; + try { + log.debug("disposing component ", getComponentName(component)); + disposable.dispose(configuration); + } + catch (RuntimeException e) { + String disposableName = getComponentName(disposable); + log.error(e, "Unexpected error disposing ", disposableName); + } + } + + private String getComponentName(final ConfigurableComponent configurableComponent) { + return configurableComponent.getClass().getCanonicalName(); + } +}