Merge branch '2.0.x'

pull/13616/head
Madhura Bhave 6 years ago
commit 4eff07f6c0

@ -16,6 +16,9 @@
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;
@ -31,6 +34,7 @@ import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathP
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;
/** /**
@ -70,14 +74,27 @@ public class ServletEndpointManagementContextConfiguration {
ServletEndpointsSupplier servletEndpointsSupplier) { ServletEndpointsSupplier servletEndpointsSupplier) {
DispatcherServletPathProvider servletPathProvider = this.context DispatcherServletPathProvider servletPathProvider = this.context
.getBean(DispatcherServletPathProvider.class); .getBean(DispatcherServletPathProvider.class);
String servletPath = servletPathProvider.getServletPath(); Set<String> cleanedPaths = getServletPaths(properties, servletPathProvider);
if (servletPath.equals("/")) { return new ServletEndpointRegistrar(cleanedPaths,
servletPath = "";
}
return new ServletEndpointRegistrar(servletPath + properties.getBasePath(),
servletEndpointsSupplier.getEndpoints()); servletEndpointsSupplier.getEndpoints());
} }
private Set<String> getServletPaths(WebEndpointProperties properties,
DispatcherServletPathProvider servletPathProvider) {
Set<String> servletPaths = servletPathProvider.getServletPaths();
return servletPaths.stream().map((p) -> {
String path = cleanServletPath(p);
return path + 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

@ -137,10 +137,9 @@ public final class EndpointRequest {
private RequestMatcher createDelegate(WebApplicationContext context) { private RequestMatcher createDelegate(WebApplicationContext context) {
try { try {
String servletPath = getServletPath(context); Set<String> servletPaths = getServletPaths(context);
RequestMatcherFactory requestMatcherFactory = (StringUtils RequestMatcherFactory requestMatcherFactory = new RequestMatcherFactory(
.hasText(servletPath) ? new RequestMatcherFactory(servletPath) servletPaths);
: RequestMatcherFactory.withEmptyServletPath());
return createDelegate(context, requestMatcherFactory); return createDelegate(context, requestMatcherFactory);
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
@ -148,13 +147,13 @@ public final class EndpointRequest {
} }
} }
private String getServletPath(WebApplicationContext context) { private Set<String> getServletPaths(WebApplicationContext context) {
try { try {
return context.getBean(DispatcherServletPathProvider.class) return context.getBean(DispatcherServletPathProvider.class)
.getServletPath(); .getServletPaths();
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
return ""; return Collections.singleton("");
} }
} }
@ -226,7 +225,7 @@ public final class EndpointRequest {
requestMatcherFactory, paths); requestMatcherFactory, paths);
if (this.includeLinks if (this.includeLinks
&& StringUtils.hasText(pathMappedEndpoints.getBasePath())) { && StringUtils.hasText(pathMappedEndpoints.getBasePath())) {
delegateMatchers.add( delegateMatchers.addAll(
requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath())); requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath()));
} }
return new OrRequestMatcher(delegateMatchers); return new OrRequestMatcher(delegateMatchers);
@ -259,7 +258,8 @@ 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()
.map((path) -> requestMatcherFactory.antPath(path, "/**")) .flatMap(
(path) -> requestMatcherFactory.antPath(path, "/**").stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -276,7 +276,9 @@ 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())) {
return requestMatcherFactory.antPath(properties.getBasePath()); List<RequestMatcher> matchers = requestMatcherFactory
.antPath(properties.getBasePath());
return new OrRequestMatcher(matchers);
} }
return EMPTY_MATCHER; return EMPTY_MATCHER;
} }
@ -288,25 +290,27 @@ public final class EndpointRequest {
*/ */
private static class RequestMatcherFactory { private static class RequestMatcherFactory {
private final String servletPath; private final Set<String> servletPaths = new LinkedHashSet<>();
private static final RequestMatcherFactory EMPTY_SERVLET_PATH = new RequestMatcherFactory(
"");
RequestMatcherFactory(String servletPath) {
this.servletPath = servletPath;
}
RequestMatcher antPath(String... parts) { RequestMatcherFactory(Set<String> servletPaths) {
String pattern = (this.servletPath.equals("/") ? "" : this.servletPath); this.servletPaths.addAll(servletPaths);
for (String part : parts) {
pattern += part;
}
return new AntPathRequestMatcher(pattern);
} }
static RequestMatcherFactory withEmptyServletPath() { List<RequestMatcher> antPath(String... parts) {
return EMPTY_SERVLET_PATH; List<RequestMatcher> matchers = new ArrayList<>();
this.servletPaths.stream().map((p) -> {
if (StringUtils.hasText(p)) {
return p;
}
return "";
}).distinct().forEach((path) -> {
String pattern = (path.equals("/") ? "" : path);
for (String part : parts) {
pattern += part;
}
matchers.add(new AntPathRequestMatcher(pattern));
});
return matchers;
} }
} }

@ -16,6 +16,8 @@
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;
@ -95,7 +97,7 @@ class WebMvcEndpointChildContextConfiguration {
@Bean @Bean
public DispatcherServletPathProvider childDispatcherServletPathProvider() { public DispatcherServletPathProvider childDispatcherServletPathProvider() {
return () -> ""; return () -> Collections.singleton("");
} }
} }

@ -17,6 +17,8 @@
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;
@ -48,18 +50,22 @@ 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);
String basePath = (String) ReflectionTestUtils.getField(bean, "basePath"); Set<String> basePaths = (Set<String>) ReflectionTestUtils.getField(bean,
assertThat(basePath).isEqualTo("/test/actuator"); "basePaths");
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);
@ -67,8 +73,9 @@ 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);
String basePath = (String) ReflectionTestUtils.getField(bean, "basePath"); Set<String> basePaths = (Set<String>) ReflectionTestUtils.getField(bean,
assertThat(basePath).isEqualTo("/actuator"); "basePaths");
assertThat(basePaths).containsExactly("/actuator");
}); });
} }
@ -91,7 +98,13 @@ public class ServletEndpointManagementContextConfigurationTests {
@Bean @Bean
public DispatcherServletPathProvider servletPathProvider() { public DispatcherServletPathProvider servletPathProvider() {
return () -> "/test"; return () -> {
Set<String> paths = new LinkedHashSet<>();
paths.add("/");
paths.add("/test");
paths.add("/foo/");
return paths;
};
} }
} }

@ -17,6 +17,8 @@
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;
@ -76,23 +78,23 @@ public class EndpointRequestTests {
@Test @Test
public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() { public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", assertMatcher(matcher, "/actuator", "/spring", "/admin").matches("/actuator/foo",
"/actuator/foo"); "/spring", "/admin");
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", assertMatcher(matcher, "/actuator", "/spring", "/admin").matches("/actuator/bar",
"/actuator/bar"); "/spring", "/admin");
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator"); assertMatcher(matcher, "/actuator", "/spring").matches("/actuator", "/spring");
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring", assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/actuator/baz",
"/actuator/baz"); "/spring");
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo"); assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/actuator/foo", "");
} }
@Test @Test
public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() { public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator", null).matches("/actuator/foo"); assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/foo");
assertMatcher(matcher, "/actuator", null).matches("/actuator/bar"); assertMatcher(matcher, "/actuator", (String) null).matches("/actuator/bar");
assertMatcher(matcher, "/actuator", null).matches("/actuator"); assertMatcher(matcher, "/actuator", (String) null).matches("/actuator");
assertMatcher(matcher, "/actuator", null).doesNotMatch("/actuator/baz"); assertMatcher(matcher, "/actuator", (String) null).doesNotMatch("/actuator/baz");
} }
@Test @Test
@ -219,8 +221,8 @@ public class EndpointRequestTests {
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath,
String servletPath) { String... servletPaths) {
return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath); return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPaths);
} }
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
@ -243,7 +245,7 @@ public class EndpointRequestTests {
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
PathMappedEndpoints pathMappedEndpoints, String servletPath) { PathMappedEndpoints pathMappedEndpoints, String... servletPaths) {
StaticWebApplicationContext context = new StaticWebApplicationContext(); StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(WebEndpointProperties.class); context.registerBean(WebEndpointProperties.class);
if (pathMappedEndpoints != null) { if (pathMappedEndpoints != null) {
@ -254,8 +256,9 @@ public class EndpointRequestTests {
properties.setBasePath(pathMappedEndpoints.getBasePath()); properties.setBasePath(pathMappedEndpoints.getBasePath());
} }
} }
if (servletPath != null) { if (servletPaths != null) {
DispatcherServletPathProvider pathProvider = () -> servletPath; DispatcherServletPathProvider pathProvider = () -> new LinkedHashSet<>(
Arrays.asList(servletPaths));
context.registerBean(DispatcherServletPathProvider.class, () -> pathProvider); context.registerBean(DispatcherServletPathProvider.class, () -> pathProvider);
} }
return assertThat(new RequestMatcherAssert(context, matcher)); return assertThat(new RequestMatcherAssert(context, matcher));
@ -276,8 +279,8 @@ public class EndpointRequestTests {
matches(mockRequest(servletPath)); matches(mockRequest(servletPath));
} }
public void matches(String servletPath, String pathInfo) { public void matches(String pathInfo, String... servletPaths) {
matches(mockRequest(servletPath, pathInfo)); Arrays.stream(servletPaths).forEach((p) -> matches(mockRequest(p, pathInfo)));
} }
private void matches(HttpServletRequest request) { private void matches(HttpServletRequest request) {

@ -68,8 +68,8 @@ public class WebMvcEndpointChildContextConfigurationTests {
this.contextRunner this.contextRunner
.withUserConfiguration(WebMvcEndpointChildContextConfiguration.class) .withUserConfiguration(WebMvcEndpointChildContextConfiguration.class)
.run((context) -> assertThat(context .run((context) -> assertThat(context
.getBean(DispatcherServletPathProvider.class).getServletPath()) .getBean(DispatcherServletPathProvider.class).getServletPaths())
.isEmpty()); .containsExactly(""));
} }
static class ExistingConfig { static class ExistingConfig {

@ -17,6 +17,8 @@
package org.springframework.boot.actuate.endpoint.web; package org.springframework.boot.actuate.endpoint.web;
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;
@ -27,26 +29,38 @@ 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
* endpoints}. * endpoints}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
*/ */
public class ServletEndpointRegistrar implements ServletContextInitializer { 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 String basePath; private final Set<String> basePaths = new LinkedHashSet<>();
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.basePath = (basePath != null ? basePath : ""); this.basePaths.add((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;
} }
@ -59,14 +73,20 @@ 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());
registration.addMapping(urlMapping); registration.addMapping(getUrlMappings(endpoint.getRootPath(), name));
registration.setInitParameters(endpointServlet.getInitParameters()); registration.setInitParameters(endpointServlet.getInitParameters());
logger.info("Registered '" + path + "' to " + name); }
private String[] getUrlMappings(String endpointPath, String name) {
return this.basePaths.stream()
.map((bp) -> (bp != null ? bp + "/" + endpointPath : "/" + endpointPath))
.distinct().map((p) -> {
logger.info("Registered '" + p + "' to " + name);
return (p.endsWith("/") ? p + "*" : p + "/*");
}).toArray(String[]::new);
} }
} }

@ -18,6 +18,8 @@ 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;
@ -47,6 +49,7 @@ 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 {
@ -73,14 +76,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(null, null); new ServletEndpointRegistrar((String) 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(null, ServletEndpointRegistrar registrar = new ServletEndpointRegistrar((String) 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"),
@ -102,6 +105,64 @@ 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 {

@ -17,6 +17,7 @@
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;
@ -151,8 +152,9 @@ public class DispatcherServletAutoConfiguration {
@ConditionalOnMissingBean(DispatcherServletPathProvider.class) @ConditionalOnMissingBean(DispatcherServletPathProvider.class)
@ConditionalOnSingleCandidate(DispatcherServlet.class) @ConditionalOnSingleCandidate(DispatcherServlet.class)
public DispatcherServletPathProvider dispatcherServletPathProvider() { public DispatcherServletPathProvider dispatcherServletPathProvider() {
return () -> DispatcherServletRegistrationConfiguration.this.webMvcProperties return () -> Collections.singleton(
.getServlet().getPath(); DispatcherServletRegistrationConfiguration.this.webMvcProperties
.getServlet().getPath());
} }
} }

@ -16,11 +16,13 @@
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;
/** /**
* Interface that provides the path of the {@link DispatcherServlet} in an application * Interface that provides the paths that the {@link DispatcherServlet} in an application
* context. * context is mapped to.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.2 * @since 2.0.2
@ -28,6 +30,6 @@ import org.springframework.web.servlet.DispatcherServlet;
@FunctionalInterface @FunctionalInterface
public interface DispatcherServletPathProvider { public interface DispatcherServletPathProvider {
String getServletPath(); Set<String> getServletPaths();
} }

@ -112,7 +112,7 @@ public class DispatcherServletAutoConfigurationTests {
.containsExactly("/spring/*"); .containsExactly("/spring/*");
assertThat(registration.getMultipartConfig()).isNull(); assertThat(registration.getMultipartConfig()).isNull();
assertThat(context.getBean(DispatcherServletPathProvider.class) assertThat(context.getBean(DispatcherServletPathProvider.class)
.getServletPath()).isEqualTo("/spring"); .getServletPaths()).containsExactly("/spring");
}); });
} }
@ -129,8 +129,8 @@ public class DispatcherServletAutoConfigurationTests {
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(context
.getBean(DispatcherServletPathProvider.class).getServletPath()) .getBean(DispatcherServletPathProvider.class).getServletPaths())
.isEqualTo("/spring")); .containsExactly("/spring"));
} }
@Test @Test

Loading…
Cancel
Save