diff --git a/org.springframework.context/src/main/java/org/springframework/context/LifecycleProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/LifecycleProcessor.java new file mode 100644 index 00000000000..8d39eab60aa --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/LifecycleProcessor.java @@ -0,0 +1,25 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context; + +/** + * @author Mark Fisher + * @since 3.0 + */ +public interface LifecycleProcessor extends Lifecycle { + +} diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 93db7d25595..d0db5de9037 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -50,6 +49,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.HierarchicalMessageSource; import org.springframework.context.Lifecycle; +import org.springframework.context.LifecycleProcessor; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.MessageSourceResolvable; @@ -126,6 +126,14 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader */ public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource"; + /** + * Name of the LifecycleProcessor bean in the factory. + * If none is supplied, a DefaultLifecycleProcessor is used. + * @see org.springframework.context.LifecycleProcessor + * @see org.springframework.context.support.DefaultLifecycleProcessor + */ + public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor"; + /** * Name of the ApplicationEventMulticaster bean in the factory. * If none is supplied, a default SimpleApplicationEventMulticaster is used. @@ -179,6 +187,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader /** MessageSource we delegate our implementation of this interface to */ private MessageSource messageSource; + /** LifecycleProcessor for managing the lifecycle of beans within this context */ + private LifecycleProcessor lifecycleProcessor; + /** Helper class used in event publishing */ private ApplicationEventMulticaster applicationEventMulticaster; @@ -287,8 +298,21 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } /** - * Return the internal MessageSource used by the context. - * @return the internal MessageSource (never null) + * Return the internal LifecycleProcessor used by the context. + * @return the internal LifecycleProcessor (never null) + * @throws IllegalStateException if the context has not been initialized yet + */ + private LifecycleProcessor getLifecycleProcessor() { + if (this.lifecycleProcessor == null) { + throw new IllegalStateException("LifecycleProcessor not initialized - " + + "call 'refresh' before invoking lifecycle methods via the context: " + this); + } + return this.lifecycleProcessor; + } + + /** + * Return the internal ApplicationEventMulticaster used by the context. + * @return the internal ApplicationEventMulticaster (never null) * @throws IllegalStateException if the context has not been initialized yet */ private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException { @@ -377,6 +401,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader // Initialize message source for this context. initMessageSource(); + // Initialize lifecycle processor for this context. + initLifecycleProcessor(); + // Initialize event multicaster for this context. initApplicationEventMulticaster(); @@ -676,6 +703,33 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } } + /** + * Initialize the LifecycleProcessor. + * Uses DefaultLifecycleProcessor if none defined in the context. + * @see org.springframework.context.support.DefaultLifecycleProcessor + */ + protected void initLifecycleProcessor() { + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) { + this.lifecycleProcessor = + beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class); + if (logger.isDebugEnabled()) { + logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]"); + } + } + else { + DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor(); + defaultProcessor.setBeanFactory(beanFactory); + this.lifecycleProcessor = defaultProcessor; + beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor); + if (logger.isDebugEnabled()) { + logger.debug("Unable to locate LifecycleProcessor with name '" + + LIFECYCLE_PROCESSOR_BEAN_NAME + + "': using default [" + this.lifecycleProcessor + "]"); + } + } + } + /** * Initialize the ApplicationEventMulticaster. * Uses SimpleApplicationEventMulticaster if none defined in the context. @@ -851,10 +905,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } // Stop all Lifecycle beans, to avoid delays during individual destruction. - Map lifecycleBeans = getLifecycleBeans(); - for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) { - doStop(lifecycleBeans, beanName); - } + this.getLifecycleProcessor().stop(); // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); @@ -1076,18 +1127,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader //--------------------------------------------------------------------- public void start() { - Map lifecycleBeans = getLifecycleBeans(); - for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) { - doStart(lifecycleBeans, beanName); - } + this.getLifecycleProcessor().start(); publishEvent(new ContextStartedEvent(this)); } public void stop() { - Map lifecycleBeans = getLifecycleBeans(); - for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) { - doStop(lifecycleBeans, beanName); - } + this.getLifecycleProcessor().stop(); publishEvent(new ContextStoppedEvent(this)); } @@ -1118,46 +1163,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader return beans; } - /** - * Start the specified bean as part of the given set of Lifecycle beans, - * making sure that any beans that it depends on are started first. - * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value - * @param beanName the name of the bean to start - */ - private void doStart(Map lifecycleBeans, String beanName) { - Lifecycle bean = lifecycleBeans.get(beanName); - if (bean != null) { - String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName); - for (String dependency : dependenciesForBean) { - doStart(lifecycleBeans, dependency); - } - if (!bean.isRunning()) { - bean.start(); - } - lifecycleBeans.remove(beanName); - } - } - - /** - * Stop the specified bean as part of the given set of Lifecycle beans, - * making sure that any beans that depends on it are stopped first. - * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value - * @param beanName the name of the bean to stop - */ - private void doStop(Map lifecycleBeans, String beanName) { - Lifecycle bean = (Lifecycle) lifecycleBeans.get(beanName); - if (bean != null) { - String[] dependentBeans = getBeanFactory().getDependentBeans(beanName); - for (String dependentBean : dependentBeans) { - doStop(lifecycleBeans, dependentBean); - } - if (bean.isRunning()) { - bean.stop(); - } - lifecycleBeans.remove(beanName); - } - } - //--------------------------------------------------------------------- // Abstract methods that must be implemented by subclasses diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java new file mode 100644 index 00000000000..5dc249479b9 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.support; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.Lifecycle; +import org.springframework.context.LifecycleProcessor; +import org.springframework.util.Assert; + +/** + * @author Mark Fisher + * @since 3.0 + */ +public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware { + + private volatile boolean running; + + private volatile ConfigurableListableBeanFactory beanFactory; + + + public void setBeanFactory(BeanFactory beanFactory) { + Assert.isTrue(beanFactory instanceof ConfigurableListableBeanFactory, + "A ConfigurableListableBeanFactory is required."); + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + /* + * Lifecycle implementation + */ + + public boolean isRunning() { + return this.running; + } + + public void start() { + Map lifecycleBeans = getLifecycleBeans(); + for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) { + doStart(lifecycleBeans, beanName); + } + this.running = true; + } + + public void stop() { + Map lifecycleBeans = getLifecycleBeans(); + for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) { + doStop(lifecycleBeans, beanName); + } + this.running = false; + } + + /** + * Start the specified bean as part of the given set of Lifecycle beans, + * making sure that any beans that it depends on are started first. + * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value + * @param beanName the name of the bean to start + */ + private void doStart(Map lifecycleBeans, String beanName) { + Lifecycle bean = lifecycleBeans.get(beanName); + if (bean != null && !this.equals(bean)) { + String[] dependenciesForBean = this.beanFactory.getDependenciesForBean(beanName); + for (String dependency : dependenciesForBean) { + doStart(lifecycleBeans, dependency); + } + if (!bean.isRunning()) { + bean.start(); + } + lifecycleBeans.remove(beanName); + } + } + + /** + * Stop the specified bean as part of the given set of Lifecycle beans, + * making sure that any beans that depends on it are stopped first. + * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value + * @param beanName the name of the bean to stop + */ + private void doStop(Map lifecycleBeans, String beanName) { + Lifecycle bean = lifecycleBeans.get(beanName); + if (bean != null && !this.equals(bean)) { + String[] dependentBeans = this.beanFactory.getDependentBeans(beanName); + for (String dependentBean : dependentBeans) { + doStop(lifecycleBeans, dependentBean); + } + if (bean.isRunning()) { + bean.stop(); + } + lifecycleBeans.remove(beanName); + } + } + + private Map getLifecycleBeans() { + String[] beanNames = beanFactory.getSingletonNames(); + Map beans = new LinkedHashMap(); + for (String beanName : beanNames) { + Object bean = beanFactory.getSingleton(beanName); + if (bean instanceof Lifecycle) { + beans.put(beanName, (Lifecycle) bean); + } + } + return beans; + } + +}