INT-2485 Orderly Shutdown

Initial commit - @ManagedOperation on IMBE

- can be invoked via JMX, <control-bus/>, or getting a reference to
the IMBE from the application context.

INT-2485 Updates After Review Comments (JIRA)

* Shutdown Schedulers first, and wait for them
* Add a force parameter, which overrides thread pool shutdown options
* Shutdown Sources/Channels after all thread pools have stopped
* Mark other components as OrderlyShutdownCapable (e.g. JMS/AMQP Listener containers) and shut them down first
* Wait for remaining time to allow for quiescence

Also
* remove TimeUnit parameter (not JMX-friendly); time limit is now always milliseconds
* If thread pools don't stop in time limit, force them down.

INT-2485 Handle Self-Destruction

Add shutdown-executor to IMBE.

When the shutdown was called on a Spring-Managed thread, the shutdown
was not clean because we timed out waiting for the current thread to
terminate. After that, we force terminated other components.

Now, by providing a dedicated Executor for the shutdown process, it
is used for the shutdown instead of the current thread. This Executor
is *not* shutdown.

It is not necessary to provide an Executor if the stopActiveComponents()
method is called on some other thread that is not involved in the
shutdown.

Also adds executor name to logs, when available.

INT-2485 Polishing

Fix MBean object name collision when running all tests.

INT-2485 Enable TCP Shutdown

Make TCP connection factories 'OrderlyShutdownCapable' so
they are stopped before schedulers/executors in order for
them to release any executor threads they are holding.

INT-2485 Polishing

Didn't need DirectFieldAccessor - scheduler and executor have
an accessor for the native ExecutorService.

Copyrights

INT-2485 Polishing

schemaLocation version.

INT-2485 PR Review Polishing
This commit is contained in:
Gary Russell
2012-04-02 18:01:35 -04:00
committed by Oleg Zhurakousky
parent 77e3886a12
commit 0a12a496cc
11 changed files with 672 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009-2010 the original author or authors.
* Copyright 2009-2012 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
@@ -13,8 +13,10 @@
package org.springframework.integration.monitor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Date;
@@ -28,10 +30,14 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.Lifecycle;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.OrderlyShutdownCapable;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
@@ -107,6 +113,28 @@ public class MBeanExporterIntegrationTests {
}
// Lifecycle method name
assertEquals("start", startName);
assertTrue((Boolean) server.invoke(names.iterator().next(), "isRunning", null, null));
messageChannelsMonitor.stopActiveComponents(false, 3000);
assertFalse((Boolean) server.invoke(names.iterator().next(), "isRunning", null, null));
ActiveChannel activeChannel = context.getBean("activeChannel", ActiveChannel.class);
assertTrue(activeChannel.isStopCalled());
OtherActiveComponent otherActiveComponent = context.getBean(OtherActiveComponent.class);
assertTrue(otherActiveComponent.isStopCalled());
}
@Test
public void testSelfDestruction() throws Exception {
context = new GenericXmlApplicationContext(getClass(), "self-destruction-context.xml");
SourcePollingChannelAdapter adapter = context.getBean(SourcePollingChannelAdapter.class);
adapter.start();
int n = 0;
while (adapter.isRunning()) {
n += 10;
if (n > 10000) {
fail("Adapter failed to stop");
}
Thread.sleep(10);
}
}
@Test
@@ -276,4 +304,55 @@ public class MBeanExporterIntegrationTests {
}
}
public static interface ActiveChannel {
boolean isStopCalled();
}
public static class ActiveChannelImpl implements MessageChannel, Lifecycle, ActiveChannel {
private boolean stopCalled;
public boolean send(Message<?> message) {
return false;
}
public boolean send(Message<?> message, long timeout) {
return false;
}
public void start() {
}
public void stop() {
this.stopCalled = true;
}
public boolean isRunning() {
return false;
}
public boolean isStopCalled() {
return this.stopCalled;
}
}
public static class OtherActiveComponent implements OrderlyShutdownCapable, Lifecycle {
private boolean stopCalled;
public void start() {
}
public void stop() {
this.stopCalled = true;
}
public boolean isRunning() {
return false;
}
public boolean isStopCalled() {
return this.stopCalled;
}
}
}

View File

@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:int="http://www.springframework.org/schema/integration"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
@@ -17,4 +21,18 @@
<bean id="service" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$SimpleService" />
<bean id="activeChannel" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$ActiveChannelImpl" />
<task:scheduler id="someScheduler" />
<task:executor id="someExecutor" />
<bean id="nonSpringExecutor" class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor" />
<bean id="otherActiveComponent" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$OtherActiveComponent" />
<bean id="ignoreWrappedExecutor" class="org.springframework.core.task.support.ExecutorServiceAdapter">
<constructor-arg ref="someExecutor" />
</bean>
</beans>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-jmx="http://www.springframework.org/schema/integration/jmx"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/integration/jmx http://www.springframework.org/schema/integration/jmx/spring-integration-jmx.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<int:inbound-channel-adapter channel="toControlBus" auto-startup="false"
expression="@integrationMbeanExporter.stopActiveComponents(false, 1000)" />
<int:channel id="toControlBus" />
<int:control-bus input-channel="toControlBus" />
<int-jmx:mbean-export id="integrationMbeanExporter" shutdown-executor="shutdownExec"
default-domain="self-destruct" />
<context:mbean-server />
<int:poller default="true" fixed-delay="500" />
<task:executor id="shutdownExec" />
<bean id="activeChannel" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$ActiveChannelImpl" />
<task:scheduler id="someScheduler" />
<task:executor id="someExecutor" />
<bean id="nonSpringExecutor" class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor" />
<bean id="otherActiveComponent" class="org.springframework.integration.monitor.MBeanExporterIntegrationTests$OtherActiveComponent" />
</beans>