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:
parent
d666f0a7d6
commit
535ec5cffd
|
|
@ -190,6 +190,8 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
|
|||
|
||||
private int startupDelay = 0;
|
||||
|
||||
private int shutdownOrder = Integer.MAX_VALUE;
|
||||
|
||||
private boolean exposeSchedulerInRepository = false;
|
||||
|
||||
private boolean waitForJobsToCompleteOnShutdown = false;
|
||||
|
|
@ -373,6 +375,21 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
|
|||
return this.autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order in which this scheduler should be stopped.
|
||||
* By default it will be stopped in the last group.
|
||||
*/
|
||||
public void setShutdownOrder(int shutdownOrder) {
|
||||
this.shutdownOrder = shutdownOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order in which this scheduler will be stopped.
|
||||
*/
|
||||
public int getShutdownOrder() {
|
||||
return this.shutdownOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds to wait after initialization before
|
||||
* starting the scheduler asynchronously. Default is 0, meaning
|
||||
|
|
@ -708,6 +725,11 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
|
|||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) throws SchedulingException {
|
||||
this.stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
public boolean isRunning() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package org.springframework.context;
|
||||
|
||||
/**
|
||||
* Strategy interface for processing Lifecycle beans within the ApplicationContext.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
package org.springframework.context;
|
||||
|
||||
/**
|
||||
* An extension of the Lifecycle interface for those beans that require to be
|
||||
* started upon ApplicationContext refresh and/or shutdown in a particular order.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
|
|
@ -24,4 +27,8 @@ public interface SmartLifecycle extends Lifecycle {
|
|||
|
||||
boolean isAutoStartup();
|
||||
|
||||
int getShutdownOrder();
|
||||
|
||||
void stop(Runnable callback);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,18 @@
|
|||
|
||||
package org.springframework.context.support;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
|
@ -29,16 +38,31 @@ import org.springframework.context.SmartLifecycle;
|
|||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile long shutdownGroupTimeout = 30000;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private volatile ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void setShutdownGroupTimeout(long shutdownGroupTimeout) {
|
||||
this.shutdownGroupTimeout = shutdownGroupTimeout;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isTrue(beanFactory instanceof ConfigurableListableBeanFactory,
|
||||
"A ConfigurableListableBeanFactory is required.");
|
||||
|
|
@ -63,8 +87,23 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
|||
|
||||
public void stop() {
|
||||
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
|
||||
for (String beanName : new LinkedHashSet<String>(lifecycleBeans.keySet())) {
|
||||
doStop(lifecycleBeans, beanName);
|
||||
Map<Integer, ShutdownGroup> shutdownGroups = new HashMap<Integer, ShutdownGroup>();
|
||||
for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) {
|
||||
Lifecycle lifecycle = entry.getValue();
|
||||
int shutdownOrder = getShutdownOrder(lifecycle);
|
||||
ShutdownGroup group = shutdownGroups.get(shutdownOrder);
|
||||
if (group == null) {
|
||||
group = new ShutdownGroup(shutdownOrder, this.shutdownGroupTimeout, lifecycleBeans);
|
||||
shutdownGroups.put(shutdownOrder, group);
|
||||
}
|
||||
group.add(entry.getKey(), lifecycle);
|
||||
}
|
||||
if (shutdownGroups.size() > 0) {
|
||||
List<Integer> keys = new ArrayList<Integer>(shutdownGroups.keySet());
|
||||
Collections.sort(keys);
|
||||
for (Integer key : keys) {
|
||||
shutdownGroups.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
|
@ -112,15 +151,24 @@ 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) {
|
||||
private void doStop(Map<String, Lifecycle> lifecycleBeans, String beanName, final CountDownLatch latch) {
|
||||
Lifecycle bean = lifecycleBeans.get(beanName);
|
||||
if (bean != null && !this.equals(bean)) {
|
||||
if (bean != null) {
|
||||
String[] dependentBeans = this.beanFactory.getDependentBeans(beanName);
|
||||
for (String dependentBean : dependentBeans) {
|
||||
doStop(lifecycleBeans, dependentBean);
|
||||
doStop(lifecycleBeans, dependentBean, latch);
|
||||
}
|
||||
if (bean.isRunning()) {
|
||||
bean.stop();
|
||||
if (bean instanceof SmartLifecycle) {
|
||||
((SmartLifecycle) bean).stop(new Runnable() {
|
||||
public void run() {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
bean.stop();
|
||||
}
|
||||
}
|
||||
lifecycleBeans.remove(beanName);
|
||||
}
|
||||
|
|
@ -131,7 +179,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
|||
Map<String, Lifecycle> beans = new LinkedHashMap<String, Lifecycle>();
|
||||
for (String beanName : beanNames) {
|
||||
Object bean = beanFactory.getSingleton(beanName);
|
||||
if (bean instanceof Lifecycle) {
|
||||
if (bean instanceof Lifecycle && !this.equals(bean)) {
|
||||
beans.put(beanName, (Lifecycle) bean);
|
||||
}
|
||||
}
|
||||
|
|
@ -150,4 +198,90 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
|||
return beans;
|
||||
}
|
||||
|
||||
|
||||
private static int getShutdownOrder(Lifecycle bean) {
|
||||
return (bean instanceof SmartLifecycle) ?
|
||||
((SmartLifecycle) bean).getShutdownOrder() : Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
private class ShutdownGroup {
|
||||
|
||||
private final List<ShutdownGroupMember> members = new ArrayList<ShutdownGroupMember>();
|
||||
|
||||
private Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
|
||||
|
||||
private volatile int smartMemberCount;
|
||||
|
||||
private final int order;
|
||||
|
||||
private final long timeout;
|
||||
|
||||
|
||||
ShutdownGroup(int order, long timeout, Map<String, Lifecycle> lifecycleBeans) {
|
||||
this.order = order;
|
||||
this.timeout = timeout;
|
||||
this.lifecycleBeans = lifecycleBeans;
|
||||
}
|
||||
|
||||
void add(String name, Lifecycle bean) {
|
||||
if (bean instanceof SmartLifecycle) {
|
||||
this.smartMemberCount++;
|
||||
}
|
||||
this.members.add(new ShutdownGroupMember(name, bean));
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (members.size() == 0) {
|
||||
return;
|
||||
}
|
||||
Collections.sort(members);
|
||||
final CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
|
||||
for (ShutdownGroupMember member : members) {
|
||||
if (lifecycleBeans.containsKey(member.name)) {
|
||||
doStop(lifecycleBeans, member.name, latch);
|
||||
}
|
||||
else if (member.bean instanceof SmartLifecycle) {
|
||||
// already removed, must have been a dependent
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ShutdownGroupMember implements Comparable<ShutdownGroupMember> {
|
||||
|
||||
private String name;
|
||||
|
||||
private Lifecycle bean;
|
||||
|
||||
ShutdownGroupMember(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);
|
||||
return (thisOrder == otherOrder) ? 0 : (thisOrder < otherOrder) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -65,6 +65,8 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
|||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private int shutdownOrder = Integer.MAX_VALUE;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private Connection sharedConnection;
|
||||
|
|
@ -116,6 +118,21 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
|||
return this.autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order in which this container should be stopped.
|
||||
* By default it will be stopped in the last group.
|
||||
*/
|
||||
public void setShutdownOrder(int shutdownOrder) {
|
||||
this.shutdownOrder = shutdownOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order in which this container will be stopped.
|
||||
*/
|
||||
public int getShutdownOrder() {
|
||||
return this.shutdownOrder;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
|
@ -286,6 +303,11 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
|||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
this.stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all invoker tasks and stop the shared Connection, if any.
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
|
|
|
|||
|
|
@ -514,6 +514,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
|||
* @throws JmsException if stopping failed
|
||||
* @see #stop()
|
||||
*/
|
||||
@Override
|
||||
public void stop(Runnable callback) throws JmsException {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.stopCallback = callback;
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ public class GenericMessageEndpointManager implements SmartLifecycle, Initializi
|
|||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private int shutdownOrder = Integer.MAX_VALUE;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
|
@ -226,6 +228,21 @@ public class GenericMessageEndpointManager implements SmartLifecycle, Initializi
|
|||
return this.autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order in which this endpoint manager should be stopped.
|
||||
* By default it will be stopped in the last group.
|
||||
*/
|
||||
public void setShutdownOrder(int shutdownOrder) {
|
||||
this.shutdownOrder = shutdownOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order in which this endpoint manager will be stopped.
|
||||
*/
|
||||
public int getShutdownOrder() {
|
||||
return this.shutdownOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the message endpoint, and automatically activates it
|
||||
* if the "autoStartup" flag is set to "true".
|
||||
|
|
@ -280,6 +297,13 @@ public class GenericMessageEndpointManager implements SmartLifecycle, Initializi
|
|||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.stop();
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the configured message endpoint is currently active.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue