SPR-5507 The 'shutdownOrder' property of SmartLifecycle has been renamed 'phase'. The order no longer applies to shutdown only; now startup order is determined by the phase value as well. Components start in ascending order and stop in descending order.

This commit is contained in:
Mark Fisher
2009-11-24 16:07:23 +00:00
parent 1f5568fa81
commit 021663b12f
7 changed files with 541 additions and 176 deletions

View File

@@ -33,14 +33,28 @@ public interface SmartLifecycle extends Lifecycle {
boolean isAutoStartup();
/**
* Return the order in which this Lifecycle component should be stopped.
* The shutdown process begins with the component(s) having the <i>lowest</i>
* value and ends with the highest value (Integer.MIN_VALUE is the lowest
* possible, and Integer.MAX_VALUE is the highest possible). Any Lifecycle
* components within the context that do not also implement SmartLifecycle
* will be treated as if they have a value of Integer.MAX_VALUE.
* Return the phase within which this Lifecycle component should be started
* and stopped. The startup process begins with the <i>lowest</i> phase
* value and ends with the <i>highest</i> phase value (Integer.MIN_VALUE is
* the lowest possible, and Integer.MAX_VALUE is the highest possible). The
* shutdown process will apply the reverse order. Any components with the
* same value will be arbitrarily ordered within the same phase.
* <p>
* Example: if component B depends on component A having already started, then
* component A should have a lower phase value than component B. During the
* shutdown process, component B would be stopped before component A.
* <p>
* Any Lifecycle components within the context that do not also implement
* SmartLifecycle will be treated as if they have a phase value of 0. That
* way a SmartLifecycle implementation may start before those Lifecycle
* components if it has a negative phase value, or it may start after
* those components if it has a positive phase value.
* <p>
* Any explicit "depends-on" relationship will take precedence over
* the phase order such that the dependent bean always starts after its
* dependency and always stops before its dependency.
*/
int getShutdownOrder();
int getPhase();
/**
* Indicates that a Lifecycle component must stop if it is currently running.

View File

@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -47,7 +46,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private final Log logger = LogFactory.getLog(this.getClass());
private volatile long shutdownGroupTimeout = 30000;
private volatile long timeoutPerShutdownPhase = 30000;
private volatile boolean running;
@@ -55,12 +54,12 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
/**
* Specify the maximum time allotted for the shutdown of any group of
* SmartLifecycle beans (those with the same 'order' value). The default
* value is 30 seconds.
* Specify the maximum time allotted for the shutdown of any phase
* (group of SmartLifecycle beans with the same 'phase' value).
* The default value is 30 seconds.
*/
public void setShutdownGroupTimeout(long shutdownGroupTimeout) {
this.shutdownGroupTimeout = shutdownGroupTimeout;
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
}
public void setBeanFactory(BeanFactory beanFactory) {
@@ -77,58 +76,84 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
return this.running;
}
/**
* Start all registered beans that implement Lifecycle and are
* <i>not</i> already running. Any bean that implements SmartLifecycle
* will be started within its 'phase', and all phases will be ordered
* from lowest to highest value. All beans that do not implement
* 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.
*/
public void start() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
for (String beanName : new LinkedHashSet<String>(lifecycleBeans.keySet())) {
doStart(lifecycleBeans, beanName);
}
this.running = true;
this.startBeans(false);
}
/**
* Stop all registered beans that implement Lifecycle and <i>are</i>
* currently running. Any bean that implements SmartLifecycle
* will be stopped within its 'phase', and all phases will be ordered
* from highest to lowest value. All beans that do not implement
* 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.
*/
public void stop() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, ShutdownGroup> shutdownGroups = new HashMap<Integer, ShutdownGroup>();
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) {
Lifecycle lifecycle = entry.getValue();
int shutdownOrder = getShutdownOrder(lifecycle);
ShutdownGroup group = shutdownGroups.get(shutdownOrder);
int shutdownOrder = getPhase(lifecycle);
LifecycleGroup group = phases.get(shutdownOrder);
if (group == null) {
group = new ShutdownGroup(shutdownOrder, this.shutdownGroupTimeout, lifecycleBeans);
shutdownGroups.put(shutdownOrder, group);
group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans);
phases.put(shutdownOrder, group);
}
group.add(entry.getKey(), lifecycle);
}
if (shutdownGroups.size() > 0) {
List<Integer> keys = new ArrayList<Integer>(shutdownGroups.keySet());
Collections.sort(keys);
if (phases.size() > 0) {
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
Collections.sort(keys, Collections.reverseOrder());
for (Integer key : keys) {
shutdownGroups.get(key).shutdown();
phases.get(key).stop();
}
}
this.running = false;
}
public void onRefresh() {
Map<String, SmartLifecycle> lifecycleBeans = getSmartLifecycleBeans();
for (String beanName : new LinkedHashSet<String>(lifecycleBeans.keySet())) {
SmartLifecycle bean = lifecycleBeans.get(beanName);
if (bean != null && bean.isAutoStartup()) {
String[] dependenciesForBean = this.beanFactory.getDependenciesForBean(beanName);
for (String dependency : dependenciesForBean) {
doStart(lifecycleBeans, dependency);
}
if (!bean.isRunning()) {
bean.start();
}
lifecycleBeans.remove(beanName);
}
}
this.startBeans(true);
}
public void onClose() {
stop();
}
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
for (Map.Entry<String, ? extends Lifecycle> entry : lifecycleBeans.entrySet()) {
Lifecycle lifecycle = entry.getValue();
if (!autoStartupOnly || (lifecycle instanceof SmartLifecycle && ((SmartLifecycle) lifecycle).isAutoStartup())) {
int phase = getPhase(lifecycle);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans);
phases.put(phase, group);
}
group.add(entry.getKey(), lifecycle);
}
}
if (phases.size() > 0) {
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
this.running = true;
}
/**
* 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.
@@ -155,7 +180,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
* @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<String, Lifecycle> lifecycleBeans, String beanName, final CountDownLatch latch) {
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, final CountDownLatch latch) {
Lifecycle bean = lifecycleBeans.get(beanName);
if (bean != null) {
String[] dependentBeans = this.beanFactory.getDependentBeans(beanName);
@@ -194,44 +219,31 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
return beans;
}
private Map<String, SmartLifecycle> getSmartLifecycleBeans() {
String[] beanNames = beanFactory.getSingletonNames();
Map<String, SmartLifecycle> beans = new LinkedHashMap<String, SmartLifecycle>();
for (String beanName : beanNames) {
Object bean = beanFactory.getSingleton(beanName);
if (bean instanceof SmartLifecycle) {
beans.put(beanName, (SmartLifecycle) bean);
}
}
return beans;
}
private static int getShutdownOrder(Lifecycle bean) {
private static int getPhase(Lifecycle bean) {
return (bean instanceof SmartLifecycle) ?
((SmartLifecycle) bean).getShutdownOrder() : Integer.MAX_VALUE;
((SmartLifecycle) bean).getPhase() : 0;
}
/**
* Helper class for maintaining a group of Lifecycle beans that should be shutdown
* together based on their 'shutdownOrder' value (or the default MAX_INTEGER value).
* 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 ShutdownGroup {
private class LifecycleGroup {
private final List<ShutdownGroupMember> members = new ArrayList<ShutdownGroupMember>();
private final List<LifecycleGroupMember> members = new ArrayList<LifecycleGroupMember>();
private Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
private Map<String, ? extends Lifecycle> lifecycleBeans = getLifecycleBeans();
private volatile int smartMemberCount;
private final int order;
private final int phase;
private final long timeout;
ShutdownGroup(int order, long timeout, Map<String, Lifecycle> lifecycleBeans) {
this.order = order;
LifecycleGroup(int phase, long timeout, Map<String, ? extends Lifecycle> lifecycleBeans) {
this.phase = phase;
this.timeout = timeout;
this.lifecycleBeans = lifecycleBeans;
}
@@ -240,16 +252,28 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
if (bean instanceof SmartLifecycle) {
this.smartMemberCount++;
}
this.members.add(new ShutdownGroupMember(name, bean));
this.members.add(new LifecycleGroupMember(name, bean));
}
void shutdown() {
void start() {
if (members.size() == 0) {
return;
}
Collections.sort(members);
for (LifecycleGroupMember member : members) {
if (lifecycleBeans.containsKey(member.name)) {
doStart(lifecycleBeans, member.name);
}
}
}
void stop() {
if (members.size() == 0) {
return;
}
Collections.sort(members, Collections.reverseOrder());
final CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
for (ShutdownGroupMember member : members) {
for (LifecycleGroupMember member : members) {
if (lifecycleBeans.containsKey(member.name)) {
doStop(lifecycleBeans, member.name, latch);
}
@@ -262,8 +286,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
latch.await(this.timeout, TimeUnit.MILLISECONDS);
if (latch.getCount() != 0) {
if (logger.isWarnEnabled()) {
logger.warn("failed to shutdown beans with order " +
this.order + " within timeout of " + this.timeout);
logger.warn("failed to shutdown beans with phase value " +
this.phase + " within timeout of " + this.timeout);
}
}
}
@@ -274,20 +298,20 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
private static class ShutdownGroupMember implements Comparable<ShutdownGroupMember> {
private static class LifecycleGroupMember implements Comparable<LifecycleGroupMember> {
private String name;
private final String name;
private Lifecycle bean;
private final Lifecycle bean;
ShutdownGroupMember(String name, Lifecycle bean) {
LifecycleGroupMember(String name, Lifecycle bean) {
this.name = name;
this.bean = bean;
}
public int compareTo(ShutdownGroupMember other) {
int thisOrder = getShutdownOrder(this.bean);
int otherOrder = getShutdownOrder(other.bean);
public int compareTo(LifecycleGroupMember other) {
int thisOrder = getPhase(this.bean);
int otherOrder = getPhase(other.bean);
return (thisOrder == otherOrder) ? 0 : (thisOrder < otherOrder) ? -1 : 1;
}
}