Listen for parent close events and close child context
If the context hierarchy is from a SpringApplication we can control the shutdown semantics a bit. Specifically we need a listener in the child context that will shut it down when the parent closes (since assummably the child relies on beans in the arent that may now be disposed). Fixes gh-275pull/276/merge
parent
ea1a8d0dc0
commit
2d54b54d81
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.boot.actuate.endpoint;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ShutdownEndpoint}.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
public class ShutdownParentEndpointTests {
|
||||||
|
|
||||||
|
private ConfigurableApplicationContext context;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void close() {
|
||||||
|
if (this.context != null) {
|
||||||
|
this.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shutdownChild() throws Exception {
|
||||||
|
this.context = new SpringApplicationBuilder(Config.class).child(Empty.class)
|
||||||
|
.web(false).run();
|
||||||
|
assertThat((String) getEndpointBean().invoke().get("message"),
|
||||||
|
startsWith("Shutting down"));
|
||||||
|
assertTrue(this.context.isActive());
|
||||||
|
Thread.sleep(600);
|
||||||
|
assertFalse(this.context.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shutdownParent() throws Exception {
|
||||||
|
this.context = new SpringApplicationBuilder(Empty.class).child(Config.class)
|
||||||
|
.web(false).run();
|
||||||
|
assertThat((String) getEndpointBean().invoke().get("message"),
|
||||||
|
startsWith("Shutting down"));
|
||||||
|
assertTrue(this.context.isActive());
|
||||||
|
Thread.sleep(600);
|
||||||
|
assertFalse(this.context.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShutdownEndpoint getEndpointBean() {
|
||||||
|
return this.context.getBean(ShutdownEndpoint.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
public static class Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ShutdownEndpoint endpoint() {
|
||||||
|
ShutdownEndpoint endpoint = new ShutdownEndpoint();
|
||||||
|
endpoint.setEnabled(true);
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public static class Empty {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.boot.context.listener;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.initializer.ParentContextApplicationContextInitializer.ParentContextAvailableEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.event.ContextClosedEvent;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener that closes the application context if its parent is closed. It listens for
|
||||||
|
* refresh events and grabs the current context from there, and then listens for closed
|
||||||
|
* events and propagates it down the hierarchy.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
public class ParentContextCloserListener implements
|
||||||
|
ApplicationListener<ParentContextAvailableEvent>, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return Ordered.LOWEST_PRECEDENCE - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ParentContextAvailableEvent event) {
|
||||||
|
maybeInstallListenerInParent(event.getApplicationContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeInstallListenerInParent(ConfigurableApplicationContext child) {
|
||||||
|
if (child.getParent() instanceof ConfigurableApplicationContext) {
|
||||||
|
ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child
|
||||||
|
.getParent();
|
||||||
|
parent.addApplicationListener(new ContextCloserListener(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ContextCloserListener implements
|
||||||
|
ApplicationListener<ContextClosedEvent> {
|
||||||
|
|
||||||
|
private ConfigurableApplicationContext context;
|
||||||
|
|
||||||
|
public ContextCloserListener(ConfigurableApplicationContext context) {
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ContextClosedEvent event) {
|
||||||
|
if (this.context != null
|
||||||
|
&& event.getApplicationContext() == this.context.getParent()
|
||||||
|
&& this.context.isActive()) {
|
||||||
|
this.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue