Allow endpoints to be mapped to /

This commit removes the restriction that was added in 4a61e45 to
prevent / from being used as the management context path when the
management context was not using a different port

The management context path can now be set to / irrespective of the
configuration of the management port. To avoid a possible clash
with the application's welcome page or similar, the links "endpoint"
that is mapping to the management context path is disabled when
the management context path is /.

As part of allowing / to be used as the management context path again,
the handling of endpoint mappings and the creation of paths for
individual operations has been consolidated into a new EndpointMapping
class that is used across the three (MVC, WebFlux, and Jersey)
implementations.

See gh-9898
This commit is contained in:
Andy Wilkinson
2017-09-08 00:12:09 +01:00
parent 4c0a70ced6
commit c06de245d9
18 changed files with 291 additions and 65 deletions

View File

@@ -82,7 +82,6 @@ public class ManagementContextAutoConfiguration {
@Override
public void afterSingletonsInstantiated() {
verifySslConfiguration();
verifyContextPathConfiguration();
if (this.environment instanceof ConfigurableEnvironment) {
addLocalManagementPortPropertyAlias(
(ConfigurableEnvironment) this.environment);
@@ -97,15 +96,6 @@ public class ManagementContextAutoConfiguration {
+ "server is not listening on a separate port");
}
private void verifyContextPathConfiguration() {
String contextPath = this.environment.getProperty("management.context-path");
if ("".equals(contextPath) || "/".equals(contextPath)) {
throw new IllegalStateException("A management context path of '"
+ contextPath + "' requires the management server to be "
+ "listening on a separate port");
}
}
/**
* Add an alias for 'local.management.port' that actually resolves using
* 'local.server.port'.

View File

@@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
import org.springframework.boot.endpoint.web.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.endpoint.web.mvc.WebEndpointServletHandlerMapping;
@@ -66,7 +67,8 @@ class WebEndpointInfrastructureManagementContextConfiguration {
ManagementServerProperties managementServerProperties) {
return (resourceConfig) -> resourceConfig.registerResources(new HashSet<>(
new JerseyEndpointResourceFactory().createEndpointResources(
managementServerProperties.getContextPath(),
new EndpointMapping(
managementServerProperties.getContextPath()),
provider.getEndpoints())));
}
@@ -93,8 +95,8 @@ class WebEndpointInfrastructureManagementContextConfiguration {
CorsEndpointProperties corsProperties,
ManagementServerProperties managementServerProperties) {
WebEndpointServletHandlerMapping handlerMapping = new WebEndpointServletHandlerMapping(
managementServerProperties.getContextPath(), provider.getEndpoints(),
getCorsConfiguration(corsProperties));
new EndpointMapping(managementServerProperties.getContextPath()),
provider.getEndpoints(), getCorsConfiguration(corsProperties));
for (WebEndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(handlerMapping);
}
@@ -137,7 +139,8 @@ class WebEndpointInfrastructureManagementContextConfiguration {
EndpointProvider<WebEndpointOperation> provider,
ManagementServerProperties managementServerProperties) {
return new WebEndpointReactiveHandlerMapping(
managementServerProperties.getContextPath(), provider.getEndpoints());
new EndpointMapping(managementServerProperties.getContextPath()),
provider.getEndpoints());
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
@@ -69,8 +70,9 @@ public class CloudFoundryActuatorAutoConfiguration {
EndpointProvider<WebEndpointOperation> provider, Environment environment,
RestTemplateBuilder builder) {
return new CloudFoundryWebEndpointServletHandlerMapping(
"/cloudfoundryapplication", provider.getEndpoints(),
getCorsConfiguration(), getSecurityInterceptor(builder, environment));
new EndpointMapping("/cloudfoundryapplication"),
provider.getEndpoints(), getCorsConfiguration(),
getSecurityInterceptor(builder, environment));
}
private CloudFoundrySecurityInterceptor getSecurityInterceptor(

View File

@@ -35,6 +35,7 @@ import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.ParameterMappingException;
import org.springframework.boot.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.Link;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
import org.springframework.boot.endpoint.web.WebEndpointResponse;
@@ -72,11 +73,11 @@ class CloudFoundryWebEndpointServletHandlerMapping
private final EndpointLinksResolver endpointLinksResolver = new EndpointLinksResolver();
CloudFoundryWebEndpointServletHandlerMapping(String endpointPath,
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration,
CloudFoundrySecurityInterceptor securityInterceptor) {
super(endpointPath, webEndpoints, corsConfiguration);
super(endpointMapping, webEndpoints, corsConfiguration);
this.securityInterceptor = securityInterceptor;
}

View File

@@ -79,7 +79,7 @@ public class CloudFoundryActuatorAutoConfigurationTests {
@Test
public void cloudFoundryPlatformActive() throws Exception {
CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping();
assertThat(handlerMapping.getEndpointPath())
assertThat(handlerMapping.getEndpointMapping().getPath())
.isEqualTo("/cloudfoundryapplication");
CorsConfiguration corsConfiguration = (CorsConfiguration) ReflectionTestUtils
.getField(handlerMapping, "corsConfiguration");

View File

@@ -31,6 +31,7 @@ import org.springframework.boot.endpoint.OperationParameterMapper;
import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.endpoint.Selector;
import org.springframework.boot.endpoint.WriteOperation;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
@@ -196,7 +197,8 @@ public class CloudFoundryMvcWebEndpointIntegrationTests {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new CloudFoundryWebEndpointServletHandlerMapping("/cfApplication",
return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cfApplication"),
webEndpointDiscoverer.discoverEndpoints(), corsConfiguration,
interceptor);
}

View File

@@ -23,6 +23,7 @@ import org.junit.Test;
import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.OperationType;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.endpoint.web.WebEndpointHttpMethod;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
@@ -138,7 +139,8 @@ public class RequestMappingEndpointTests {
WebEndpointOperation operation = new WebEndpointOperation(OperationType.READ,
(arguments) -> "Invoked", true, requestPredicate, "test");
WebEndpointServletHandlerMapping mapping = new WebEndpointServletHandlerMapping(
"application", Collections.singleton(new EndpointInfo<>("test", true,
new EndpointMapping("application"),
Collections.singleton(new EndpointInfo<>("test", true,
Collections.singleton(operation))));
mapping.setApplicationContext(new StaticApplicationContext());
mapping.afterPropertiesSet();