From e53d3167ab839dd809cf5980104907fc889f2c72 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 11 Jul 2016 11:16:34 +0100 Subject: [PATCH] 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 --- .../actuate/endpoint/ShutdownEndpoint.java | 8 +++-- .../endpoint/ShutdownEndpointTests.java | 31 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java index 2b78563cf55..b3009442262 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java @@ -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> @@ -58,7 +59,7 @@ public class ShutdownEndpoint extends AbstractEndpoint> } 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> } ShutdownEndpoint.this.context.close(); } - }).start(); - + }); + thread.setContextClassLoader(getClass().getClassLoader()); + thread.start(); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java index a4de92086c9..b6385ae9417 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java @@ -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 { @@ -53,18 +58,30 @@ public class ShutdownEndpointTests extends AbstractEndpointTests 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() { @Override public void onApplicationEvent(ContextClosedEvent event) { + Config.this.threadContextClassLoader = Thread.currentThread() + .getContextClassLoader(); Config.this.latch.countDown(); } };