diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration.java index 9c97916609..2eb70eba71 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration.java @@ -16,9 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web; -import java.util.Set; -import java.util.stream.Collectors; - import org.glassfish.jersey.server.ResourceConfig; import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter; @@ -30,11 +27,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; import org.springframework.web.servlet.DispatcherServlet; /** @@ -72,27 +68,13 @@ public class ServletEndpointManagementContextConfiguration { public ServletEndpointRegistrar servletEndpointRegistrar( WebEndpointProperties properties, ServletEndpointsSupplier servletEndpointsSupplier) { - DispatcherServletPathProvider servletPathProvider = this.context - .getBean(DispatcherServletPathProvider.class); - Set cleanedPaths = getServletPaths(properties, servletPathProvider); - return new ServletEndpointRegistrar(cleanedPaths, + DispatcherServletPath dispatcherServletPath = this.context + .getBean(DispatcherServletPath.class); + return new ServletEndpointRegistrar( + dispatcherServletPath.getRelativePath(properties.getBasePath()), servletEndpointsSupplier.getEndpoints()); } - private Set getServletPaths(WebEndpointProperties properties, - DispatcherServletPathProvider servletPathProvider) { - return servletPathProvider.getServletPaths().stream() - .map((p) -> cleanServletPath(p) + properties.getBasePath()) - .collect(Collectors.toSet()); - } - - private String cleanServletPath(String servletPath) { - if (StringUtils.hasText(servletPath) && servletPath.endsWith("/")) { - return servletPath.substring(0, servletPath.length() - 1); - } - return servletPath; - } - } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index b576b440df..9528a9404c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -33,7 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -137,23 +137,20 @@ public final class EndpointRequest { private RequestMatcher createDelegate(WebApplicationContext context) { try { - Set servletPaths = getServletPaths(context); - RequestMatcherFactory requestMatcherFactory = new RequestMatcherFactory( - servletPaths); - return createDelegate(context, requestMatcherFactory); + String pathPrefix = getPathPrefix(context); + return createDelegate(context, new RequestMatcherFactory(pathPrefix)); } catch (NoSuchBeanDefinitionException ex) { return EMPTY_MATCHER; } } - private Set getServletPaths(WebApplicationContext context) { + private String getPathPrefix(WebApplicationContext context) { try { - return context.getBean(DispatcherServletPathProvider.class) - .getServletPaths(); + return context.getBean(DispatcherServletPath.class).getPrefix(); } catch (NoSuchBeanDefinitionException ex) { - return Collections.singleton(""); + return ""; } } @@ -225,7 +222,7 @@ public final class EndpointRequest { requestMatcherFactory, paths); if (this.includeLinks && StringUtils.hasText(pathMappedEndpoints.getBasePath())) { - delegateMatchers.addAll( + delegateMatchers.add( requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath())); } return new OrRequestMatcher(delegateMatchers); @@ -258,8 +255,7 @@ public final class EndpointRequest { private List getDelegateMatchers( RequestMatcherFactory requestMatcherFactory, Set paths) { return paths.stream() - .flatMap( - (path) -> requestMatcherFactory.antPath(path, "/**").stream()) + .map((path) -> requestMatcherFactory.antPath(path, "/**")) .collect(Collectors.toList()); } @@ -276,9 +272,7 @@ public final class EndpointRequest { WebEndpointProperties properties = context .getBean(WebEndpointProperties.class); if (StringUtils.hasText(properties.getBasePath())) { - List matchers = requestMatcherFactory - .antPath(properties.getBasePath()); - return new OrRequestMatcher(matchers); + return requestMatcherFactory.antPath(properties.getBasePath()); } return EMPTY_MATCHER; } @@ -290,19 +284,18 @@ public final class EndpointRequest { */ private static class RequestMatcherFactory { - private final Set servletPaths = new LinkedHashSet<>(); + private final String prefix; - RequestMatcherFactory(Set servletPaths) { - this.servletPaths.addAll(servletPaths); + RequestMatcherFactory(String prefix) { + this.prefix = prefix; } - List antPath(String... parts) { - return this.servletPaths.stream() - .map((p) -> (StringUtils.hasText(p) && !p.equals("/") ? p : "")) - .distinct() - .map((path) -> Arrays.stream(parts) - .collect(Collectors.joining("", path, ""))) - .map(AntPathRequestMatcher::new).collect(Collectors.toList()); + public RequestMatcher antPath(String... parts) { + String pattern = this.prefix; + for (String part : parts) { + pattern += part; + } + return new AntPathRequestMatcher(pattern); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java index 52479598d7..568a12ff92 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet; -import java.util.Collections; - import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; @@ -27,7 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.context.annotation.Bean; @@ -72,6 +70,12 @@ class WebMvcEndpointChildContextConfiguration { return dispatcherServlet; } + @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) + public DispatcherServletRegistrationBean dispatcherServletRegistrationBean( + DispatcherServlet dispatcherServlet) { + return new DispatcherServletRegistrationBean(dispatcherServlet, "/"); + } + @Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME) public CompositeHandlerMapping compositeHandlerMapping() { return new CompositeHandlerMapping(); @@ -95,9 +99,4 @@ class WebMvcEndpointChildContextConfiguration { return new OrderedRequestContextFilter(); } - @Bean - public DispatcherServletPathProvider childDispatcherServletPathProvider() { - return () -> Collections.singleton(""); - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfigurationTests.java index 70cd7db506..51c76f18ad 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfigurationTests.java @@ -17,15 +17,13 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web; import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; import org.glassfish.jersey.server.ResourceConfig; import org.junit.Test; import org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -50,22 +48,18 @@ public class ServletEndpointManagementContextConfigurationTests { .withUserConfiguration(TestConfig.class); @Test - @SuppressWarnings("unchecked") public void contextShouldContainServletEndpointRegistrar() { FilteredClassLoader classLoader = new FilteredClassLoader(ResourceConfig.class); this.contextRunner.withClassLoader(classLoader).run((context) -> { assertThat(context).hasSingleBean(ServletEndpointRegistrar.class); ServletEndpointRegistrar bean = context .getBean(ServletEndpointRegistrar.class); - Set basePaths = (Set) ReflectionTestUtils.getField(bean, - "basePaths"); - assertThat(basePaths).containsExactlyInAnyOrder("/test/actuator", "/actuator", - "/foo/actuator"); + String basePath = (String) ReflectionTestUtils.getField(bean, "basePath"); + assertThat(basePath).isEqualTo("/test/actuator"); }); } @Test - @SuppressWarnings("unchecked") public void servletPathShouldNotAffectJerseyConfiguration() { FilteredClassLoader classLoader = new FilteredClassLoader( DispatcherServlet.class); @@ -73,9 +67,8 @@ public class ServletEndpointManagementContextConfigurationTests { assertThat(context).hasSingleBean(ServletEndpointRegistrar.class); ServletEndpointRegistrar bean = context .getBean(ServletEndpointRegistrar.class); - Set basePaths = (Set) ReflectionTestUtils.getField(bean, - "basePaths"); - assertThat(basePaths).containsExactly("/actuator"); + String basePath = (String) ReflectionTestUtils.getField(bean, "basePath"); + assertThat(basePath).isEqualTo("/actuator"); }); } @@ -97,14 +90,8 @@ public class ServletEndpointManagementContextConfigurationTests { } @Bean - public DispatcherServletPathProvider servletPathProvider() { - return () -> { - Set paths = new LinkedHashSet<>(); - paths.add("/"); - paths.add("/test"); - paths.add("/foo/"); - return paths; - }; + public DispatcherServletPath dispatcherServletPath() { + return () -> "/test"; } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 20df4c8c57..d2682e30ea 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -17,8 +17,6 @@ package org.springframework.boot.actuate.autoconfigure.security.servlet; import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -33,7 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -78,12 +76,11 @@ public class EndpointRequestTests { @Test public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", "/spring", "/admin") - .matches(Arrays.asList("/spring", "/admin"), "/actuator/foo"); - assertMatcher(matcher, "/actuator", "/spring", "/admin") - .matches(Arrays.asList("/spring", "/admin"), "/actuator/bar"); - assertMatcher(matcher, "/actuator", "/spring").matches(Arrays.asList("/spring"), - "/actuator"); + assertMatcher(matcher, "/actuator", "/spring").matches("/spring", + "/actuator/foo"); + assertMatcher(matcher, "/actuator", "/spring").matches("/spring", + "/actuator/bar"); + assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator"); assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring", "/actuator/baz"); assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo"); @@ -92,10 +89,10 @@ public class EndpointRequestTests { @Test public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/foo"); - assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/bar"); - assertMatcher(matcher, "/actuator", (String) null).matches("/actuator"); - assertMatcher(matcher, "/actuator", (String) null).doesNotMatch("/actuator/baz"); + assertMatcher(matcher, "/actuator", null).matches("/actuator/foo"); + assertMatcher(matcher, "/actuator", null).matches("/actuator/bar"); + assertMatcher(matcher, "/actuator", null).matches("/actuator"); + assertMatcher(matcher, "/actuator", null).doesNotMatch("/actuator/baz"); } @Test @@ -222,8 +219,8 @@ public class EndpointRequestTests { } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, - String... servletPaths) { - return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPaths); + String servletPath) { + return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath); } private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { @@ -246,7 +243,7 @@ public class EndpointRequestTests { } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, - PathMappedEndpoints pathMappedEndpoints, String... servletPaths) { + PathMappedEndpoints pathMappedEndpoints, String dispatcherServletPath) { StaticWebApplicationContext context = new StaticWebApplicationContext(); context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { @@ -257,10 +254,9 @@ public class EndpointRequestTests { properties.setBasePath(pathMappedEndpoints.getBasePath()); } } - if (servletPaths != null) { - DispatcherServletPathProvider pathProvider = () -> new LinkedHashSet<>( - Arrays.asList(servletPaths)); - context.registerBean(DispatcherServletPathProvider.class, () -> pathProvider); + if (dispatcherServletPath != null) { + DispatcherServletPath path = () -> dispatcherServletPath; + context.registerBean(DispatcherServletPath.class, () -> path); } return assertThat(new RequestMatcherAssert(context, matcher)); } @@ -280,8 +276,8 @@ public class EndpointRequestTests { matches(mockRequest(servletPath)); } - public void matches(List servletPaths, String pathInfo) { - servletPaths.forEach((p) -> matches(mockRequest(p, pathInfo))); + public void matches(String servletPath, String pathInfo) { + matches(mockRequest(servletPath, pathInfo)); } private void matches(HttpServletRequest request) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationTests.java index 61e5c59934..20ebfcdebd 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationTests.java @@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet; import org.junit.Test; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.context.annotation.Bean; @@ -64,12 +64,12 @@ public class WebMvcEndpointChildContextConfigurationTests { } @Test - public void contextShouldConfigureDispatcherServletPathProviderWithEmptyPath() { + public void contextShouldConfigureDispatcherServletPathWithRootPath() { this.contextRunner .withUserConfiguration(WebMvcEndpointChildContextConfiguration.class) - .run((context) -> assertThat(context - .getBean(DispatcherServletPathProvider.class).getServletPaths()) - .containsExactly("")); + .run((context) -> assertThat( + context.getBean(DispatcherServletPath.class).getPath()) + .isEqualTo("/")); } static class ExistingConfig { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrar.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrar.java index 1a45fa1db9..26f761962b 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrar.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrar.java @@ -16,10 +16,7 @@ package org.springframework.boot.actuate.endpoint.web; -import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -30,7 +27,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; /** * {@link ServletContextInitializer} to register {@link ExposableServletEndpoint servlet @@ -44,24 +40,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer { private static final Log logger = LogFactory.getLog(ServletEndpointRegistrar.class); - private final Set basePaths = new LinkedHashSet<>(); + private final String basePath; private final Collection servletEndpoints; public ServletEndpointRegistrar(String basePath, Collection servletEndpoints) { Assert.notNull(servletEndpoints, "ServletEndpoints must not be null"); - this.basePaths.add((basePath != null ? basePath : "")); - this.servletEndpoints = servletEndpoints; - } - - public ServletEndpointRegistrar(Set basePaths, - Collection servletEndpoints) { - Assert.notNull(servletEndpoints, "ServletEndpoints must not be null"); - this.basePaths.addAll(basePaths); - if (CollectionUtils.isEmpty(this.basePaths)) { - this.basePaths.add(""); - } + this.basePath = (basePath != null ? basePath : ""); this.servletEndpoints = servletEndpoints; } @@ -74,24 +60,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer { private void register(ServletContext servletContext, ExposableServletEndpoint endpoint) { String name = endpoint.getId() + "-actuator-endpoint"; + String path = this.basePath + "/" + endpoint.getRootPath(); + String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*"); EndpointServlet endpointServlet = endpoint.getEndpointServlet(); Dynamic registration = servletContext.addServlet(name, endpointServlet.getServlet()); - String[] urlMappings = getUrlMappings(endpoint.getRootPath()); - registration.addMapping(urlMappings); - if (logger.isInfoEnabled()) { - Arrays.stream(urlMappings).forEach( - (mapping) -> logger.info("Registered '" + mapping + "' to " + name)); - } + registration.addMapping(urlMapping); registration.setInitParameters(endpointServlet.getInitParameters()); - } - - private String[] getUrlMappings(String endpointPath) { - return this.basePaths.stream() - .map((basePath) -> (basePath != null ? basePath + "/" + endpointPath - : "/" + endpointPath)) - .distinct().map((path) -> (path.endsWith("/") ? path + "*" : path + "/*")) - .toArray(String[]::new); + logger.info("Registered '" + path + "' to " + name); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrarTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrarTests.java index 9221133b6c..542f435bc2 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrarTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrarTests.java @@ -18,8 +18,6 @@ package org.springframework.boot.actuate.endpoint.web; import java.io.IOException; import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; import javax.servlet.GenericServlet; import javax.servlet.Servlet; @@ -49,7 +47,6 @@ import static org.mockito.Mockito.verify; * Tests for {@link ServletEndpointRegistrar}. * * @author Phillip Webb - * @author Madhura Bhave */ public class ServletEndpointRegistrarTests { @@ -76,14 +73,14 @@ public class ServletEndpointRegistrarTests { public void createWhenServletEndpointsIsNullShouldThrowException() { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("ServletEndpoints must not be null"); - new ServletEndpointRegistrar((String) null, null); + new ServletEndpointRegistrar(null, null); } @Test public void onStartupShouldRegisterServlets() throws Exception { ExposableServletEndpoint endpoint = mockEndpoint( new EndpointServlet(TestServlet.class)); - ServletEndpointRegistrar registrar = new ServletEndpointRegistrar((String) null, + ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(null, Collections.singleton(endpoint)); registrar.onStartup(this.servletContext); verify(this.servletContext).addServlet(eq("test-actuator-endpoint"), @@ -105,64 +102,6 @@ public class ServletEndpointRegistrarTests { verify(this.dynamic).addMapping("/actuator/test/*"); } - @Test - public void onStartupWhenHasMultipleBasePathsShouldIncludeAllBasePaths() - throws Exception { - ExposableServletEndpoint endpoint = mockEndpoint( - new EndpointServlet(TestServlet.class)); - Set basePaths = new LinkedHashSet<>(); - basePaths.add("/actuator"); - basePaths.add("/admin"); - basePaths.add("/application"); - ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(basePaths, - Collections.singleton(endpoint)); - registrar.onStartup(this.servletContext); - verify(this.servletContext).addServlet(eq("test-actuator-endpoint"), - this.servlet.capture()); - assertThat(this.servlet.getValue()).isInstanceOf(TestServlet.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(this.dynamic).addMapping(captor.capture()); - assertThat(captor.getAllValues()).containsExactlyInAnyOrder("/application/test/*", - "/admin/test/*", "/actuator/test/*"); - } - - @Test - public void onStartupWhenHasEmptyBasePathsShouldIncludeRoot() throws Exception { - ExposableServletEndpoint endpoint = mockEndpoint( - new EndpointServlet(TestServlet.class)); - Set basePaths = Collections.emptySet(); - ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(basePaths, - Collections.singleton(endpoint)); - registrar.onStartup(this.servletContext); - verify(this.dynamic).addMapping("/test/*"); - } - - @Test - public void onStartupWhenHasBasePathsHasNullValueShouldIncludeRoot() - throws Exception { - ExposableServletEndpoint endpoint = mockEndpoint( - new EndpointServlet(TestServlet.class)); - Set basePaths = new LinkedHashSet<>(); - basePaths.add(null); - ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(basePaths, - Collections.singleton(endpoint)); - registrar.onStartup(this.servletContext); - verify(this.dynamic).addMapping("/test/*"); - } - - @Test - public void onStartupWhenDuplicateValuesShouldIncludeDistinct() throws Exception { - ExposableServletEndpoint endpoint = mockEndpoint( - new EndpointServlet(TestServlet.class)); - Set basePaths = new LinkedHashSet<>(); - basePaths.add(""); - basePaths.add(null); - ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(basePaths, - Collections.singleton(endpoint)); - registrar.onStartup(this.servletContext); - verify(this.dynamic).addMapping("/test/*"); - } - @Test public void onStartupWhenHasInitParametersShouldRegisterInitParameters() throws Exception { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequest.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequest.java index 7e3d3d3f2d..8d473fcbc6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequest.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequest.java @@ -27,7 +27,7 @@ import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import org.springframework.boot.autoconfigure.security.StaticResourceLocation; -import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -96,14 +96,14 @@ public final class StaticResourceRequest { * Locations}. */ public static final class StaticResourceRequestMatcher - extends ApplicationContextRequestMatcher { + extends ApplicationContextRequestMatcher { private final Set locations; private volatile RequestMatcher delegate; private StaticResourceRequestMatcher(Set locations) { - super(ServerProperties.class); + super(DispatcherServletPath.class); this.locations = locations; } @@ -134,25 +134,26 @@ public final class StaticResourceRequest { } @Override - protected void initialized(Supplier serverProperties) { + protected void initialized( + Supplier dispatcherServletPath) { this.delegate = new OrRequestMatcher( - getDelegateMatchers(serverProperties.get())); + getDelegateMatchers(dispatcherServletPath.get())); } private List getDelegateMatchers( - ServerProperties serverProperties) { - return getPatterns(serverProperties).map(AntPathRequestMatcher::new) + DispatcherServletPath dispatcherServletPath) { + return getPatterns(dispatcherServletPath).map(AntPathRequestMatcher::new) .collect(Collectors.toList()); } - private Stream getPatterns(ServerProperties serverProperties) { + private Stream getPatterns(DispatcherServletPath dispatcherServletPath) { return this.locations.stream().flatMap(StaticResourceLocation::getPatterns) - .map(serverProperties.getServlet()::getPath); + .map(dispatcherServletPath::getRelativePath); } @Override protected boolean matches(HttpServletRequest request, - Supplier context) { + Supplier context) { return this.delegate.matches(request); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 2ef354cfa6..becb1d2eea 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -269,6 +269,13 @@ public class ServerProperties { return this.session; } + /** + * Return the mapping used to map a servlet to the path. + * @return the servlet mapping + * @deprecated since 2.0.4 in favor of + * {@link org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath#getServletUrlMapping} + */ + @Deprecated public String getServletMapping() { if (this.path.equals("") || this.path.equals("/")) { return "/"; @@ -282,6 +289,14 @@ public class ServerProperties { return this.path + "/*"; } + /** + * Return a path relative to the servlet prefix. + * @param path the path to make relative + * @return the relative path + * @deprecated since 2.0.4 in favor of + * {@link org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath#getRelativePath(String)} + */ + @Deprecated public String getPath(String path) { String prefix = getServletPrefix(); if (!path.startsWith("/")) { @@ -290,6 +305,13 @@ public class ServerProperties { return prefix + path; } + /** + * Return the servlet prefix. + * @return the servlet prefix + * @deprecated since 2.0.4 in favor of + * {@link org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath#getPrefix()} + */ + @Deprecated public String getServletPrefix() { String result = this.path; int index = result.indexOf('*'); @@ -302,6 +324,14 @@ public class ServerProperties { return result; } + /** + * Create a array of relative paths from the given source. + * @param paths the source paths + * @return the relative paths + * @deprecated since 2.0.4 in favor of + * {@link org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath#getRelativePath(String)} + */ + @Deprecated public String[] getPathsArray(Collection paths) { String[] result = new String[paths.size()]; int i = 0; @@ -311,6 +341,14 @@ public class ServerProperties { return result; } + /** + * Create a array of relative paths from the given source. + * @param paths the source paths + * @return the relative paths + * @deprecated since 2.0.4 in favor of + * {@link org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath#getRelativePath(String)} + */ + @Deprecated public String[] getPathsArray(String[] paths) { String[] result = new String[paths.length]; int i = 0; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java index 321c6d311d..88c878cd12 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java @@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.web.servlet; import java.util.Arrays; -import java.util.Collections; import java.util.List; import javax.servlet.MultipartConfigElement; @@ -34,7 +33,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; @@ -139,11 +137,10 @@ public class DispatcherServletAutoConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) - public ServletRegistrationBean dispatcherServletRegistration( + public DispatcherServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { - ServletRegistrationBean registration = new ServletRegistrationBean<>( - dispatcherServlet, - this.serverProperties.getServlet().getServletMapping()); + DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean( + dispatcherServlet, this.serverProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); @@ -153,15 +150,6 @@ public class DispatcherServletAutoConfiguration { return registration; } - @Bean - @ConditionalOnMissingBean(DispatcherServletPathProvider.class) - @ConditionalOnSingleCandidate(DispatcherServlet.class) - public DispatcherServletPathProvider dispatcherServletPathProvider() { - return () -> Collections.singleton( - DispatcherServletRegistrationConfiguration.this.serverProperties - .getServlet().getPath()); - } - } @Order(Ordered.LOWEST_PRECEDENCE - 10) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPath.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPath.java new file mode 100644 index 0000000000..aede028d0b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPath.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web.servlet; + +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * Interface that can be used by auto-configurations that need path details for the + * {@link DispatcherServletAutoConfiguration#DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME + * default} {@link DispatcherServlet}. + * + * @author Madhura Bhave + * @author Stephane Nicoll + * @since 2.0.4 + */ +@FunctionalInterface +public interface DispatcherServletPath { + + /** + * Returns the configured path of the dispatcher servlet. + * @return the configured path + */ + String getPath(); + + /** + * Return a form of the given path that's relative to the dispatcher servlet path. + * @param path the path to make relative + * @return the relative path + */ + default String getRelativePath(String path) { + String prefix = getPrefix(); + if (!path.startsWith("/")) { + path = "/" + path; + } + return prefix + path; + } + + /** + * Return a cleaned up version of the path that can be used as a prefix for URLs. The + * resulting path will have path will not have a trailing slash. + * @return the prefix + * @see #getRelativePath(String) + */ + default String getPrefix() { + String result = getPath(); + int index = result.indexOf('*'); + if (index != -1) { + result = result.substring(0, index); + } + if (result.endsWith("/")) { + result = result.substring(0, result.length() - 1); + } + return result; + } + + /** + * Return a URL mapping pattern that can be used with a + * {@link ServletRegistrationBean} to map the dispatcher servlet. + * @return the path as a servlet URL mapping + */ + default String getServletUrlMapping() { + if (getPath().equals("") || getPath().equals("/")) { + return "/"; + } + if (getPath().contains("*")) { + return getPath(); + } + if (getPath().endsWith("/")) { + return getPath() + "*"; + } + return getPath() + "/*"; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathProvider.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathProvider.java index d0cb308d6b..76b6fa97bb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathProvider.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathProvider.java @@ -16,8 +16,6 @@ package org.springframework.boot.autoconfigure.web.servlet; -import java.util.Set; - import org.springframework.web.servlet.DispatcherServlet; /** @@ -26,10 +24,13 @@ import org.springframework.web.servlet.DispatcherServlet; * * @author Madhura Bhave * @since 2.0.2 + * @deprecated since 2.0.4 in favor of {@link DispatcherServletPath} and + * {@link DispatcherServletRegistrationBean} */ +@Deprecated @FunctionalInterface public interface DispatcherServletPathProvider { - Set getServletPaths(); + String getServletPath(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBean.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBean.java new file mode 100644 index 0000000000..541f899b83 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBean.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web.servlet; + +import java.util.Collection; + +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.util.Assert; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * {@link ServletRegistrationBean} for the auto-configured {@link DispatcherServlet}. Both + * registeres the servlet and exposes {@link DispatcherServletPath} information. + * + * @author Phillip Webb + * @since 2.0.4 + */ +public class DispatcherServletRegistrationBean extends + ServletRegistrationBean implements DispatcherServletPath { + + private final String path; + + /** + * Create a new {@link DispatcherServletRegistrationBean} instance for the given + * servlet and path. + * @param servlet the dispatcher servlet + * @param path the dispatcher servlet path + */ + public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) { + super(servlet); + Assert.notNull(path, "Path must not be null"); + this.path = path; + super.addUrlMappings(getServletUrlMapping()); + } + + @Override + public String getPath() { + return this.path; + } + + @Override + public void setUrlMappings(Collection urlMappings) { + throw new UnsupportedOperationException( + "URL Mapping cannot be changed on a DispatcherServlet registration"); + } + + @Override + public void addUrlMappings(String... urlMappings) { + throw new UnsupportedOperationException( + "URL Mapping cannot be changed on a DispatcherServlet registration"); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.java index 25c81e2e9f..25431fd514 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.java @@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.server.ErrorPage; @@ -94,11 +95,15 @@ public class ErrorMvcAutoConfiguration { private final ServerProperties serverProperties; + private final DispatcherServletPath dispatcherServletPath; + private final List errorViewResolvers; public ErrorMvcAutoConfiguration(ServerProperties serverProperties, + DispatcherServletPath dispatcherServletPath, ObjectProvider> errorViewResolversProvider) { this.serverProperties = serverProperties; + this.dispatcherServletPath = dispatcherServletPath; this.errorViewResolvers = errorViewResolversProvider.getIfAvailable(); } @@ -118,7 +123,7 @@ public class ErrorMvcAutoConfiguration { @Bean public ErrorPageCustomizer errorPageCustomizer() { - return new ErrorPageCustomizer(this.serverProperties); + return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath); } @Bean @@ -327,15 +332,18 @@ public class ErrorMvcAutoConfiguration { private final ServerProperties properties; - protected ErrorPageCustomizer(ServerProperties properties) { + private final DispatcherServletPath dispatcherServletPath; + + protected ErrorPageCustomizer(ServerProperties properties, + DispatcherServletPath dispatcherServletPath) { this.properties = properties; + this.dispatcherServletPath = dispatcherServletPath; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { - ErrorPage errorPage = new ErrorPage( - this.properties.getServlet().getServletPrefix() - + this.properties.getError().getPath()); + ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath + .getRelativePath(this.properties.getError().getPath())); errorPageRegistry.addErrorPages(errorPage); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequestTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequestTests.java index 01ef099e31..80916df613 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequestTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequestTests.java @@ -25,6 +25,7 @@ import org.junit.rules.ExpectedException; import org.springframework.boot.autoconfigure.security.StaticResourceLocation; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -96,15 +97,20 @@ public class StaticResourceRequestTests { } private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { + DispatcherServletPath dispatcherServletPath = () -> ""; StaticWebApplicationContext context = new StaticWebApplicationContext(); context.registerBean(ServerProperties.class); + context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath); return assertThat(new RequestMatcherAssert(context, matcher)); } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, ServerProperties serverProperties) { + DispatcherServletPath dispatcherServletPath = () -> serverProperties.getServlet() + .getPath(); StaticWebApplicationContext context = new StaticWebApplicationContext(); context.registerBean(ServerProperties.class, () -> serverProperties); + context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath); return assertThat(new RequestMatcherAssert(context, matcher)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index c5ce34c9c5..ddebfab559 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -79,6 +79,7 @@ public class ServerPropertiesTests { } @Test + @Deprecated public void testServletPathAsMapping() { bind("server.servlet.path", "/foo/*"); assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*"); @@ -86,6 +87,7 @@ public class ServerPropertiesTests { } @Test + @Deprecated public void testServletPathAsPrefix() { bind("server.servlet.path", "/foo"); assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfigurationTests.java index 1655fc3eb1..ce0bb8d910 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfigurationTests.java @@ -66,8 +66,7 @@ public class DispatcherServletAutoConfigurationTests { .run((context) -> { assertThat(context).doesNotHaveBean(ServletRegistrationBean.class); assertThat(context).doesNotHaveBean(DispatcherServlet.class); - assertThat(context) - .doesNotHaveBean(DispatcherServletPathProvider.class); + assertThat(context).doesNotHaveBean(DispatcherServletPath.class); }); } @@ -77,7 +76,7 @@ public class DispatcherServletAutoConfigurationTests { public void registrationOverrideWithDispatcherServletWrongName() { this.contextRunner .withUserConfiguration(CustomDispatcherServletDifferentName.class, - CustomDispatcherServletPathProvider.class) + CustomDispatcherServletPath.class) .run((context) -> { ServletRegistrationBean registration = context .getBean(ServletRegistrationBean.class); @@ -91,7 +90,7 @@ public class DispatcherServletAutoConfigurationTests { @Test public void registrationOverrideWithAutowiredServlet() { this.contextRunner.withUserConfiguration(CustomAutowiredRegistration.class, - CustomDispatcherServletPathProvider.class).run((context) -> { + CustomDispatcherServletPath.class).run((context) -> { ServletRegistrationBean registration = context .getBean(ServletRegistrationBean.class); assertThat(registration.getUrlMappings()).containsExactly("/foo"); @@ -111,43 +110,35 @@ public class DispatcherServletAutoConfigurationTests { assertThat(registration.getUrlMappings()) .containsExactly("/spring/*"); assertThat(registration.getMultipartConfig()).isNull(); - assertThat(context.getBean(DispatcherServletPathProvider.class) - .getServletPaths()).containsExactly("/spring"); + assertThat(context.getBean(DispatcherServletPath.class).getPath()) + .isEqualTo("/spring"); }); } @Test - public void pathProviderNotCreatedWhenMultipleDispatcherServletsPresent() { - this.contextRunner - .withUserConfiguration(CustomDispatcherServletDifferentName.class) - .run((context) -> assertThat(context) - .doesNotHaveBean(DispatcherServletPathProvider.class)); - } - - @Test - public void pathProviderWhenCustomDispatcherServletSameNameShouldReturnConfiguredServletPath() { + public void dispatcherServletPathWhenCustomDispatcherServletSameNameShouldReturnConfiguredServletPath() { this.contextRunner.withUserConfiguration(CustomDispatcherServletSameName.class) .withPropertyValues("server.servlet.path:/spring") - .run((context) -> assertThat(context - .getBean(DispatcherServletPathProvider.class).getServletPaths()) - .containsExactly("/spring")); + .run((context) -> assertThat( + context.getBean(DispatcherServletPath.class).getPath()) + .isEqualTo("/spring")); } @Test - public void pathProviderNotCreatedWhenDefaultDispatcherServletNotAvailable() { + public void dispatcherServletPathNotCreatedWhenDefaultDispatcherServletNotAvailable() { this.contextRunner .withUserConfiguration(CustomDispatcherServletDifferentName.class, NonServletConfiguration.class) .run((context) -> assertThat(context) - .doesNotHaveBean(DispatcherServletPathProvider.class)); + .doesNotHaveBean(DispatcherServletPath.class)); } @Test - public void pathProviderNotCreatedWhenCustomRegistrationBeanPresent() { + public void dispatcherServletPathNotCreatedWhenCustomRegistrationBeanPresent() { this.contextRunner .withUserConfiguration(CustomDispatcherServletRegistration.class) .run((context) -> assertThat(context) - .doesNotHaveBean(DispatcherServletPathProvider.class)); + .doesNotHaveBean(DispatcherServletPath.class)); } @Test @@ -237,11 +228,11 @@ public class DispatcherServletAutoConfigurationTests { } @Configuration - protected static class CustomDispatcherServletPathProvider { + protected static class CustomDispatcherServletPath { @Bean - public DispatcherServletPathProvider dispatcherServletPathProvider() { - return mock(DispatcherServletPathProvider.class); + public DispatcherServletPath dispatcherServletPath() { + return mock(DispatcherServletPath.class); } } @@ -259,8 +250,8 @@ public class DispatcherServletAutoConfigurationTests { } @Bean - public DispatcherServletPathProvider dispatcherServletPathProvider() { - return mock(DispatcherServletPathProvider.class); + public DispatcherServletPath dispatcherServletPath() { + return mock(DispatcherServletPath.class); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathTests.java new file mode 100644 index 0000000000..663d89d1b9 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletPathTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web.servlet; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DispatcherServletPath}. + * + * @author Phillip Webb + */ +public class DispatcherServletPathTests { + + @Test + public void getRelativePathReturnsRelativePath() { + assertThat(((DispatcherServletPath) () -> "spring").getRelativePath("boot")) + .isEqualTo("spring/boot"); + assertThat(((DispatcherServletPath) () -> "spring/").getRelativePath("boot")) + .isEqualTo("spring/boot"); + assertThat(((DispatcherServletPath) () -> "spring").getRelativePath("/boot")) + .isEqualTo("spring/boot"); + } + + @Test + public void getPrefixWhenHasSimplePathReturnPath() { + assertThat(((DispatcherServletPath) () -> "spring").getPrefix()) + .isEqualTo("spring"); + } + + @Test + public void getPrefixWhenHasPatternRemovesPattern() { + assertThat(((DispatcherServletPath) () -> "spring/*.do").getPrefix()) + .isEqualTo("spring"); + } + + @Test + public void getPathWhenPathEndsWithSlashRemovesSlash() { + assertThat(((DispatcherServletPath) () -> "spring/").getPrefix()) + .isEqualTo("spring"); + } + + @Test + public void getServletUrlMappingWhenPathIsEmptyReturnsSlash() { + assertThat(((DispatcherServletPath) () -> "").getServletUrlMapping()) + .isEqualTo("/"); + } + + @Test + public void getServletUrlMappingWhenPathIsSlashReturnsSlash() { + assertThat(((DispatcherServletPath) () -> "/").getServletUrlMapping()) + .isEqualTo("/"); + } + + @Test + public void getServletUrlMappingWhenPathContainsStarReturnsPath() { + assertThat(((DispatcherServletPath) () -> "spring/*.do").getServletUrlMapping()) + .isEqualTo("spring/*.do"); + } + + @Test + public void getServletUrlMappingWhenHasPathNotEndingSlashReturnsSlashStarPattern() { + assertThat(((DispatcherServletPath) () -> "spring/boot").getServletUrlMapping()) + .isEqualTo("spring/boot/*"); + } + + @Test + public void getServletUrlMappingWhenHasPathEndingWithSlashReturnsSlashStarPattern() { + assertThat(((DispatcherServletPath) () -> "spring/boot/").getServletUrlMapping()) + .isEqualTo("spring/boot/*"); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBeanTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBeanTests.java new file mode 100644 index 0000000000..5f0e70278a --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBeanTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web.servlet; + +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.web.servlet.DispatcherServlet; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DispatcherServletRegistrationBean}. + * + * @author Phillip Webb + */ +public class DispatcherServletRegistrationBeanTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createWhenPathIsNullThrowsException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Path must not be null"); + new DispatcherServletRegistrationBean(new DispatcherServlet(), null); + } + + @Test + public void getPathReturnsPath() { + DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean( + new DispatcherServlet(), "/test"); + assertThat(bean.getPath()).isEqualTo("/test"); + } + + @Test + public void getUrlMappingsReturnsSinglePathMappedPattern() { + DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean( + new DispatcherServlet(), "/test"); + assertThat(bean.getUrlMappings()).containsOnly("/test/*"); + } + + @Test + public void setUrlMappingsCannotBeCalled() { + DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean( + new DispatcherServlet(), "/test"); + this.thrown.expect(UnsupportedOperationException.class); + bean.setUrlMappings(Collections.emptyList()); + } + + @Test + public void addUrlMappingsCannotBeCalled() { + DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean( + new DispatcherServlet(), "/test"); + this.thrown.expect(UnsupportedOperationException.class); + bean.addUrlMappings("/test"); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfigurationTests.java index 21dc96fdaa..a266abd972 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfigurationTests.java @@ -20,6 +20,7 @@ import org.junit.Rule; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.web.servlet.error.ErrorAttributes; @@ -39,7 +40,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class ErrorMvcAutoConfigurationTests { private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ErrorMvcAutoConfiguration.class)); + .withConfiguration( + AutoConfigurations.of(DispatcherServletAutoConfiguration.class, + ErrorMvcAutoConfiguration.class)); @Rule public OutputCapture outputCapture = new OutputCapture(); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java index 28922d6a1b..4fe86a2c6a 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -48,19 +50,28 @@ import org.springframework.web.servlet.DispatcherServlet; @Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @AutoConfigureAfter(WebMvcAutoConfiguration.class) -@EnableConfigurationProperties(WebMvcProperties.class) +@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class }) public class MockMvcAutoConfiguration { private final WebApplicationContext context; + private final ServerProperties serverProperties; + private final WebMvcProperties webMvcProperties; MockMvcAutoConfiguration(WebApplicationContext context, - WebMvcProperties webMvcProperties) { + ServerProperties serverProperties, WebMvcProperties webMvcProperties) { this.context = context; + this.serverProperties = serverProperties; this.webMvcProperties = webMvcProperties; } + @Bean + @ConditionalOnMissingBean + public DispatcherServletPath dispatcherServletPath() { + return () -> this.serverProperties.getServlet().getPath(); + } + @Bean @ConditionalOnMissingBean(MockMvcBuilder.class) public DefaultMockMvcBuilder mockMvcBuilder(