Skip to content

Commit 21ebb94

Browse files
committed
Respect Tomcat's failCtxIfServletStartFails flag
Ensure that if the user has set `failCtxIfServletStartFails` to `true` using a `ContextCustomizers` any Servlet init exceptions stop the application from running. Closes gh-14448
1 parent 042d495 commit 21ebb94

File tree

3 files changed

+51
-13
lines changed

3 files changed

+51
-13
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatEmbeddedContext.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,10 +17,12 @@
1717
package org.springframework.boot.web.embedded.tomcat;
1818

1919
import org.apache.catalina.Container;
20+
import org.apache.catalina.LifecycleException;
2021
import org.apache.catalina.Manager;
2122
import org.apache.catalina.core.StandardContext;
2223
import org.apache.catalina.session.ManagerBase;
2324

25+
import org.springframework.util.Assert;
2426
import org.springframework.util.ClassUtils;
2527
import org.springframework.util.ReflectionUtils;
2628

@@ -59,7 +61,7 @@ public void setManager(Manager manager) {
5961
super.setManager(manager);
6062
}
6163

62-
public void deferredLoadOnStartup() {
64+
public void deferredLoadOnStartup() throws LifecycleException {
6365
// Some older Servlet frameworks (e.g. Struts, BIRT) use the Thread context class
6466
// loader to create servlet instances in this phase. If they do that and then try
6567
// to initialize them later the class loader may have changed, so wrap the call to
@@ -70,15 +72,20 @@ public void deferredLoadOnStartup() {
7072
if (classLoader != null) {
7173
existingLoader = ClassUtils.overrideThreadContextClassLoader(classLoader);
7274
}
73-
74-
if (this.overrideLoadOnStart) {
75-
// Earlier versions of Tomcat used a version that returned void. If that
76-
// version is used our overridden loadOnStart method won't have been called
77-
// and the original will have already run.
78-
super.loadOnStartup(findChildren());
75+
try {
76+
if (this.overrideLoadOnStart) {
77+
// Earlier versions of Tomcat used a version that returned void. If that
78+
// version is used our overridden loadOnStart method won't have been
79+
// called and the original will have already run.
80+
boolean started = super.loadOnStartup(findChildren());
81+
Assert.state(started,
82+
"Unable to start embedded tomcat context " + getName());
83+
}
7984
}
80-
if (existingLoader != null) {
81-
ClassUtils.overrideThreadContextClassLoader(existingLoader);
85+
finally {
86+
if (existingLoader != null) {
87+
ClassUtils.overrideThreadContextClassLoader(existingLoader);
88+
}
8289
}
8390
}
8491

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,15 @@ public void start() throws WebServerException {
223223
}
224224

225225
private void checkThatConnectorsHaveStarted() {
226+
checkConnectorHasStarted(this.tomcat.getConnector());
226227
for (Connector connector : this.tomcat.getService().findConnectors()) {
227-
if (LifecycleState.FAILED.equals(connector.getState())) {
228-
throw new ConnectorStartFailedException(connector.getPort());
229-
}
228+
checkConnectorHasStarted(connector);
229+
}
230+
}
231+
232+
private void checkConnectorHasStarted(Connector connector) {
233+
if (LifecycleState.FAILED.equals(connector.getState())) {
234+
throw new ConnectorStartFailedException(connector.getPort());
230235
}
231236
}
232237

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import javax.naming.InitialContext;
3131
import javax.naming.NamingException;
3232
import javax.servlet.ServletException;
33+
import javax.servlet.http.HttpServlet;
3334

3435
import org.apache.catalina.Container;
3536
import org.apache.catalina.Context;
@@ -41,6 +42,7 @@
4142
import org.apache.catalina.Valve;
4243
import org.apache.catalina.connector.Connector;
4344
import org.apache.catalina.core.AprLifecycleListener;
45+
import org.apache.catalina.core.StandardContext;
4446
import org.apache.catalina.core.StandardWrapper;
4547
import org.apache.catalina.startup.Tomcat;
4648
import org.apache.catalina.util.CharsetMapper;
@@ -427,6 +429,21 @@ public void customTomcatHttpOnlyCookie() {
427429
assertThat(context.getUseHttpOnly()).isFalse();
428430
}
429431

432+
@Test
433+
public void exceptionThrownOnLoadFailureWhenFailCtxIfServletStartFailsIsTrue() {
434+
TomcatServletWebServerFactory factory = getFactory();
435+
factory.addContextCustomizers((context) -> {
436+
if (context instanceof StandardContext) {
437+
((StandardContext) context).setFailCtxIfServletStartFails(true);
438+
}
439+
});
440+
this.webServer = factory.getWebServer((context) -> {
441+
context.addServlet("failing", FailingServlet.class).setLoadOnStartup(0);
442+
});
443+
this.thrown.expect(WebServerException.class);
444+
this.webServer.start();
445+
}
446+
430447
@Override
431448
protected JspServlet getJspServlet() throws ServletException {
432449
Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat();
@@ -475,4 +492,13 @@ protected void handleExceptionCausedByBlockedPort(RuntimeException ex,
475492
assertThat(((ConnectorStartFailedException) ex).getPort()).isEqualTo(blockedPort);
476493
}
477494

495+
static class FailingServlet extends HttpServlet {
496+
497+
@Override
498+
public void init() throws ServletException {
499+
throw new RuntimeException("Init Failure");
500+
}
501+
502+
}
503+
478504
}

0 commit comments

Comments
 (0)