Set main thread's context class loader when starting Tomcat
When an app is deployed to Tomcat, all of the application's startup is performed with a WebAppClassLoader being the thread context class loader. When an app is using embedded Tomcat, the WebAppClassLoader is created as part of the application starting but is never set as the thread context class loader. This difference in TCCL can cause problems. For example, it breaks the use of JNDI during application startup with embedded Tomcat. This commit updates the embedded Tomcat servlet container to set the TCCL to be the WebAppClassLoader once the Tomcat context has been started. Once Tomcat is stopped, it sets the TCCL back to the ClassLoader that loaded it. Closes gh-2308
This commit is contained in:
parent
d6e0b5a165
commit
ff99bb0730
|
@ -21,6 +21,7 @@ import java.util.Map;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Engine;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
|
@ -91,6 +92,9 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
|||
// We can re-throw failure exception directly in the main thread
|
||||
rethrowDeferredStartupExceptions();
|
||||
|
||||
ClassLoader classLoader = findContext().getLoader().getClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
|
||||
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
|
||||
// blocking non-daemon to stop immediate shutdown
|
||||
startDaemonAwaitThread();
|
||||
|
@ -101,6 +105,15 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
|||
}
|
||||
}
|
||||
|
||||
private Context findContext() {
|
||||
for (Container child : this.tomcat.getHost().findChildren()) {
|
||||
if (child instanceof Context) {
|
||||
return (Context) child;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("The host does not contain a Context");
|
||||
}
|
||||
|
||||
private void addInstanceIdToEngineName() {
|
||||
int instanceId = containerCounter.incrementAndGet();
|
||||
if (instanceId > 0) {
|
||||
|
@ -245,6 +258,10 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
|||
ex);
|
||||
}
|
||||
finally {
|
||||
if (Thread.currentThread()
|
||||
.getContextClassLoader() instanceof TomcatEmbeddedWebappClassLoader) {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
containerCounter.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.catalina.connector.Connector;
|
|||
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.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
|
@ -72,6 +73,11 @@ public class TomcatEmbeddedServletContainerFactoryTests
|
|||
return new TomcatEmbeddedServletContainerFactory(0);
|
||||
}
|
||||
|
||||
@After
|
||||
public void restoreTccl() {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
// JMX MBean names clash if you get more than one Engine with the same name...
|
||||
@Test
|
||||
public void tomcatEngineNames() throws Exception {
|
||||
|
@ -358,6 +364,17 @@ public class TomcatEmbeddedServletContainerFactoryTests
|
|||
assertThat(s3.split(":")[0]).as(message).isNotEqualTo(s2.split(":")[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tcclOfMainThreadIsTomcatWebAppClassLoader() {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
TomcatEmbeddedServletContainerFactory factory = getFactory();
|
||||
this.container = factory.getEmbeddedServletContainer();
|
||||
this.container.start();
|
||||
assertThat(Thread.currentThread().getContextClassLoader())
|
||||
.isInstanceOf(TomcatEmbeddedWebappClassLoader.class);
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wrapper getJspServlet() {
|
||||
Container context = ((TomcatEmbeddedServletContainer) this.container).getTomcat()
|
||||
|
|
Loading…
Reference in New Issue