Support multiple paths in DispatcherServletPathProvider
Closes gh-13603
This commit is contained in:
parent
43ea78f2ce
commit
fddc9e9c7e
|
@ -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(Set<String> servletPaths) {
|
||||||
"");
|
this.servletPaths.addAll(servletPaths);
|
||||||
|
|
||||||
RequestMatcherFactory(String servletPath) {
|
|
||||||
this.servletPath = servletPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestMatcher antPath(String... parts) {
|
List<RequestMatcher> antPath(String... parts) {
|
||||||
String pattern = (this.servletPath.equals("/") ? "" : this.servletPath);
|
List<RequestMatcher> matchers = new ArrayList<>();
|
||||||
for (String part : parts) {
|
this.servletPaths.stream().map((p) -> {
|
||||||
pattern += part;
|
if (StringUtils.hasText(p)) {
|
||||||
}
|
return p;
|
||||||
return new AntPathRequestMatcher(pattern);
|
}
|
||||||
}
|
return "";
|
||||||
|
}).distinct().forEach((path) -> {
|
||||||
static RequestMatcherFactory withEmptyServletPath() {
|
String pattern = (path.equals("/") ? "" : path);
|
||||||
return EMPTY_SERVLET_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;
|
||||||
|
@ -160,8 +161,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.serverProperties
|
return () -> Collections.singleton(
|
||||||
.getServlet().getPath();
|
DispatcherServletRegistrationConfiguration.this.serverProperties
|
||||||
|
.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("server.servlet.path:/spring")
|
.withPropertyValues("server.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…
Reference in New Issue