This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-5791-struts-boot in repository https://gitbox.apache.org/repos/asf/struts.git
commit 9363c37ed36adeb9153780da1987e67e72d9d093 Author: Lukasz Lenart <[email protected]> AuthorDate: Fri Oct 23 12:54:45 2026 +0200 WW-5791 fix(boot): serve an empty docBase and capture every filter init failure Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../org/apache/struts2/boot/StrutsBootServer.java | 22 +++++++++++++++------- .../apache/struts2/boot/StrutsBootTomcatTest.java | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/boot/src/main/java/org/apache/struts2/boot/StrutsBootServer.java b/boot/src/main/java/org/apache/struts2/boot/StrutsBootServer.java index 910dd5cc0..4b42a731e 100644 --- a/boot/src/main/java/org/apache/struts2/boot/StrutsBootServer.java +++ b/boot/src/main/java/org/apache/struts2/boot/StrutsBootServer.java @@ -63,7 +63,8 @@ public final class StrutsBootServer implements AutoCloseable { try { tomcat.setBaseDir(baseDir.toString()); tomcat.setPort(settings.port()); - Context context = tomcat.addContext(settings.contextPath(), baseDir.toString()); + Path docBase = Files.createDirectory(baseDir.resolve("webapp")); + Context context = tomcat.addContext(settings.contextPath(), docBase.toString()); context.addServletContainerInitializer( (classes, servletContext) -> servletContext.setAttribute(ActionRegistry.SERVLET_CONTEXT_ATTRIBUTE, registry), null); @@ -77,12 +78,11 @@ public final class StrutsBootServer implements AutoCloseable { ConfigurationException failure = e instanceof ConfigurationException configurationException ? configurationException : new ConfigurationException("Struts Boot failed to start: " + e.getMessage(), e); - try { - server.close(); - } catch (RuntimeException closeFailure) { - failure.addSuppressed(closeFailure); - } + server.closeAfterFailure(failure); throw failure; + } catch (Error e) { + server.closeAfterFailure(e); + throw e; } Runtime.getRuntime().addShutdownHook(server.shutdownHook); return server; @@ -120,6 +120,14 @@ public final class StrutsBootServer implements AutoCloseable { } } + private void closeAfterFailure(Throwable failure) { + try { + close(); + } catch (RuntimeException closeFailure) { + failure.addSuppressed(closeFailure); + } + } + private void assertStarted(Context context, CapturingStrutsFilter filter) { Throwable initFailure = filter.failure.get(); if (initFailure != null || context.getState() != LifecycleState.STARTED) { @@ -197,7 +205,7 @@ public final class StrutsBootServer implements AutoCloseable { public void init(FilterConfig filterConfig) throws ServletException { try { super.init(filterConfig); - } catch (RuntimeException | ServletException e) { + } catch (Throwable e) { failure.set(e); throw e; } diff --git a/boot/src/test/java/org/apache/struts2/boot/StrutsBootTomcatTest.java b/boot/src/test/java/org/apache/struts2/boot/StrutsBootTomcatTest.java index be3f6d829..19ede906f 100644 --- a/boot/src/test/java/org/apache/struts2/boot/StrutsBootTomcatTest.java +++ b/boot/src/test/java/org/apache/struts2/boot/StrutsBootTomcatTest.java @@ -86,6 +86,7 @@ public class StrutsBootTomcatTest { @Test public void testUnregisteredMethodNotInvokable() throws Exception { UserAction.deleteInvoked = false; + assertThat(get(server.baseUrl() + "/admin/users!list.action").statusCode()).isEqualTo(200); HttpResponse<String> response = get(server.baseUrl() + "/admin/users!delete.action"); @@ -99,6 +100,13 @@ public class StrutsBootTomcatTest { assertThat(get(server.baseUrl() + "/hello.action").statusCode()).isEqualTo(200); } + @Test + public void testBaseDirFilesAreNotServed() throws Exception { + Files.writeString(server.baseDir().resolve("base-dir-file.txt"), "not public"); + + assertThat(get(server.baseUrl() + "/base-dir-file.txt").statusCode()).isNotEqualTo(200); + } + @Test public void testConfigurationErrorStopsTomcatAndCleansBaseDir() throws Exception { Set<Path> before = bootTempDirs();
