SPR-5507 Added support for shutdown order on SmartLifecycle. DefaultLifecycleProcessor now manages the shutdown in phases depending on that order (with a timeout value per group).

This commit is contained in:
Mark Fisher
2009-11-12 02:09:44 +00:00
parent d666f0a7d6
commit 535ec5cffd
8 changed files with 490 additions and 7 deletions

View File

@@ -0,0 +1,271 @@
/*
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.Test;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
/**
* @author Mark Fisher
* @since 3.0
*/
public class DefaultLifecycleProcessorTests {
@Test
public void smartLifecycleGroupShutdown() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
TestSmartLifecycleBean bean1 = new TestSmartLifecycleBean(1, 300, stoppedBeans);
TestSmartLifecycleBean bean2 = new TestSmartLifecycleBean(3, 100, stoppedBeans);
TestSmartLifecycleBean bean3 = new TestSmartLifecycleBean(1, 600, stoppedBeans);
TestSmartLifecycleBean bean4 = new TestSmartLifecycleBean(2, 400, stoppedBeans);
TestSmartLifecycleBean bean5 = new TestSmartLifecycleBean(2, 700, stoppedBeans);
TestSmartLifecycleBean bean6 = new TestSmartLifecycleBean(Integer.MAX_VALUE, 200, stoppedBeans);
TestSmartLifecycleBean bean7 = new TestSmartLifecycleBean(3, 200, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3);
context.getBeanFactory().registerSingleton("bean4", bean4);
context.getBeanFactory().registerSingleton("bean5", bean5);
context.getBeanFactory().registerSingleton("bean6", bean6);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.refresh();
context.stop();
assertEquals(1, getShutdownOrder(stoppedBeans.get(0)));
assertEquals(1, getShutdownOrder(stoppedBeans.get(1)));
assertEquals(2, getShutdownOrder(stoppedBeans.get(2)));
assertEquals(2, getShutdownOrder(stoppedBeans.get(3)));
assertEquals(3, getShutdownOrder(stoppedBeans.get(4)));
assertEquals(3, getShutdownOrder(stoppedBeans.get(5)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(6)));
}
@Test
public void singleSmartLifecycleShutdown() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
TestSmartLifecycleBean bean = new TestSmartLifecycleBean(99, 300, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.refresh();
assertTrue(bean.isRunning());
context.stop();
assertEquals(1, stoppedBeans.size());
assertFalse(bean.isRunning());
assertEquals(bean, stoppedBeans.get(0));
}
@Test
public void singleLifecycleShutdown() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
Lifecycle bean = new TestLifecycleBean(stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.refresh();
assertFalse(bean.isRunning());
bean.start();
assertTrue(bean.isRunning());
context.stop();
assertEquals(1, stoppedBeans.size());
assertFalse(bean.isRunning());
assertEquals(bean, stoppedBeans.get(0));
}
@Test
public void mixedShutdown() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
Lifecycle bean1 = new TestLifecycleBean(stoppedBeans);
Lifecycle bean2 = new TestSmartLifecycleBean(500, 200, stoppedBeans);
Lifecycle bean3 = new TestSmartLifecycleBean(Integer.MAX_VALUE, 100, stoppedBeans);
Lifecycle bean4 = new TestLifecycleBean(stoppedBeans);
Lifecycle bean5 = new TestSmartLifecycleBean(1, 200, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3);
context.getBeanFactory().registerSingleton("bean4", bean4);
context.getBeanFactory().registerSingleton("bean5", bean5);
context.refresh();
assertFalse(bean1.isRunning());
assertFalse(bean4.isRunning());
bean1.start();
bean4.start();
assertTrue(bean1.isRunning());
assertTrue(bean2.isRunning());
assertTrue(bean3.isRunning());
assertTrue(bean4.isRunning());
assertTrue(bean5.isRunning());
context.stop();
assertFalse(bean1.isRunning());
assertFalse(bean2.isRunning());
assertFalse(bean3.isRunning());
assertFalse(bean4.isRunning());
assertFalse(bean5.isRunning());
assertEquals(5, stoppedBeans.size());
assertEquals(1, getShutdownOrder(stoppedBeans.get(0)));
assertEquals(500, getShutdownOrder(stoppedBeans.get(1)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(2)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(3)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(4)));
}
@Test
public void dependantShutdownFirstEvenIfItsOrderIsHigher() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
TestSmartLifecycleBean bean1 = new TestSmartLifecycleBean(1, 200, stoppedBeans);
TestSmartLifecycleBean bean99 = new TestSmartLifecycleBean(99, 100, stoppedBeans);
TestSmartLifecycleBean bean2 = new TestSmartLifecycleBean(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = new TestSmartLifecycleBean(7, 400, stoppedBeans);
TestSmartLifecycleBean beanLast = new TestSmartLifecycleBean(Integer.MAX_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("beanLast", beanLast);
context.getBeanFactory().registerDependentBean("bean2", "bean99");
context.refresh();
assertTrue(bean1.isRunning());
assertTrue(bean7.isRunning());
assertTrue(bean99.isRunning());
context.stop();
assertFalse(bean1.isRunning());
assertFalse(bean7.isRunning());
assertFalse(bean99.isRunning());
assertEquals(5, stoppedBeans.size());
assertEquals(1, getShutdownOrder(stoppedBeans.get(0)));
assertEquals(99, getShutdownOrder(stoppedBeans.get(1)));
assertEquals(2, getShutdownOrder(stoppedBeans.get(2)));
assertEquals(7, getShutdownOrder(stoppedBeans.get(3)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(4)));
}
@Test
public void dependantShutdownFirstEvenIfNotSmartLifecycle() throws Exception {
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<Lifecycle>();
TestSmartLifecycleBean bean1 = new TestSmartLifecycleBean(1, 200, stoppedBeans);
TestLifecycleBean simpleBean = new TestLifecycleBean(stoppedBeans);
TestSmartLifecycleBean bean2 = new TestSmartLifecycleBean(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = new TestSmartLifecycleBean(7, 400, stoppedBeans);
TestSmartLifecycleBean beanLast = new TestSmartLifecycleBean(Integer.MAX_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerSingleton("beanLast", beanLast);
context.getBeanFactory().registerDependentBean("bean2", "simpleBean");
context.refresh();
assertTrue(bean1.isRunning());
assertTrue(bean7.isRunning());
assertFalse(simpleBean.isRunning());
simpleBean.start();
assertTrue(simpleBean.isRunning());
context.stop();
assertFalse(bean1.isRunning());
assertFalse(bean7.isRunning());
assertFalse(simpleBean.isRunning());
assertEquals(5, stoppedBeans.size());
assertEquals(1, getShutdownOrder(stoppedBeans.get(0)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(1)));
assertEquals(2, getShutdownOrder(stoppedBeans.get(2)));
assertEquals(7, getShutdownOrder(stoppedBeans.get(3)));
assertEquals(Integer.MAX_VALUE, getShutdownOrder(stoppedBeans.get(4)));
}
private static int getShutdownOrder(Lifecycle lifecycle) {
return (lifecycle instanceof SmartLifecycle) ?
((SmartLifecycle) lifecycle).getShutdownOrder() : Integer.MAX_VALUE;
}
private class TestLifecycleBean implements Lifecycle {
protected final CopyOnWriteArrayList<Lifecycle> stoppedBeans;
private volatile boolean running;
TestLifecycleBean(CopyOnWriteArrayList<Lifecycle> stoppedBeans) {
this.stoppedBeans = stoppedBeans;
}
public boolean isRunning() {
return this.running;
}
public void start() {
this.running = true;
}
public void stop() {
this.stoppedBeans.add(this);
this.running = false;
}
}
private class TestSmartLifecycleBean extends TestLifecycleBean implements SmartLifecycle {
private final int shutdownOrder;
private final int shutdownDelay;
TestSmartLifecycleBean(int shutdownOrder, int shutdownDelay, CopyOnWriteArrayList<Lifecycle> stoppedBeans) {
super(stoppedBeans);
this.shutdownOrder = shutdownOrder;
this.shutdownDelay = shutdownDelay;
}
public int getShutdownOrder() {
return this.shutdownOrder;
}
public boolean isAutoStartup() {
return true;
}
public void stop(final Runnable callback) {
final int delay = this.shutdownDelay;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(delay);
stop();
}
catch (InterruptedException e) {
// ignore
}
finally {
callback.run();
}
}
}).start();
}
}
}