diff --git a/.eclipse/eclipse.properties b/eclipse/eclipse.properties similarity index 100% rename from .eclipse/eclipse.properties rename to eclipse/eclipse.properties diff --git a/pom.xml b/pom.xml index dc9f3404fe..d673e1dc10 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,9 @@ default - true + + !disable-spring-boot-default-profile + 0.0.3 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/CompositeHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerMapping.java index c975cf1f67..b613000a06 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerMapping.java @@ -24,12 +24,11 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerMapping; /** - * Composite {@link HandlerExceptionResolver}. + * Composite {@link HandlerMapping}. * * @author Andy Wilkinson * @author Stephane Nicoll 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 c51e6b51b6..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.servlet.WebMvcProperties; +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(WebMvcProperties.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( - WebMvcProperties serverProperties) { - return getPatterns(serverProperties).map(AntPathRequestMatcher::new) + DispatcherServletPath dispatcherServletPath) { + return getPatterns(dispatcherServletPath).map(AntPathRequestMatcher::new) .collect(Collectors.toList()); } - private Stream getPatterns(WebMvcProperties 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/servlet/DispatcherServletAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java index 055cdc32cf..6293e7e0de 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; @@ -134,11 +132,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.webMvcProperties.getServlet().getServletMapping()); + DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean( + dispatcherServlet, this.webMvcProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); @@ -148,15 +145,6 @@ public class DispatcherServletAutoConfiguration { return registration; } - @Bean - @ConditionalOnMissingBean(DispatcherServletPathProvider.class) - @ConditionalOnSingleCandidate(DispatcherServlet.class) - public DispatcherServletPathProvider dispatcherServletPathProvider() { - return () -> Collections.singleton( - DispatcherServletRegistrationConfiguration.this.webMvcProperties - .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 6e53ce3f51..098d797358 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.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -96,15 +97,15 @@ public class ErrorMvcAutoConfiguration { private final ServerProperties serverProperties; - private final WebMvcProperties webMvcProperties; + private final DispatcherServletPath dispatcherServletPath; private final List errorViewResolvers; public ErrorMvcAutoConfiguration(ServerProperties serverProperties, - WebMvcProperties webMvcProperties, + DispatcherServletPath dispatcherServletPath, ObjectProvider> errorViewResolversProvider) { this.serverProperties = serverProperties; - this.webMvcProperties = webMvcProperties; + this.dispatcherServletPath = dispatcherServletPath; this.errorViewResolvers = errorViewResolversProvider.getIfAvailable(); } @@ -124,7 +125,7 @@ public class ErrorMvcAutoConfiguration { @Bean public ErrorPageCustomizer errorPageCustomizer() { - return new ErrorPageCustomizer(this.serverProperties, this.webMvcProperties); + return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath); } @Bean @@ -331,21 +332,20 @@ public class ErrorMvcAutoConfiguration { */ private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { - private final ServerProperties serverProperties; + private final ServerProperties properties; - private final WebMvcProperties webMvcProperties; + private final DispatcherServletPath dispatcherServletPath; - protected ErrorPageCustomizer(ServerProperties serverProperties, - WebMvcProperties webMvcProperties) { - this.serverProperties = serverProperties; - this.webMvcProperties = webMvcProperties; + protected ErrorPageCustomizer(ServerProperties properties, + DispatcherServletPath dispatcherServletPath) { + this.properties = properties; + this.dispatcherServletPath = dispatcherServletPath; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { - ErrorPage errorPage = new ErrorPage( - this.webMvcProperties.getServlet().getServletPrefix() - + this.serverProperties.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 ee842dc774..8855a8f106 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 @@ -24,7 +24,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.boot.autoconfigure.security.StaticResourceLocation; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; +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; @@ -74,11 +74,9 @@ public class StaticResourceRequestTests { @Test public void atLocationWhenHasServletPathShouldMatchLocation() { - WebMvcProperties webMvcProperties = new WebMvcProperties(); - webMvcProperties.getServlet().setPath("/foo"); RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS); - assertMatcher(matcher, webMvcProperties).matches("/foo", "/css/file.css"); - assertMatcher(matcher, webMvcProperties).doesNotMatch("/foo", "/js/file.js"); + assertMatcher(matcher, "/foo").matches("/foo", "/css/file.css"); + assertMatcher(matcher, "/foo").doesNotMatch("/foo", "/js/file.js"); } @Test @@ -96,15 +94,16 @@ public class StaticResourceRequestTests { } private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { + DispatcherServletPath dispatcherServletPath = () -> ""; StaticWebApplicationContext context = new StaticWebApplicationContext(); - context.registerBean(WebMvcProperties.class); + context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath); return assertThat(new RequestMatcherAssert(context, matcher)); } - private RequestMatcherAssert assertMatcher(RequestMatcher matcher, - WebMvcProperties webMvcProperties) { + private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String path) { + DispatcherServletPath dispatcherServletPath = () -> path; StaticWebApplicationContext context = new StaticWebApplicationContext(); - context.registerBean(WebMvcProperties.class, () -> webMvcProperties); + 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/servlet/DispatcherServletAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfigurationTests.java index d2da32c049..9ad1d12203 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("spring.mvc.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 b3713e0581..92ce52834f 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,7 +50,7 @@ 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; @@ -61,6 +63,12 @@ public class MockMvcAutoConfiguration { this.webMvcProperties = webMvcProperties; } + @Bean + @ConditionalOnMissingBean + public DispatcherServletPath dispatcherServletPath() { + return () -> this.webMvcProperties.getServlet().getPath(); + } + @Bean @ConditionalOnMissingBean(MockMvcBuilder.class) public DefaultMockMvcBuilder mockMvcBuilder(