All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.context.support.DefaultLifecycleProcessor Maven / Gradle / Ivy

There is a newer version: 6.1.13
Show newest version
/*
 * Copyright 2002-2022 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
 *
 *      https://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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.Lifecycle;
import org.springframework.context.LifecycleProcessor;
import org.springframework.context.Phased;
import org.springframework.context.SmartLifecycle;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Default implementation of the {@link LifecycleProcessor} strategy.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @since 3.0
 */
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {

	private final Log logger = LogFactory.getLog(getClass());

	private volatile long timeoutPerShutdownPhase = 30000;

	private volatile boolean running;

	@Nullable
	private volatile ConfigurableListableBeanFactory beanFactory;


	/**
	 * Specify the maximum time allotted in milliseconds for the shutdown of
	 * any phase (group of SmartLifecycle beans with the same 'phase' value).
	 * 

The default value is 30 seconds. */ public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) { this.timeoutPerShutdownPhase = timeoutPerShutdownPhase; } @Override public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) { throw new IllegalArgumentException( "DefaultLifecycleProcessor requires a ConfigurableListableBeanFactory: " + beanFactory); } this.beanFactory = clbf; } private ConfigurableListableBeanFactory getBeanFactory() { ConfigurableListableBeanFactory beanFactory = this.beanFactory; Assert.state(beanFactory != null, "No BeanFactory available"); return beanFactory; } // Lifecycle implementation /** * Start all registered beans that implement {@link Lifecycle} and are not * already running. Any bean that implements {@link SmartLifecycle} will be * started within its 'phase', and all phases will be ordered from lowest to * highest value. All beans that do not implement {@link SmartLifecycle} will be * started in the default phase 0. A bean declared as a dependency of another bean * will be started before the dependent bean regardless of the declared phase. */ @Override public void start() { startBeans(false); this.running = true; } /** * Stop all registered beans that implement {@link Lifecycle} and are * currently running. Any bean that implements {@link SmartLifecycle} will be * stopped within its 'phase', and all phases will be ordered from highest to * lowest value. All beans that do not implement {@link SmartLifecycle} will be * stopped in the default phase 0. A bean declared as dependent on another bean * will be stopped before the dependency bean regardless of the declared phase. */ @Override public void stop() { stopBeans(); this.running = false; } @Override public void onRefresh() { startBeans(true); this.running = true; } @Override public void onClose() { stopBeans(); this.running = false; } @Override public boolean isRunning() { return this.running; } // Internal helpers private void startBeans(boolean autoStartupOnly) { Map lifecycleBeans = getLifecycleBeans(); Map phases = new TreeMap<>(); lifecycleBeans.forEach((beanName, bean) -> { if (!autoStartupOnly || (bean instanceof SmartLifecycle smartLifecycle && smartLifecycle.isAutoStartup())) { int phase = getPhase(bean); phases.computeIfAbsent( phase, p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly) ).add(beanName, bean); } }); if (!phases.isEmpty()) { phases.values().forEach(LifecycleGroup::start); } } /** * 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 a 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, boolean autoStartupOnly) { Lifecycle bean = lifecycleBeans.remove(beanName); if (bean != null && bean != this) { String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName); for (String dependency : dependenciesForBean) { doStart(lifecycleBeans, dependency, autoStartupOnly); } if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle smartLifecycle) || smartLifecycle.isAutoStartup())) { if (logger.isTraceEnabled()) { logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]"); } try { bean.start(); } catch (Throwable ex) { throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex); } if (logger.isDebugEnabled()) { logger.debug("Successfully started bean '" + beanName + "'"); } } } } private void stopBeans() { Map lifecycleBeans = getLifecycleBeans(); Map phases = new HashMap<>(); lifecycleBeans.forEach((beanName, bean) -> { int shutdownPhase = getPhase(bean); LifecycleGroup group = phases.get(shutdownPhase); if (group == null) { group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false); phases.put(shutdownPhase, group); } group.add(beanName, bean); }); if (!phases.isEmpty()) { List keys = new ArrayList<>(phases.keySet()); keys.sort(Collections.reverseOrder()); for (Integer key : keys) { phases.get(key).stop(); } } } /** * 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 a 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, final String beanName, final CountDownLatch latch, final Set countDownBeanNames) { Lifecycle bean = lifecycleBeans.remove(beanName); if (bean != null) { String[] dependentBeans = getBeanFactory().getDependentBeans(beanName); for (String dependentBean : dependentBeans) { doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames); } try { if (bean.isRunning()) { if (bean instanceof SmartLifecycle smartLifecycle) { if (logger.isTraceEnabled()) { logger.trace("Asking bean '" + beanName + "' of type [" + bean.getClass().getName() + "] to stop"); } countDownBeanNames.add(beanName); smartLifecycle.stop(() -> { latch.countDown(); countDownBeanNames.remove(beanName); if (logger.isDebugEnabled()) { logger.debug("Bean '" + beanName + "' completed its stop procedure"); } }); } else { if (logger.isTraceEnabled()) { logger.trace("Stopping bean '" + beanName + "' of type [" + bean.getClass().getName() + "]"); } bean.stop(); if (logger.isDebugEnabled()) { logger.debug("Successfully stopped bean '" + beanName + "'"); } } } else if (bean instanceof SmartLifecycle) { // Don't wait for beans that aren't running... latch.countDown(); } } catch (Throwable ex) { if (logger.isWarnEnabled()) { logger.warn("Failed to stop bean '" + beanName + "'", ex); } } } } // overridable hooks /** * Retrieve all applicable Lifecycle beans: all singletons that have already been created, * as well as all SmartLifecycle beans (even if they are marked as lazy-init). * @return the Map of applicable beans, with bean names as keys and bean instances as values */ protected Map getLifecycleBeans() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); Map beans = new LinkedHashMap<>(); String[] beanNames = beanFactory.getBeanNamesForType(Lifecycle.class, false, false); for (String beanName : beanNames) { String beanNameToRegister = BeanFactoryUtils.transformedBeanName(beanName); boolean isFactoryBean = beanFactory.isFactoryBean(beanNameToRegister); String beanNameToCheck = (isFactoryBean ? BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName); if ((beanFactory.containsSingleton(beanNameToRegister) && (!isFactoryBean || matchesBeanType(Lifecycle.class, beanNameToCheck, beanFactory))) || matchesBeanType(SmartLifecycle.class, beanNameToCheck, beanFactory)) { Object bean = beanFactory.getBean(beanNameToCheck); if (bean != this && bean instanceof Lifecycle lifecycle) { beans.put(beanNameToRegister, lifecycle); } } } return beans; } private boolean matchesBeanType(Class targetType, String beanName, BeanFactory beanFactory) { Class beanType = beanFactory.getType(beanName); return (beanType != null && targetType.isAssignableFrom(beanType)); } /** * Determine the lifecycle phase of the given bean. *

The default implementation checks for the {@link Phased} interface, using * a default of 0 otherwise. Can be overridden to apply other/further policies. * @param bean the bean to introspect * @return the phase (an integer value) * @see Phased#getPhase() * @see SmartLifecycle */ protected int getPhase(Lifecycle bean) { return (bean instanceof Phased phased ? phased.getPhase() : 0); } /** * Helper class for maintaining a group of Lifecycle beans that should be started * and stopped together based on their 'phase' value (or the default value of 0). */ private class LifecycleGroup { private final int phase; private final long timeout; private final Map lifecycleBeans; private final boolean autoStartupOnly; private final List members = new ArrayList<>(); private int smartMemberCount; public LifecycleGroup( int phase, long timeout, Map lifecycleBeans, boolean autoStartupOnly) { this.phase = phase; this.timeout = timeout; this.lifecycleBeans = lifecycleBeans; this.autoStartupOnly = autoStartupOnly; } public void add(String name, Lifecycle bean) { this.members.add(new LifecycleGroupMember(name, bean)); if (bean instanceof SmartLifecycle) { this.smartMemberCount++; } } public void start() { if (this.members.isEmpty()) { return; } if (logger.isDebugEnabled()) { logger.debug("Starting beans in phase " + this.phase); } Collections.sort(this.members); for (LifecycleGroupMember member : this.members) { doStart(this.lifecycleBeans, member.name, this.autoStartupOnly); } } public void stop() { if (this.members.isEmpty()) { return; } if (logger.isDebugEnabled()) { logger.debug("Stopping beans in phase " + this.phase); } this.members.sort(Collections.reverseOrder()); CountDownLatch latch = new CountDownLatch(this.smartMemberCount); Set countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet<>()); Set lifecycleBeanNames = new HashSet<>(this.lifecycleBeans.keySet()); for (LifecycleGroupMember member : this.members) { if (lifecycleBeanNames.contains(member.name)) { doStop(this.lifecycleBeans, member.name, latch, countDownBeanNames); } else if (member.bean instanceof SmartLifecycle) { // Already removed: must have been a dependent bean from another phase latch.countDown(); } } try { latch.await(this.timeout, TimeUnit.MILLISECONDS); if (latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isInfoEnabled()) { logger.info("Failed to shut down " + countDownBeanNames.size() + " bean" + (countDownBeanNames.size() > 1 ? "s" : "") + " with phase value " + this.phase + " within timeout of " + this.timeout + "ms: " + countDownBeanNames); } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } /** * Adapts the Comparable interface onto the lifecycle phase model. */ private class LifecycleGroupMember implements Comparable { private final String name; private final Lifecycle bean; LifecycleGroupMember(String name, Lifecycle bean) { this.name = name; this.bean = bean; } @Override public int compareTo(LifecycleGroupMember other) { int thisPhase = getPhase(this.bean); int otherPhase = getPhase(other.bean); return Integer.compare(thisPhase, otherPhase); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy