Ensure that Tomcat does not report unstopped main thread when startup fails
Following the changes made in ff99bb07, Tomcat’s WebAppClassLoader is
set as the thread context class loader of the main thread. If Tomcat
was stopped while the main thread was still running (typically as a
result of a startup failure), this had the unwanted side-effect of
causing Tomcat to report that the application had started a thread named
main and had failed to stop it.
This commit updates TomcatEmbeddedServletContainer so that, during stop
processing, the current thread’s context class loader is reset before
Tomcat is stopped. This prevents Tomcat from incorrectly believe that
the application has started and failed to stop the main thread.
Closes gh-5357
This commit is contained in:
@@ -155,6 +155,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||
}
|
||||
|
||||
};
|
||||
awaitThread.setContextClassLoader(getClass().getClassLoader());
|
||||
awaitThread.setDaemon(false);
|
||||
awaitThread.start();
|
||||
}
|
||||
@@ -191,13 +192,21 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||
|
||||
private void stopSilently() {
|
||||
try {
|
||||
this.tomcat.stop();
|
||||
stopTomcat();
|
||||
}
|
||||
catch (LifecycleException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void stopTomcat() throws LifecycleException {
|
||||
if (Thread.currentThread()
|
||||
.getContextClassLoader() instanceof TomcatEmbeddedWebappClassLoader) {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
this.tomcat.stop();
|
||||
}
|
||||
|
||||
private void addPreviouslyRemovedConnectors() {
|
||||
Service[] services = this.tomcat.getServer().findServices();
|
||||
for (Service service : services) {
|
||||
@@ -246,7 +255,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||
public synchronized void stop() throws EmbeddedServletContainerException {
|
||||
try {
|
||||
try {
|
||||
this.tomcat.stop();
|
||||
stopTomcat();
|
||||
this.tomcat.destroy();
|
||||
}
|
||||
catch (LifecycleException ex) {
|
||||
@@ -258,10 +267,6 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||
ex);
|
||||
}
|
||||
finally {
|
||||
if (Thread.currentThread()
|
||||
.getContextClassLoader() instanceof TomcatEmbeddedWebappClassLoader) {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
containerCounter.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
@@ -44,6 +45,7 @@ import org.springframework.boot.context.embedded.AbstractEmbeddedServletContaine
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
|
||||
import org.springframework.boot.context.embedded.Ssl;
|
||||
import org.springframework.boot.testutil.OutputCapture;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
@@ -67,6 +69,9 @@ import static org.mockito.Mockito.verify;
|
||||
public class TomcatEmbeddedServletContainerFactoryTests
|
||||
extends AbstractEmbeddedServletContainerFactoryTests {
|
||||
|
||||
@Rule
|
||||
public OutputCapture outputCapture = new OutputCapture();
|
||||
|
||||
@Override
|
||||
protected TomcatEmbeddedServletContainerFactory getFactory() {
|
||||
return new TomcatEmbeddedServletContainerFactory(0);
|
||||
@@ -335,6 +340,16 @@ public class TomcatEmbeddedServletContainerFactoryTests
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupFailureDoesNotResultInUnstoppedThreadsBeingReported()
|
||||
throws IOException {
|
||||
super.portClashOfPrimaryConnectorResultsInPortInUseException();
|
||||
|
||||
String string = this.outputCapture.toString();
|
||||
assertThat(string)
|
||||
.doesNotContain("appears to have started a thread named [main]");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConnector(int port,
|
||||
AbstractEmbeddedServletContainerFactory factory) {
|
||||
|
||||
Reference in New Issue
Block a user