mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Set TCCL of shutdown thread when triggered by the shutdown endpoint
Previously, the shutdown endpoint would spawn a new thread to perform the shutdown but did not explicitly configure its thread context class loader (TCCL). This mean that the new thread would use the request thread's TCCL as its TCCL. This meant that a different TCCL would be used compared to a shutdown triggered by the shutdown hook and also caused problems with Tomcat's thread leak detection logic. This commit updates the shutdown endpoint to explicitly configure the TCCL of the shutdown thread to be the ClassLoader that loaded the endpoint's class. Closes gh-6361
This commit is contained in:
parent
78879f4bdb
commit
e53d3167ab
@ -30,6 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.shutdown")
|
||||
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
|
||||
@ -58,7 +59,7 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
|
||||
}
|
||||
finally {
|
||||
|
||||
new Thread(new Runnable() {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@ -69,8 +70,9 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
|
||||
}
|
||||
ShutdownEndpoint.this.context.close();
|
||||
}
|
||||
}).start();
|
||||
|
||||
});
|
||||
thread.setContextClassLoader(getClass().getClassLoader());
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2016 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.
|
||||
@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -28,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@ -37,6 +41,7 @@ import static org.junit.Assert.assertTrue;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoint> {
|
||||
|
||||
@ -53,18 +58,30 @@ public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoin
|
||||
|
||||
@Test
|
||||
public void invoke() throws Exception {
|
||||
CountDownLatch latch = this.context.getBean(Config.class).latch;
|
||||
assertThat((String) getEndpointBean().invoke().get("message"),
|
||||
startsWith("Shutting down"));
|
||||
Config config = this.context.getBean(Config.class);
|
||||
ClassLoader previousTccl = Thread.currentThread().getContextClassLoader();
|
||||
Map<String, Object> result;
|
||||
Thread.currentThread().setContextClassLoader(
|
||||
new URLClassLoader(new URL[0], getClass().getClassLoader()));
|
||||
try {
|
||||
result = getEndpointBean().invoke();
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(previousTccl);
|
||||
}
|
||||
assertThat((String) result.get("message"), startsWith("Shutting down"));
|
||||
assertTrue(this.context.isActive());
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
assertTrue(config.latch.await(10, TimeUnit.SECONDS));
|
||||
assertThat(config.threadContextClassLoader, is(getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class Config {
|
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1);
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
private volatile ClassLoader threadContextClassLoader;
|
||||
|
||||
@Bean
|
||||
public ShutdownEndpoint endpoint() {
|
||||
@ -77,6 +94,8 @@ public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoin
|
||||
return new ApplicationListener<ContextClosedEvent>() {
|
||||
@Override
|
||||
public void onApplicationEvent(ContextClosedEvent event) {
|
||||
Config.this.threadContextClassLoader = Thread.currentThread()
|
||||
.getContextClassLoader();
|
||||
Config.this.latch.countDown();
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user