diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java index 8050c91749..3fbca1f21a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java @@ -31,6 +31,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.stream.Collectors; import javax.servlet.ServletContainerInitializer; @@ -91,6 +92,7 @@ import org.springframework.util.StringUtils; * @author Andy Wilkinson * @author EddĂș MelĂ©ndez * @author Christoffer Sawicki + * @author Dawid Antecki * @since 2.0.0 * @see #setPort(int) * @see #setContextLifecycleListeners(Collection) @@ -813,7 +815,9 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto @Override public Set listWebAppPaths(String path) { - return this.delegate.listWebAppPaths(path); + return this.delegate.listWebAppPaths(path).stream() + .filter((webAppPath) -> !webAppPath.startsWith("/org/springframework/boot")) + .collect(Collectors.toSet()); } @Override diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/com/example/ResourceHandlingApplication.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/com/example/ResourceHandlingApplication.java index 1356b60288..bc40dc94de 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/com/example/ResourceHandlingApplication.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/com/example/ResourceHandlingApplication.java @@ -18,6 +18,8 @@ package com.example; import java.io.IOException; import java.net.URL; +import java.util.LinkedHashSet; +import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -38,31 +40,62 @@ import org.springframework.context.annotation.Bean; @SpringBootApplication public class ResourceHandlingApplication { + @Bean + public ServletRegistrationBean resourceServletRegistration() { + ServletRegistrationBean registration = new ServletRegistrationBean(new GetResourceServlet()); + registration.addUrlMappings("/servletContext"); + return registration; + } + + @Bean + public ServletRegistrationBean resourcePathsServletRegistration() { + ServletRegistrationBean registration = new ServletRegistrationBean( + new GetResourcePathsServlet()); + registration.addUrlMappings("/resourcePaths"); + return registration; + } + public static void main(String[] args) { new SpringApplicationBuilder(ResourceHandlingApplication.class).properties("server.port:0") .listeners(new WebServerPortFileWriter(args[0])).run(args); } - @Bean - public ServletRegistrationBean resourceServletRegistration() { - ServletRegistrationBean registration = new ServletRegistrationBean(new HttpServlet() { + private static final class GetResourcePathsServlet extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - URL resource = getServletContext().getResource(req.getQueryString()); - if (resource == null) { - resp.sendError(404); - } - else { - resp.getWriter().println(resource); - resp.getWriter().flush(); + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + collectResourcePaths("/").forEach(resp.getWriter()::println); + resp.getWriter().flush(); + } + + private Set collectResourcePaths(String path) { + Set allResourcePaths = new LinkedHashSet<>(); + Set pathsForPath = getServletContext().getResourcePaths(path); + if (pathsForPath != null) { + for (String resourcePath : pathsForPath) { + allResourcePaths.add(resourcePath); + allResourcePaths.addAll(collectResourcePaths(resourcePath)); } } + return allResourcePaths; + } + + } + + private static final class GetResourceServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + URL resource = getServletContext().getResource(req.getQueryString()); + if (resource == null) { + resp.sendError(404); + } + else { + resp.getWriter().println(resource); + resp.getWriter().flush(); + } + } - }); - registration.addUrlMappings("/servletContext"); - return registration; } } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java index b1031da5ed..8e062a6aff 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarDevelopmentIntegrationTests.java @@ -16,6 +16,13 @@ package org.springframework.boot.context.embedded; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.junit.jupiter.api.TestTemplate; import org.springframework.http.HttpStatus; @@ -53,4 +60,29 @@ public class EmbeddedServletContainerWarDevelopmentIntegrationTests { assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } + @TestTemplate + public void loaderClassesAreNotAvailableViaResourcePaths(RestTemplate rest) { + ResponseEntity entity = rest.getForEntity("/resourcePaths", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(readLines(entity.getBody())) + .noneMatch((resourcePath) -> resourcePath.startsWith("/org/springframework/boot/loader")); + } + + private List readLines(String input) { + if (input == null) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new StringReader(input))) { + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + return lines; + } + catch (IOException ex) { + throw new RuntimeException("Failed to read lines from input '" + input + "'"); + } + } + } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java index 19e9f91d75..2b2f8cd312 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerWarPackagingIntegrationTests.java @@ -16,6 +16,13 @@ package org.springframework.boot.context.embedded; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.junit.jupiter.api.TestTemplate; import org.springframework.http.HttpStatus; @@ -75,4 +82,29 @@ public class EmbeddedServletContainerWarPackagingIntegrationTests { assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } + @TestTemplate + public void loaderClassesAreNotAvailableViaResourcePaths(RestTemplate rest) { + ResponseEntity entity = rest.getForEntity("/resourcePaths", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(readLines(entity.getBody())) + .noneMatch((resourcePath) -> resourcePath.startsWith("/org/springframework/boot/loader")); + } + + private List readLines(String input) { + if (input == null) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new StringReader(input))) { + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + return lines; + } + catch (IOException ex) { + throw new RuntimeException("Failed to read lines from input '" + input + "'"); + } + } + }