Commit b5b60c20 authored by Phillip Webb's avatar Phillip Webb

Merge branch '2.0.x'

parents 276f9782 9a9111af
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
<profile> <profile>
<id>default</id> <id>default</id>
<activation> <activation>
<activeByDefault>true</activeByDefault> <property>
<name>!disable-spring-boot-default-profile</name>
</property>
</activation> </activation>
<properties> <properties>
<spring-javaformat.version>0.0.3</spring-javaformat.version> <spring-javaformat.version>0.0.3</spring-javaformat.version>
......
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web; 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.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter; import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
...@@ -30,11 +27,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -30,11 +27,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; 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.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
/** /**
...@@ -72,27 +68,13 @@ public class ServletEndpointManagementContextConfiguration { ...@@ -72,27 +68,13 @@ public class ServletEndpointManagementContextConfiguration {
public ServletEndpointRegistrar servletEndpointRegistrar( public ServletEndpointRegistrar servletEndpointRegistrar(
WebEndpointProperties properties, WebEndpointProperties properties,
ServletEndpointsSupplier servletEndpointsSupplier) { ServletEndpointsSupplier servletEndpointsSupplier) {
DispatcherServletPathProvider servletPathProvider = this.context DispatcherServletPath dispatcherServletPath = this.context
.getBean(DispatcherServletPathProvider.class); .getBean(DispatcherServletPath.class);
Set<String> cleanedPaths = getServletPaths(properties, servletPathProvider); return new ServletEndpointRegistrar(
return new ServletEndpointRegistrar(cleanedPaths, dispatcherServletPath.getRelativePath(properties.getBasePath()),
servletEndpointsSupplier.getEndpoints()); servletEndpointsSupplier.getEndpoints());
} }
private Set<String> 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 @Configuration
......
...@@ -33,7 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; ...@@ -33,7 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; 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.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
...@@ -137,23 +137,20 @@ public final class EndpointRequest { ...@@ -137,23 +137,20 @@ public final class EndpointRequest {
private RequestMatcher createDelegate(WebApplicationContext context) { private RequestMatcher createDelegate(WebApplicationContext context) {
try { try {
Set<String> servletPaths = getServletPaths(context); String pathPrefix = getPathPrefix(context);
RequestMatcherFactory requestMatcherFactory = new RequestMatcherFactory( return createDelegate(context, new RequestMatcherFactory(pathPrefix));
servletPaths);
return createDelegate(context, requestMatcherFactory);
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
return EMPTY_MATCHER; return EMPTY_MATCHER;
} }
} }
private Set<String> getServletPaths(WebApplicationContext context) { private String getPathPrefix(WebApplicationContext context) {
try { try {
return context.getBean(DispatcherServletPathProvider.class) return context.getBean(DispatcherServletPath.class).getPrefix();
.getServletPaths();
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
return Collections.singleton(""); return "";
} }
} }
...@@ -225,7 +222,7 @@ public final class EndpointRequest { ...@@ -225,7 +222,7 @@ public final class EndpointRequest {
requestMatcherFactory, paths); requestMatcherFactory, paths);
if (this.includeLinks if (this.includeLinks
&& StringUtils.hasText(pathMappedEndpoints.getBasePath())) { && StringUtils.hasText(pathMappedEndpoints.getBasePath())) {
delegateMatchers.addAll( delegateMatchers.add(
requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath())); requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath()));
} }
return new OrRequestMatcher(delegateMatchers); return new OrRequestMatcher(delegateMatchers);
...@@ -258,8 +255,7 @@ public final class EndpointRequest { ...@@ -258,8 +255,7 @@ public final class EndpointRequest {
private List<RequestMatcher> getDelegateMatchers( private List<RequestMatcher> getDelegateMatchers(
RequestMatcherFactory requestMatcherFactory, Set<String> paths) { RequestMatcherFactory requestMatcherFactory, Set<String> paths) {
return paths.stream() return paths.stream()
.flatMap( .map((path) -> requestMatcherFactory.antPath(path, "/**"))
(path) -> requestMatcherFactory.antPath(path, "/**").stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
...@@ -276,9 +272,7 @@ public final class EndpointRequest { ...@@ -276,9 +272,7 @@ public final class EndpointRequest {
WebEndpointProperties properties = context WebEndpointProperties properties = context
.getBean(WebEndpointProperties.class); .getBean(WebEndpointProperties.class);
if (StringUtils.hasText(properties.getBasePath())) { if (StringUtils.hasText(properties.getBasePath())) {
List<RequestMatcher> matchers = requestMatcherFactory return requestMatcherFactory.antPath(properties.getBasePath());
.antPath(properties.getBasePath());
return new OrRequestMatcher(matchers);
} }
return EMPTY_MATCHER; return EMPTY_MATCHER;
} }
...@@ -290,19 +284,18 @@ public final class EndpointRequest { ...@@ -290,19 +284,18 @@ public final class EndpointRequest {
*/ */
private static class RequestMatcherFactory { private static class RequestMatcherFactory {
private final Set<String> servletPaths = new LinkedHashSet<>(); private final String prefix;
RequestMatcherFactory(Set<String> servletPaths) { RequestMatcherFactory(String prefix) {
this.servletPaths.addAll(servletPaths); this.prefix = prefix;
} }
List<RequestMatcher> antPath(String... parts) { public RequestMatcher antPath(String... parts) {
return this.servletPaths.stream() String pattern = this.prefix;
.map((p) -> (StringUtils.hasText(p) && !p.equals("/") ? p : "")) for (String part : parts) {
.distinct() pattern += part;
.map((path) -> Arrays.stream(parts) }
.collect(Collectors.joining("", path, ""))) return new AntPathRequestMatcher(pattern);
.map(AntPathRequestMatcher::new).collect(Collectors.toList());
} }
} }
......
...@@ -24,12 +24,11 @@ import javax.servlet.http.HttpServletRequest; ...@@ -24,12 +24,11 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
/** /**
* Composite {@link HandlerExceptionResolver}. * Composite {@link HandlerMapping}.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.web.servlet; package org.springframework.boot.actuate.autoconfigure.web.servlet;
import java.util.Collections;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
...@@ -27,7 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ...@@ -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;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; 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.error.ErrorAttributes;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -72,6 +70,12 @@ class WebMvcEndpointChildContextConfiguration { ...@@ -72,6 +70,12 @@ class WebMvcEndpointChildContextConfiguration {
return dispatcherServlet; 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) @Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME)
public CompositeHandlerMapping compositeHandlerMapping() { public CompositeHandlerMapping compositeHandlerMapping() {
return new CompositeHandlerMapping(); return new CompositeHandlerMapping();
...@@ -95,9 +99,4 @@ class WebMvcEndpointChildContextConfiguration { ...@@ -95,9 +99,4 @@ class WebMvcEndpointChildContextConfiguration {
return new OrderedRequestContextFilter(); return new OrderedRequestContextFilter();
} }
@Bean
public DispatcherServletPathProvider childDispatcherServletPathProvider() {
return () -> Collections.singleton("");
}
} }
...@@ -17,15 +17,13 @@ ...@@ -17,15 +17,13 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web; package org.springframework.boot.actuate.autoconfigure.endpoint.web;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar; import org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier; 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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
...@@ -50,22 +48,18 @@ public class ServletEndpointManagementContextConfigurationTests { ...@@ -50,22 +48,18 @@ public class ServletEndpointManagementContextConfigurationTests {
.withUserConfiguration(TestConfig.class); .withUserConfiguration(TestConfig.class);
@Test @Test
@SuppressWarnings("unchecked")
public void contextShouldContainServletEndpointRegistrar() { public void contextShouldContainServletEndpointRegistrar() {
FilteredClassLoader classLoader = new FilteredClassLoader(ResourceConfig.class); FilteredClassLoader classLoader = new FilteredClassLoader(ResourceConfig.class);
this.contextRunner.withClassLoader(classLoader).run((context) -> { this.contextRunner.withClassLoader(classLoader).run((context) -> {
assertThat(context).hasSingleBean(ServletEndpointRegistrar.class); assertThat(context).hasSingleBean(ServletEndpointRegistrar.class);
ServletEndpointRegistrar bean = context ServletEndpointRegistrar bean = context
.getBean(ServletEndpointRegistrar.class); .getBean(ServletEndpointRegistrar.class);
Set<String> basePaths = (Set<String>) ReflectionTestUtils.getField(bean, String basePath = (String) ReflectionTestUtils.getField(bean, "basePath");
"basePaths"); assertThat(basePath).isEqualTo("/test/actuator");
assertThat(basePaths).containsExactlyInAnyOrder("/test/actuator", "/actuator",
"/foo/actuator");
}); });
} }
@Test @Test
@SuppressWarnings("unchecked")
public void servletPathShouldNotAffectJerseyConfiguration() { public void servletPathShouldNotAffectJerseyConfiguration() {
FilteredClassLoader classLoader = new FilteredClassLoader( FilteredClassLoader classLoader = new FilteredClassLoader(
DispatcherServlet.class); DispatcherServlet.class);
...@@ -73,9 +67,8 @@ public class ServletEndpointManagementContextConfigurationTests { ...@@ -73,9 +67,8 @@ public class ServletEndpointManagementContextConfigurationTests {
assertThat(context).hasSingleBean(ServletEndpointRegistrar.class); assertThat(context).hasSingleBean(ServletEndpointRegistrar.class);
ServletEndpointRegistrar bean = context ServletEndpointRegistrar bean = context
.getBean(ServletEndpointRegistrar.class); .getBean(ServletEndpointRegistrar.class);
Set<String> basePaths = (Set<String>) ReflectionTestUtils.getField(bean, String basePath = (String) ReflectionTestUtils.getField(bean, "basePath");
"basePaths"); assertThat(basePath).isEqualTo("/actuator");
assertThat(basePaths).containsExactly("/actuator");
}); });
} }
...@@ -97,14 +90,8 @@ public class ServletEndpointManagementContextConfigurationTests { ...@@ -97,14 +90,8 @@ public class ServletEndpointManagementContextConfigurationTests {
} }
@Bean @Bean
public DispatcherServletPathProvider servletPathProvider() { public DispatcherServletPath dispatcherServletPath() {
return () -> { return () -> "/test";
Set<String> paths = new LinkedHashSet<>();
paths.add("/");
paths.add("/test");
paths.add("/foo/");
return paths;
};
} }
} }
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
package org.springframework.boot.actuate.autoconfigure.security.servlet; package org.springframework.boot.actuate.autoconfigure.security.servlet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -33,7 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; ...@@ -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.PathMappedEndpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint; 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.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
...@@ -78,12 +76,11 @@ public class EndpointRequestTests { ...@@ -78,12 +76,11 @@ public class EndpointRequestTests {
@Test @Test
public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() { public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator", "/spring", "/admin") assertMatcher(matcher, "/actuator", "/spring").matches("/spring",
.matches(Arrays.asList("/spring", "/admin"), "/actuator/foo"); "/actuator/foo");
assertMatcher(matcher, "/actuator", "/spring", "/admin") assertMatcher(matcher, "/actuator", "/spring").matches("/spring",
.matches(Arrays.asList("/spring", "/admin"), "/actuator/bar"); "/actuator/bar");
assertMatcher(matcher, "/actuator", "/spring").matches(Arrays.asList("/spring"), assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator");
"/actuator");
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring", assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring",
"/actuator/baz"); "/actuator/baz");
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo"); assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo");
...@@ -92,10 +89,10 @@ public class EndpointRequestTests { ...@@ -92,10 +89,10 @@ public class EndpointRequestTests {
@Test @Test
public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() { public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/foo"); assertMatcher(matcher, "/actuator", null).matches("/actuator/foo");
assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/bar"); assertMatcher(matcher, "/actuator", null).matches("/actuator/bar");
assertMatcher(matcher, "/actuator", (String) null).matches("/actuator"); assertMatcher(matcher, "/actuator", null).matches("/actuator");
assertMatcher(matcher, "/actuator", (String) null).doesNotMatch("/actuator/baz"); assertMatcher(matcher, "/actuator", null).doesNotMatch("/actuator/baz");
} }
@Test @Test
...@@ -222,8 +219,8 @@ public class EndpointRequestTests { ...@@ -222,8 +219,8 @@ public class EndpointRequestTests {
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath,
String... servletPaths) { String servletPath) {
return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPaths); return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath);
} }
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
...@@ -246,7 +243,7 @@ public class EndpointRequestTests { ...@@ -246,7 +243,7 @@ public class EndpointRequestTests {
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
PathMappedEndpoints pathMappedEndpoints, String... servletPaths) { PathMappedEndpoints pathMappedEndpoints, String dispatcherServletPath) {
StaticWebApplicationContext context = new StaticWebApplicationContext(); StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(WebEndpointProperties.class); context.registerBean(WebEndpointProperties.class);
if (pathMappedEndpoints != null) { if (pathMappedEndpoints != null) {
...@@ -257,10 +254,9 @@ public class EndpointRequestTests { ...@@ -257,10 +254,9 @@ public class EndpointRequestTests {
properties.setBasePath(pathMappedEndpoints.getBasePath()); properties.setBasePath(pathMappedEndpoints.getBasePath());
} }
} }
if (servletPaths != null) { if (dispatcherServletPath != null) {
DispatcherServletPathProvider pathProvider = () -> new LinkedHashSet<>( DispatcherServletPath path = () -> dispatcherServletPath;
Arrays.asList(servletPaths)); context.registerBean(DispatcherServletPath.class, () -> path);
context.registerBean(DispatcherServletPathProvider.class, () -> pathProvider);
} }
return assertThat(new RequestMatcherAssert(context, matcher)); return assertThat(new RequestMatcherAssert(context, matcher));
} }
...@@ -280,8 +276,8 @@ public class EndpointRequestTests { ...@@ -280,8 +276,8 @@ public class EndpointRequestTests {
matches(mockRequest(servletPath)); matches(mockRequest(servletPath));
} }
public void matches(List<String> servletPaths, String pathInfo) { public void matches(String servletPath, String pathInfo) {
servletPaths.forEach((p) -> matches(mockRequest(p, pathInfo))); matches(mockRequest(servletPath, pathInfo));
} }
private void matches(HttpServletRequest request) { private void matches(HttpServletRequest request) {
......
...@@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet; ...@@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet;
import org.junit.Test; 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.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -64,12 +64,12 @@ public class WebMvcEndpointChildContextConfigurationTests { ...@@ -64,12 +64,12 @@ public class WebMvcEndpointChildContextConfigurationTests {
} }
@Test @Test
public void contextShouldConfigureDispatcherServletPathProviderWithEmptyPath() { public void contextShouldConfigureDispatcherServletPathWithRootPath() {
this.contextRunner this.contextRunner
.withUserConfiguration(WebMvcEndpointChildContextConfiguration.class) .withUserConfiguration(WebMvcEndpointChildContextConfiguration.class)
.run((context) -> assertThat(context .run((context) -> assertThat(
.getBean(DispatcherServletPathProvider.class).getServletPaths()) context.getBean(DispatcherServletPath.class).getPath())
.containsExactly("")); .isEqualTo("/"));
} }
static class ExistingConfig { static class ExistingConfig {
......
...@@ -16,10 +16,7 @@ ...@@ -16,10 +16,7 @@
package org.springframework.boot.actuate.endpoint.web; package org.springframework.boot.actuate.endpoint.web;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
...@@ -30,7 +27,6 @@ import org.apache.commons.logging.LogFactory; ...@@ -30,7 +27,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/** /**
* {@link ServletContextInitializer} to register {@link ExposableServletEndpoint servlet * {@link ServletContextInitializer} to register {@link ExposableServletEndpoint servlet
...@@ -44,24 +40,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer { ...@@ -44,24 +40,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer {
private static final Log logger = LogFactory.getLog(ServletEndpointRegistrar.class); private static final Log logger = LogFactory.getLog(ServletEndpointRegistrar.class);
private final Set<String> basePaths = new LinkedHashSet<>(); private final String basePath;
private final Collection<ExposableServletEndpoint> servletEndpoints; private final Collection<ExposableServletEndpoint> servletEndpoints;
public ServletEndpointRegistrar(String basePath, public ServletEndpointRegistrar(String basePath,
Collection<ExposableServletEndpoint> servletEndpoints) { Collection<ExposableServletEndpoint> servletEndpoints) {
Assert.notNull(servletEndpoints, "ServletEndpoints must not be null"); Assert.notNull(servletEndpoints, "ServletEndpoints must not be null");
this.basePaths.add((basePath != null ? basePath : "")); this.basePath = (basePath != null ? basePath : "");
this.servletEndpoints = servletEndpoints;
}
public ServletEndpointRegistrar(Set<String> basePaths,
Collection<ExposableServletEndpoint> servletEndpoints) {
Assert.notNull(servletEndpoints, "ServletEndpoints must not be null");
this.basePaths.addAll(basePaths);
if (CollectionUtils.isEmpty(this.basePaths)) {
this.basePaths.add("");
}
this.servletEndpoints = servletEndpoints; this.servletEndpoints = servletEndpoints;
} }
...@@ -74,24 +60,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer { ...@@ -74,24 +60,14 @@ public class ServletEndpointRegistrar implements ServletContextInitializer {
private void register(ServletContext servletContext, private void register(ServletContext servletContext,
ExposableServletEndpoint endpoint) { ExposableServletEndpoint endpoint) {
String name = endpoint.getId() + "-actuator-endpoint"; String name = endpoint.getId() + "-actuator-endpoint";
String path = this.basePath + "/" + endpoint.getRootPath();
String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*");
EndpointServlet endpointServlet = endpoint.getEndpointServlet(); EndpointServlet endpointServlet = endpoint.getEndpointServlet();
Dynamic registration = servletContext.addServlet(name, Dynamic registration = servletContext.addServlet(name,
endpointServlet.getServlet()); endpointServlet.getServlet());
String[] urlMappings = getUrlMappings(endpoint.getRootPath()); registration.addMapping(urlMapping);
registration.addMapping(urlMappings);
if (logger.isInfoEnabled()) {
Arrays.stream(urlMappings).forEach(
(mapping) -> logger.info("Registered '" + mapping + "' to " + name));
}
registration.setInitParameters(endpointServlet.getInitParameters()); registration.setInitParameters(endpointServlet.getInitParameters());
} logger.info("Registered '" + path + "' to " + name);
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);
} }
} }
...@@ -18,8 +18,6 @@ package org.springframework.boot.actuate.endpoint.web; ...@@ -18,8 +18,6 @@ package org.springframework.boot.actuate.endpoint.web;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.GenericServlet; import javax.servlet.GenericServlet;
import javax.servlet.Servlet; import javax.servlet.Servlet;
...@@ -49,7 +47,6 @@ import static org.mockito.Mockito.verify; ...@@ -49,7 +47,6 @@ import static org.mockito.Mockito.verify;
* Tests for {@link ServletEndpointRegistrar}. * Tests for {@link ServletEndpointRegistrar}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave
*/ */
public class ServletEndpointRegistrarTests { public class ServletEndpointRegistrarTests {
...@@ -76,14 +73,14 @@ public class ServletEndpointRegistrarTests { ...@@ -76,14 +73,14 @@ public class ServletEndpointRegistrarTests {
public void createWhenServletEndpointsIsNullShouldThrowException() { public void createWhenServletEndpointsIsNullShouldThrowException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("ServletEndpoints must not be null"); this.thrown.expectMessage("ServletEndpoints must not be null");
new ServletEndpointRegistrar((String) null, null); new ServletEndpointRegistrar(null, null);
} }
@Test @Test
public void onStartupShouldRegisterServlets() throws Exception { public void onStartupShouldRegisterServlets() throws Exception {
ExposableServletEndpoint endpoint = mockEndpoint( ExposableServletEndpoint endpoint = mockEndpoint(
new EndpointServlet(TestServlet.class)); new EndpointServlet(TestServlet.class));
ServletEndpointRegistrar registrar = new ServletEndpointRegistrar((String) null, ServletEndpointRegistrar registrar = new ServletEndpointRegistrar(null,
Collections.singleton(endpoint)); Collections.singleton(endpoint));
registrar.onStartup(this.servletContext); registrar.onStartup(this.servletContext);
verify(this.servletContext).addServlet(eq("test-actuator-endpoint"), verify(this.servletContext).addServlet(eq("test-actuator-endpoint"),
...@@ -105,64 +102,6 @@ public class ServletEndpointRegistrarTests { ...@@ -105,64 +102,6 @@ public class ServletEndpointRegistrarTests {
verify(this.dynamic).addMapping("/actuator/test/*"); verify(this.dynamic).addMapping("/actuator/test/*");
} }
@Test
public void onStartupWhenHasMultipleBasePathsShouldIncludeAllBasePaths()
throws Exception {
ExposableServletEndpoint endpoint = mockEndpoint(
new EndpointServlet(TestServlet.class));
Set<String> 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<String> 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<String> 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<String> 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<String> 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 @Test
public void onStartupWhenHasInitParametersShouldRegisterInitParameters() public void onStartupWhenHasInitParametersShouldRegisterInitParameters()
throws Exception { throws Exception {
......
...@@ -27,7 +27,7 @@ import java.util.stream.Stream; ...@@ -27,7 +27,7 @@ import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation; 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.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher;
...@@ -96,14 +96,14 @@ public final class StaticResourceRequest { ...@@ -96,14 +96,14 @@ public final class StaticResourceRequest {
* Locations}. * Locations}.
*/ */
public static final class StaticResourceRequestMatcher public static final class StaticResourceRequestMatcher
extends ApplicationContextRequestMatcher<WebMvcProperties> { extends ApplicationContextRequestMatcher<DispatcherServletPath> {
private final Set<StaticResourceLocation> locations; private final Set<StaticResourceLocation> locations;
private volatile RequestMatcher delegate; private volatile RequestMatcher delegate;
private StaticResourceRequestMatcher(Set<StaticResourceLocation> locations) { private StaticResourceRequestMatcher(Set<StaticResourceLocation> locations) {
super(WebMvcProperties.class); super(DispatcherServletPath.class);
this.locations = locations; this.locations = locations;
} }
...@@ -134,25 +134,26 @@ public final class StaticResourceRequest { ...@@ -134,25 +134,26 @@ public final class StaticResourceRequest {
} }
@Override @Override
protected void initialized(Supplier<WebMvcProperties> serverProperties) { protected void initialized(
Supplier<DispatcherServletPath> dispatcherServletPath) {
this.delegate = new OrRequestMatcher( this.delegate = new OrRequestMatcher(
getDelegateMatchers(serverProperties.get())); getDelegateMatchers(dispatcherServletPath.get()));
} }
private List<RequestMatcher> getDelegateMatchers( private List<RequestMatcher> getDelegateMatchers(
WebMvcProperties serverProperties) { DispatcherServletPath dispatcherServletPath) {
return getPatterns(serverProperties).map(AntPathRequestMatcher::new) return getPatterns(dispatcherServletPath).map(AntPathRequestMatcher::new)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private Stream<String> getPatterns(WebMvcProperties serverProperties) { private Stream<String> getPatterns(DispatcherServletPath dispatcherServletPath) {
return this.locations.stream().flatMap(StaticResourceLocation::getPatterns) return this.locations.stream().flatMap(StaticResourceLocation::getPatterns)
.map(serverProperties.getServlet()::getPath); .map(dispatcherServletPath::getRelativePath);
} }
@Override @Override
protected boolean matches(HttpServletRequest request, protected boolean matches(HttpServletRequest request,
Supplier<WebMvcProperties> context) { Supplier<DispatcherServletPath> context) {
return this.delegate.matches(request); return this.delegate.matches(request);
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.autoconfigure.web.servlet; package org.springframework.boot.autoconfigure.web.servlet;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import javax.servlet.MultipartConfigElement; import javax.servlet.MultipartConfigElement;
...@@ -34,7 +33,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome; ...@@ -34,7 +33,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
...@@ -134,11 +132,10 @@ public class DispatcherServletAutoConfiguration { ...@@ -134,11 +132,10 @@ public class DispatcherServletAutoConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration( public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) { DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>( DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
dispatcherServlet, dispatcherServlet, this.webMvcProperties.getServlet().getPath());
this.webMvcProperties.getServlet().getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup( registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup()); this.webMvcProperties.getServlet().getLoadOnStartup());
...@@ -148,15 +145,6 @@ public class DispatcherServletAutoConfiguration { ...@@ -148,15 +145,6 @@ public class DispatcherServletAutoConfiguration {
return registration; 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) @Order(Ordered.LOWEST_PRECEDENCE - 10)
......
/*
* 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() + "/*";
}
}
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.web.servlet; package org.springframework.boot.autoconfigure.web.servlet;
import java.util.Set;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
/** /**
...@@ -26,10 +24,13 @@ import org.springframework.web.servlet.DispatcherServlet; ...@@ -26,10 +24,13 @@ import org.springframework.web.servlet.DispatcherServlet;
* *
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.2 * @since 2.0.2
* @deprecated since 2.0.4 in favor of {@link DispatcherServletPath} and
* {@link DispatcherServletRegistrationBean}
*/ */
@Deprecated
@FunctionalInterface @FunctionalInterface
public interface DispatcherServletPathProvider { public interface DispatcherServletPathProvider {
Set<String> getServletPaths(); String getServletPath();
} }
/*
* 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<DispatcherServlet> 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<String> 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");
}
}
...@@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi ...@@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties; 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.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
...@@ -96,15 +97,15 @@ public class ErrorMvcAutoConfiguration { ...@@ -96,15 +97,15 @@ public class ErrorMvcAutoConfiguration {
private final ServerProperties serverProperties; private final ServerProperties serverProperties;
private final WebMvcProperties webMvcProperties; private final DispatcherServletPath dispatcherServletPath;
private final List<ErrorViewResolver> errorViewResolvers; private final List<ErrorViewResolver> errorViewResolvers;
public ErrorMvcAutoConfiguration(ServerProperties serverProperties, public ErrorMvcAutoConfiguration(ServerProperties serverProperties,
WebMvcProperties webMvcProperties, DispatcherServletPath dispatcherServletPath,
ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) { ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
this.serverProperties = serverProperties; this.serverProperties = serverProperties;
this.webMvcProperties = webMvcProperties; this.dispatcherServletPath = dispatcherServletPath;
this.errorViewResolvers = errorViewResolversProvider.getIfAvailable(); this.errorViewResolvers = errorViewResolversProvider.getIfAvailable();
} }
...@@ -124,7 +125,7 @@ public class ErrorMvcAutoConfiguration { ...@@ -124,7 +125,7 @@ public class ErrorMvcAutoConfiguration {
@Bean @Bean
public ErrorPageCustomizer errorPageCustomizer() { public ErrorPageCustomizer errorPageCustomizer() {
return new ErrorPageCustomizer(this.serverProperties, this.webMvcProperties); return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
} }
@Bean @Bean
...@@ -331,21 +332,20 @@ public class ErrorMvcAutoConfiguration { ...@@ -331,21 +332,20 @@ public class ErrorMvcAutoConfiguration {
*/ */
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { 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, protected ErrorPageCustomizer(ServerProperties properties,
WebMvcProperties webMvcProperties) { DispatcherServletPath dispatcherServletPath) {
this.serverProperties = serverProperties; this.properties = properties;
this.webMvcProperties = webMvcProperties; this.dispatcherServletPath = dispatcherServletPath;
} }
@Override @Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage( ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath
this.webMvcProperties.getServlet().getServletPrefix() .getRelativePath(this.properties.getError().getPath()));
+ this.serverProperties.getError().getPath());
errorPageRegistry.addErrorPages(errorPage); errorPageRegistry.addErrorPages(errorPage);
} }
......
...@@ -24,7 +24,7 @@ import org.junit.Test; ...@@ -24,7 +24,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation; 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.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
...@@ -74,11 +74,9 @@ public class StaticResourceRequestTests { ...@@ -74,11 +74,9 @@ public class StaticResourceRequestTests {
@Test @Test
public void atLocationWhenHasServletPathShouldMatchLocation() { public void atLocationWhenHasServletPathShouldMatchLocation() {
WebMvcProperties webMvcProperties = new WebMvcProperties();
webMvcProperties.getServlet().setPath("/foo");
RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS); RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS);
assertMatcher(matcher, webMvcProperties).matches("/foo", "/css/file.css"); assertMatcher(matcher, "/foo").matches("/foo", "/css/file.css");
assertMatcher(matcher, webMvcProperties).doesNotMatch("/foo", "/js/file.js"); assertMatcher(matcher, "/foo").doesNotMatch("/foo", "/js/file.js");
} }
@Test @Test
...@@ -96,15 +94,16 @@ public class StaticResourceRequestTests { ...@@ -96,15 +94,16 @@ public class StaticResourceRequestTests {
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
DispatcherServletPath dispatcherServletPath = () -> "";
StaticWebApplicationContext context = new StaticWebApplicationContext(); StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(WebMvcProperties.class); context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath);
return assertThat(new RequestMatcherAssert(context, matcher)); return assertThat(new RequestMatcherAssert(context, matcher));
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String path) {
WebMvcProperties webMvcProperties) { DispatcherServletPath dispatcherServletPath = () -> path;
StaticWebApplicationContext context = new StaticWebApplicationContext(); StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(WebMvcProperties.class, () -> webMvcProperties); context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath);
return assertThat(new RequestMatcherAssert(context, matcher)); return assertThat(new RequestMatcherAssert(context, matcher));
} }
......
...@@ -66,8 +66,7 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -66,8 +66,7 @@ public class DispatcherServletAutoConfigurationTests {
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(ServletRegistrationBean.class); assertThat(context).doesNotHaveBean(ServletRegistrationBean.class);
assertThat(context).doesNotHaveBean(DispatcherServlet.class); assertThat(context).doesNotHaveBean(DispatcherServlet.class);
assertThat(context) assertThat(context).doesNotHaveBean(DispatcherServletPath.class);
.doesNotHaveBean(DispatcherServletPathProvider.class);
}); });
} }
...@@ -77,7 +76,7 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -77,7 +76,7 @@ public class DispatcherServletAutoConfigurationTests {
public void registrationOverrideWithDispatcherServletWrongName() { public void registrationOverrideWithDispatcherServletWrongName() {
this.contextRunner this.contextRunner
.withUserConfiguration(CustomDispatcherServletDifferentName.class, .withUserConfiguration(CustomDispatcherServletDifferentName.class,
CustomDispatcherServletPathProvider.class) CustomDispatcherServletPath.class)
.run((context) -> { .run((context) -> {
ServletRegistrationBean<?> registration = context ServletRegistrationBean<?> registration = context
.getBean(ServletRegistrationBean.class); .getBean(ServletRegistrationBean.class);
...@@ -91,7 +90,7 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -91,7 +90,7 @@ public class DispatcherServletAutoConfigurationTests {
@Test @Test
public void registrationOverrideWithAutowiredServlet() { public void registrationOverrideWithAutowiredServlet() {
this.contextRunner.withUserConfiguration(CustomAutowiredRegistration.class, this.contextRunner.withUserConfiguration(CustomAutowiredRegistration.class,
CustomDispatcherServletPathProvider.class).run((context) -> { CustomDispatcherServletPath.class).run((context) -> {
ServletRegistrationBean<?> registration = context ServletRegistrationBean<?> registration = context
.getBean(ServletRegistrationBean.class); .getBean(ServletRegistrationBean.class);
assertThat(registration.getUrlMappings()).containsExactly("/foo"); assertThat(registration.getUrlMappings()).containsExactly("/foo");
...@@ -111,43 +110,35 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -111,43 +110,35 @@ public class DispatcherServletAutoConfigurationTests {
assertThat(registration.getUrlMappings()) assertThat(registration.getUrlMappings())
.containsExactly("/spring/*"); .containsExactly("/spring/*");
assertThat(registration.getMultipartConfig()).isNull(); assertThat(registration.getMultipartConfig()).isNull();
assertThat(context.getBean(DispatcherServletPathProvider.class) assertThat(context.getBean(DispatcherServletPath.class).getPath())
.getServletPaths()).containsExactly("/spring"); .isEqualTo("/spring");
}); });
} }
@Test @Test
public void pathProviderNotCreatedWhenMultipleDispatcherServletsPresent() { public void dispatcherServletPathWhenCustomDispatcherServletSameNameShouldReturnConfiguredServletPath() {
this.contextRunner
.withUserConfiguration(CustomDispatcherServletDifferentName.class)
.run((context) -> assertThat(context)
.doesNotHaveBean(DispatcherServletPathProvider.class));
}
@Test
public void pathProviderWhenCustomDispatcherServletSameNameShouldReturnConfiguredServletPath() {
this.contextRunner.withUserConfiguration(CustomDispatcherServletSameName.class) this.contextRunner.withUserConfiguration(CustomDispatcherServletSameName.class)
.withPropertyValues("spring.mvc.servlet.path:/spring") .withPropertyValues("spring.mvc.servlet.path:/spring")
.run((context) -> assertThat(context .run((context) -> assertThat(
.getBean(DispatcherServletPathProvider.class).getServletPaths()) context.getBean(DispatcherServletPath.class).getPath())
.containsExactly("/spring")); .isEqualTo("/spring"));
} }
@Test @Test
public void pathProviderNotCreatedWhenDefaultDispatcherServletNotAvailable() { public void dispatcherServletPathNotCreatedWhenDefaultDispatcherServletNotAvailable() {
this.contextRunner this.contextRunner
.withUserConfiguration(CustomDispatcherServletDifferentName.class, .withUserConfiguration(CustomDispatcherServletDifferentName.class,
NonServletConfiguration.class) NonServletConfiguration.class)
.run((context) -> assertThat(context) .run((context) -> assertThat(context)
.doesNotHaveBean(DispatcherServletPathProvider.class)); .doesNotHaveBean(DispatcherServletPath.class));
} }
@Test @Test
public void pathProviderNotCreatedWhenCustomRegistrationBeanPresent() { public void dispatcherServletPathNotCreatedWhenCustomRegistrationBeanPresent() {
this.contextRunner this.contextRunner
.withUserConfiguration(CustomDispatcherServletRegistration.class) .withUserConfiguration(CustomDispatcherServletRegistration.class)
.run((context) -> assertThat(context) .run((context) -> assertThat(context)
.doesNotHaveBean(DispatcherServletPathProvider.class)); .doesNotHaveBean(DispatcherServletPath.class));
} }
@Test @Test
...@@ -237,11 +228,11 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -237,11 +228,11 @@ public class DispatcherServletAutoConfigurationTests {
} }
@Configuration @Configuration
protected static class CustomDispatcherServletPathProvider { protected static class CustomDispatcherServletPath {
@Bean @Bean
public DispatcherServletPathProvider dispatcherServletPathProvider() { public DispatcherServletPath dispatcherServletPath() {
return mock(DispatcherServletPathProvider.class); return mock(DispatcherServletPath.class);
} }
} }
...@@ -259,8 +250,8 @@ public class DispatcherServletAutoConfigurationTests { ...@@ -259,8 +250,8 @@ public class DispatcherServletAutoConfigurationTests {
} }
@Bean @Bean
public DispatcherServletPathProvider dispatcherServletPathProvider() { public DispatcherServletPath dispatcherServletPath() {
return mock(DispatcherServletPathProvider.class); return mock(DispatcherServletPath.class);
} }
} }
......
/*
* 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/*");
}
}
/*
* 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");
}
}
...@@ -20,6 +20,7 @@ import org.junit.Rule; ...@@ -20,6 +20,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; 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.context.runner.WebApplicationContextRunner;
import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.boot.web.servlet.error.ErrorAttributes;
...@@ -39,7 +40,9 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -39,7 +40,9 @@ import static org.assertj.core.api.Assertions.assertThat;
public class ErrorMvcAutoConfigurationTests { public class ErrorMvcAutoConfigurationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(ErrorMvcAutoConfiguration.class)); .withConfiguration(
AutoConfigurations.of(DispatcherServletAutoConfiguration.class,
ErrorMvcAutoConfiguration.class));
@Rule @Rule
public OutputCapture outputCapture = new OutputCapture(); public OutputCapture outputCapture = new OutputCapture();
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -22,6 +22,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; ...@@ -22,6 +22,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; 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.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
...@@ -48,7 +50,7 @@ import org.springframework.web.servlet.DispatcherServlet; ...@@ -48,7 +50,7 @@ import org.springframework.web.servlet.DispatcherServlet;
@Configuration @Configuration
@ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnWebApplication(type = Type.SERVLET)
@AutoConfigureAfter(WebMvcAutoConfiguration.class) @AutoConfigureAfter(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(WebMvcProperties.class) @EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class })
public class MockMvcAutoConfiguration { public class MockMvcAutoConfiguration {
private final WebApplicationContext context; private final WebApplicationContext context;
...@@ -61,6 +63,12 @@ public class MockMvcAutoConfiguration { ...@@ -61,6 +63,12 @@ public class MockMvcAutoConfiguration {
this.webMvcProperties = webMvcProperties; this.webMvcProperties = webMvcProperties;
} }
@Bean
@ConditionalOnMissingBean
public DispatcherServletPath dispatcherServletPath() {
return () -> this.webMvcProperties.getServlet().getPath();
}
@Bean @Bean
@ConditionalOnMissingBean(MockMvcBuilder.class) @ConditionalOnMissingBean(MockMvcBuilder.class)
public DefaultMockMvcBuilder mockMvcBuilder( public DefaultMockMvcBuilder mockMvcBuilder(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment