Skip to content

Commit 98c11bb

Browse files
wilkinsonaphilwebb
andcommitted
Use consistent current thread context classloader for initialization
Ensure `Thread.currentThread().getContextClassLoader()` returns the same classloader for all types of initialization. Prior to this commit, `JettyEmbeddedWebAppContext` would return a different classloader when initializing Servlet and Filter classes. This was due to the fact that our `deferredInitialize()` method has called outside of a `getContext().call(...)`. Fixes gh-37649 Co-authored-by: Phillip Webb <[email protected]>
1 parent 57f452f commit 98c11bb

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedWebAppContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ protected ServletHandler newServletHandler() {
3838
}
3939

4040
void deferredInitialize() throws Exception {
41-
((JettyEmbeddedServletHandler) getServletHandler()).deferredInitialize();
41+
JettyEmbeddedServletHandler handler = (JettyEmbeddedServletHandler) getServletHandler();
42+
getContext().call(handler::deferredInitialize, null);
4243
}
4344

4445
private static final class JettyEmbeddedServletHandler extends ServletHandler {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.Date;
4141
import java.util.EnumSet;
4242
import java.util.HashMap;
43+
import java.util.HashSet;
4344
import java.util.LinkedHashMap;
4445
import java.util.List;
4546
import java.util.Locale;
@@ -65,6 +66,7 @@
6566
import jakarta.servlet.FilterChain;
6667
import jakarta.servlet.FilterConfig;
6768
import jakarta.servlet.GenericServlet;
69+
import jakarta.servlet.ServletConfig;
6870
import jakarta.servlet.ServletContext;
6971
import jakarta.servlet.ServletContextEvent;
7072
import jakarta.servlet.ServletContextListener;
@@ -1382,6 +1384,26 @@ void startedLogMessageWithMultiplePorts() {
13821384
+ " \\(http(/1.1)?\\), [0-9]+ \\(http(/1.1)?\\)( with context path '(/)?')?");
13831385
}
13841386

1387+
@Test
1388+
void servletComponentsAreInitializedWithTheSameThreadContextClassLoader() {
1389+
AbstractServletWebServerFactory factory = getFactory();
1390+
ThreadContextClassLoaderCapturingServlet servlet = new ThreadContextClassLoaderCapturingServlet();
1391+
ThreadContextClassLoaderCapturingFilter filter = new ThreadContextClassLoaderCapturingFilter();
1392+
ThreadContextClassLoaderCapturingListener listener = new ThreadContextClassLoaderCapturingListener();
1393+
this.webServer = factory.getWebServer((context) -> {
1394+
context.addServlet("tcclCapturingServlet", servlet).setLoadOnStartup(0);
1395+
context.addFilter("tcclCapturingFilter", filter);
1396+
context.addListener(listener);
1397+
});
1398+
this.webServer.start();
1399+
assertThat(servlet.contextClassLoader).isNotNull();
1400+
assertThat(filter.contextClassLoader).isNotNull();
1401+
assertThat(listener.contextClassLoader).isNotNull();
1402+
assertThat(new HashSet<>(
1403+
Arrays.asList(servlet.contextClassLoader, filter.contextClassLoader, listener.contextClassLoader)))
1404+
.hasSize(1);
1405+
}
1406+
13851407
protected Future<Object> initiateGetRequest(int port, String path) {
13861408
return initiateGetRequest(HttpClients.createMinimal(), port, path);
13871409
}
@@ -1455,7 +1477,7 @@ private String setUpFactoryForCompression(int contentSize, String[] mimeTypes, S
14551477
compression.setExcludedUserAgents(excludedUserAgents);
14561478
}
14571479
factory.setCompression(compression);
1458-
factory.addInitializers(new ServletRegistrationBean<HttpServlet>(new HttpServlet() {
1480+
factory.addInitializers(new ServletRegistrationBean<>(new HttpServlet() {
14591481

14601482
@Override
14611483
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
@@ -1833,4 +1855,43 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
18331855

18341856
}
18351857

1858+
static class ThreadContextClassLoaderCapturingServlet extends HttpServlet {
1859+
1860+
private ClassLoader contextClassLoader;
1861+
1862+
@Override
1863+
public void init(ServletConfig config) throws ServletException {
1864+
this.contextClassLoader = Thread.currentThread().getContextClassLoader();
1865+
}
1866+
1867+
}
1868+
1869+
static class ThreadContextClassLoaderCapturingListener implements ServletContextListener {
1870+
1871+
private ClassLoader contextClassLoader;
1872+
1873+
@Override
1874+
public void contextInitialized(ServletContextEvent sce) {
1875+
this.contextClassLoader = Thread.currentThread().getContextClassLoader();
1876+
}
1877+
1878+
}
1879+
1880+
static class ThreadContextClassLoaderCapturingFilter implements Filter {
1881+
1882+
private ClassLoader contextClassLoader;
1883+
1884+
@Override
1885+
public void init(FilterConfig filterConfig) throws ServletException {
1886+
this.contextClassLoader = Thread.currentThread().getContextClassLoader();
1887+
}
1888+
1889+
@Override
1890+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
1891+
throws IOException, ServletException {
1892+
chain.doFilter(request, response);
1893+
}
1894+
1895+
}
1896+
18361897
}

0 commit comments

Comments
 (0)