diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java index 87d8eccc28..bebab073d6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java @@ -82,7 +82,7 @@ public class CloudFoundryActuatorAutoConfiguration { RestTemplateBuilder builder) { WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, parameterMapper, cachingConfigurationFactory, - endpointMediaTypes); + endpointMediaTypes, (id) -> id); return new CloudFoundryWebEndpointServletHandlerMapping( new EndpointMapping("/cloudfoundryapplication"), endpointDiscoverer.discoverEndpoints(), endpointMediaTypes, diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java new file mode 100644 index 0000000000..ef521532f3 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2017 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.actuate.autoconfigure.endpoint; + +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; +import org.springframework.core.env.Environment; + +/** + * Default {@link EndpointPathResolver} implementation that use the + * {@link Environment} to determine if an endpoint has a custom path. + * + * @author Stephane Nicoll + */ +class DefaultEndpointPathResolver implements EndpointPathResolver { + + private final Environment environment; + + DefaultEndpointPathResolver(Environment environment) { + this.environment = environment; + } + + @Override + public String resolvePath(String endpointId) { + String key = String.format("endpoints.%s.web.path", endpointId); + return this.environment.getProperty(key, String.class, endpointId); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.java index 35863b6a10..fea91af2e5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFacto import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -77,14 +78,22 @@ public class EndpointAutoConfiguration { return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES); } + @Bean + @ConditionalOnMissingBean + public EndpointPathResolver endpointPathResolver( + Environment environment) { + return new DefaultEndpointPathResolver(environment); + } + @Bean public EndpointProvider webEndpointProvider( OperationParameterMapper parameterMapper, - DefaultCachingConfigurationFactory cachingConfigurationFactory) { + DefaultCachingConfigurationFactory cachingConfigurationFactory, + EndpointPathResolver endpointPathResolver) { Environment environment = this.applicationContext.getEnvironment(); WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, parameterMapper, cachingConfigurationFactory, - endpointMediaTypes()); + endpointMediaTypes(), endpointPathResolver); return new EndpointProvider<>(environment, endpointDiscoverer, EndpointExposure.WEB); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java index 8c0f729b7c..670ce82222 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java @@ -219,6 +219,22 @@ public class CloudFoundryActuatorAutoConfigurationTests { assertThat(endpoints.get(0).getId()).isEqualTo("test"); } + @Test + public void endpointPathCustomizationIsNotApplied() + throws Exception { + TestPropertyValues.of("endpoints.test.web.path=another/custom") + .applyTo(this.context); + this.context.register(TestConfiguration.class); + this.context.refresh(); + CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping(); + List> endpoints = (List>) handlerMapping + .getEndpoints(); + assertThat(endpoints.size()).isEqualTo(1); + assertThat(endpoints.get(0).getOperations()).hasSize(1); + assertThat(endpoints.get(0).getOperations().iterator().next() + .getRequestPredicate().getPath()).isEqualTo("test"); + } + private CloudFoundryWebEndpointServletHandlerMapping getHandlerMapping() { TestPropertyValues .of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryMvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryMvcWebEndpointIntegrationTests.java index 6500dcb418..1199f0da8f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryMvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryMvcWebEndpointIntegrationTests.java @@ -219,7 +219,7 @@ public class CloudFoundryMvcWebEndpointIntegrationTests { DefaultConversionService.getSharedInstance()); return new WebAnnotationEndpointDiscoverer(applicationContext, parameterMapper, (id) -> new CachingConfiguration(0), - endpointMediaTypes); + endpointMediaTypes, (id) -> id); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java new file mode 100644 index 0000000000..3e2c10fbaa --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2017 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.actuate.autoconfigure.endpoint; + +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DefaultEndpointPathResolver}. + * + * @author Stephane Nicoll + */ +public class DefaultEndpointPathResolverTests { + + private final MockEnvironment environment = new MockEnvironment(); + + private final EndpointPathResolver resolver = new DefaultEndpointPathResolver( + this.environment); + + @Test + public void defaultConfiguration() { + assertThat(this.resolver.resolvePath("test")).isEqualTo("test"); + } + + @Test + public void userConfiguration() { + this.environment.setProperty("endpoints.test.web.path", "custom"); + assertThat(this.resolver.resolvePath("test")).isEqualTo("custom"); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointPathResolver.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointPathResolver.java new file mode 100644 index 0000000000..073ef44795 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointPathResolver.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2017 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.actuate.endpoint.web; + +/** + * Resolve the path of an endpoint. + * + * @author Stephane Nicoll + * @since 2.0.0 + */ +@FunctionalInterface +public interface EndpointPathResolver { + + /** + * Resolve the path for the endpoint with the specified {@code endpointId}. + * @param endpointId the id of an endpoint + * @return the path of the endpoint + */ + String resolvePath(String endpointId); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebAnnotationEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebAnnotationEndpointDiscoverer.java index d83199ffb5..ae949d753c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebAnnotationEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebAnnotationEndpointDiscoverer.java @@ -40,6 +40,7 @@ import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; @@ -71,14 +72,17 @@ public class WebAnnotationEndpointDiscoverer extends * @param cachingConfigurationFactory the {@link CachingConfiguration} factory to use * @param endpointMediaTypes the media types produced and consumed by web endpoint * operations + * @param endpointPathResolver the {@link EndpointPathResolver} used to resolve + * endpoint paths */ public WebAnnotationEndpointDiscoverer(ApplicationContext applicationContext, OperationParameterMapper operationParameterMapper, CachingConfigurationFactory cachingConfigurationFactory, - EndpointMediaTypes endpointMediaTypes) { + EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver) { super(applicationContext, new WebEndpointOperationFactory(operationParameterMapper, - endpointMediaTypes), + endpointMediaTypes, endpointPathResolver), WebEndpointOperation::getRequestPredicate, cachingConfigurationFactory); } @@ -121,10 +125,14 @@ public class WebAnnotationEndpointDiscoverer extends private final EndpointMediaTypes endpointMediaTypes; + private final EndpointPathResolver endpointPathResolver; + private WebEndpointOperationFactory(OperationParameterMapper parameterMapper, - EndpointMediaTypes endpointMediaTypes) { + EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver) { this.parameterMapper = parameterMapper; this.endpointMediaTypes = endpointMediaTypes; + this.endpointPathResolver = endpointPathResolver; } @Override @@ -147,7 +155,8 @@ public class WebAnnotationEndpointDiscoverer extends } private String determinePath(String endpointId, Method operationMethod) { - StringBuilder path = new StringBuilder(endpointId); + StringBuilder path = new StringBuilder( + this.endpointPathResolver.resolvePath(endpointId)); Stream.of(operationMethod.getParameters()) .filter(( parameter) -> parameter.getAnnotation(Selector.class) != null) diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/AbstractWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/AbstractWebEndpointIntegrationTests.java index 218ce4acca..10af033861 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/AbstractWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/AbstractWebEndpointIntegrationTests.java @@ -385,7 +385,7 @@ public abstract class AbstractWebEndpointIntegrationTests new CachingConfiguration(0), - endpointMediaTypes()); + endpointMediaTypes(), (id) -> id); } @Bean diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebAnnotationEndpointDiscovererTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebAnnotationEndpointDiscovererTests.java index 928df7d99f..8c76333869 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebAnnotationEndpointDiscovererTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebAnnotationEndpointDiscovererTests.java @@ -189,8 +189,8 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsCachedWithMatchingId() { - load((id) -> new CachingConfiguration(500), TestEndpointConfiguration.class, - (discoverer) -> { + load((id) -> new CachingConfiguration(500), (id) -> id, + TestEndpointConfiguration.class, (discoverer) -> { Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); @@ -237,12 +237,29 @@ public class WebAnnotationEndpointDiscovererTests { }); } + @Test + public void endpointPathCanBeCustomized() { + load((id) -> null, (id) -> "custom/" + id, + AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> { + Map> endpoints = mapEndpoints( + discoverer.discoverEndpoints()); + assertThat(endpoints).containsOnlyKeys("test"); + EndpointInfo endpoint = endpoints.get("test"); + assertThat(requestPredicates(endpoint)).has(requestPredicates( + path("custom/test").httpMethod(WebEndpointHttpMethod.GET).consumes() + .produces("application/json"), + path("custom/test/{id}").httpMethod(WebEndpointHttpMethod.GET).consumes() + .produces("application/json"))); + }); + } + private void load(Class configuration, Consumer consumer) { - this.load((id) -> null, configuration, consumer); + this.load((id) -> null, (id) -> id, configuration, consumer); } private void load(CachingConfigurationFactory cachingConfigurationFactory, + EndpointPathResolver endpointPathResolver, Class configuration, Consumer consumer) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( configuration); @@ -254,7 +271,8 @@ public class WebAnnotationEndpointDiscovererTests { cachingConfigurationFactory, new EndpointMediaTypes( Collections.singletonList("application/json"), - Collections.singletonList("application/json")))); + Collections.singletonList("application/json")), + endpointPathResolver)); } finally { context.close(); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java index 0d9df61e7b..b294c3e08e 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java @@ -99,7 +99,7 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner { WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceOperationParameterMapper(), (id) -> null, - endpointMediaTypes); + endpointMediaTypes, (id) -> id); Collection resources = new JerseyEndpointResourceFactory() .createEndpointResources(new EndpointMapping("/application"), discoverer.discoverEndpoints(), endpointMediaTypes); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java index e6bbf51ce0..415106bed6 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java @@ -105,7 +105,7 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner { WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceOperationParameterMapper(), (id) -> null, - endpointMediaTypes); + endpointMediaTypes, (id) -> id); return new WebFluxEndpointHandlerMapping(new EndpointMapping("/application"), discoverer.discoverEndpoints(), endpointMediaTypes, new CorsConfiguration()); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java index 3d5c6bf75c..f19a6a7eeb 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java @@ -88,7 +88,7 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner { WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceOperationParameterMapper(), (id) -> null, - endpointMediaTypes); + endpointMediaTypes, (id) -> id); return new WebMvcEndpointHandlerMapping(new EndpointMapping("/application"), discoverer.discoverEndpoints(), endpointMediaTypes, new CorsConfiguration()); diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 72c3ef2313..628d762a08 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -1077,18 +1077,21 @@ content into your application; rather pick only the properties that you need. endpoints.auditevents.enabled= # Enable the auditevents endpoint. endpoints.auditevents.jmx.enabled= # Expose the auditevents endpoint as a JMX MBean. endpoints.auditevents.web.enabled= # Expose the auditevents endpoint as a Web endpoint. + endpoints.auditevents.web.path=auditevents # Path of the auditevents endpoint. # AUTO-CONFIGURATION REPORT ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/condition/AutoConfigurationReportEndpoint.{sc-ext}[AutoConfigurationReportEndpoint]) endpoints.autoconfig.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.autoconfig.enabled= # Enable the autoconfig endpoint. endpoints.autoconfig.jmx.enabled= # Expose the autoconfig endpoint as a JMX MBean. endpoints.autoconfig.web.enabled= # Expose the autoconfig endpoint as a Web endpoint. + endpoints.autoconfig.web.path=autoconfig # Path of the autoconfig endpoint. # BEANS ENDPOINT ({sc-spring-boot-actuator}/beans/BeansEndpoint.{sc-ext}[BeansEndpoint]) endpoints.beans.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.beans.enabled= # Enable the beans endpoint. endpoints.beans.jmx.enabled= # Expose the beans endpoint as a JMX MBean. endpoints.beans.web.enabled= # Expose the beans endpoint as a Web endpoint. + endpoints.beans.web.path=beans # Path of the beans endpoint. # CONFIGURATION PROPERTIES REPORT ENDPOINT ({sc-spring-boot-actuator}/context/properties/ConfigurationPropertiesReportEndpoint.{sc-ext}[ConfigurationPropertiesReportEndpoint]) endpoints.configprops.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. @@ -1096,6 +1099,7 @@ content into your application; rather pick only the properties that you need. endpoints.configprops.jmx.enabled= # Expose the configprops endpoint as a JMX MBean. endpoints.configprops.keys-to-sanitize=password,secret,key,token,.*credentials.*,vcap_services # Keys that should be sanitized. Keys can be simple strings that the property ends with or regular expressions. endpoints.configprops.web.enabled= # Expose the configprops endpoint as a Web endpoint. + endpoints.configprops.web.path=configprops # Path of the configprops endpoint. # ENDPOINT DEFAULT SETTINGS endpoints.default.enabled=true # Enable all endpoints by default. @@ -1108,94 +1112,110 @@ content into your application; rather pick only the properties that you need. endpoints.env.jmx.enabled= # Expose the env endpoint as a JMX MBean. endpoints.env.keys-to-sanitize=password,secret,key,token,.*credentials.*,vcap_services # Keys that should be sanitized. Keys can be simple strings that the property ends with or regular expressions. endpoints.env.web.enabled= # Expose the env endpoint as a Web endpoint. + endpoints.env.web.path=env # Path of the env endpoint. # FLYWAY ENDPOINT ({sc-spring-boot-actuator}/flyway/FlywayEndpoint.{sc-ext}[FlywayEndpoint]) endpoints.flyway.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.flyway.enabled= # Enable the flyway endpoint. endpoints.flyway.jmx.enabled= # Expose the flyway endpoint as a JMX MBean. endpoints.flyway.web.enabled= # Expose the flyway endpoint as a Web endpoint. + endpoints.flyway.web.path=flyway # Path of the flyway endpoint. # HEALTH ENDPOINT ({sc-spring-boot-actuator}/health/HealthEndpoint.{sc-ext}[HealthEndpoint]) endpoints.health.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.health.enabled= # Enable the health endpoint. endpoints.health.jmx.enabled= # Expose the health endpoint as a JMX MBean. endpoints.health.web.enabled= # Expose the health endpoint as a Web endpoint. + endpoints.health.web.path=health # Path of the health endpoint. # HEAP DUMP ENDPOINT ({sc-spring-boot-actuator}/management/HeapDumpWebEndpoint.{sc-ext}[HeapDumpWebEndpoint]) endpoints.heapdump.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.heapdump.enabled= # Enable the heapdump endpoint. endpoints.heapdump.web.enabled= # Expose the heapdump endpoint as a Web endpoint. + endpoints.heapdump.web.path=heapdump # Path of the heapdump endpoint. # INFO ENDPOINT ({sc-spring-boot-actuator}/info/InfoEndpoint.{sc-ext}[InfoEndpoint]) endpoints.info.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.info.enabled=true # Enable the info endpoint. endpoints.info.jmx.enabled=true # Expose the info endpoint as a JMX MBean. endpoints.info.web.enabled=true # Expose the info endpoint as a Web endpoint. + endpoints.info.web.path=info # Path of the info endpoint. # LIQUIBASE ENDPOINT ({sc-spring-boot-actuator}/liquibase/LiquibaseEndpoint.{sc-ext}[LiquibaseEndpoint]) endpoints.liquibase.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.liquibase.enabled= # Enable the liquibase endpoint. endpoints.liquibase.jmx.enabled= # Expose the liquibase endpoint as a JMX MBean. endpoints.liquibase.web.enabled= # Expose the liquibase endpoint as a Web endpoint. + endpoints.liquibase.web.path=liquibase # Path of the liquibase endpoint. # LOG FILE ENDPOINT ({sc-spring-boot-actuator}/logging/LogFileWebEndpoint.{sc-ext}[LogFileWebEndpoint]) endpoints.logfile.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.logfile.enabled= # Enable the logfile endpoint. endpoints.logfile.external-file= # External Logfile to be accessed. Can be used if the logfile is written by output redirect and not by the logging system itself. endpoints.logfile.web.enabled= # Expose the logfile endpoint as a Web endpoint. + endpoints.logfile.web.path=logfile # Path of the logfile endpoint. # LOGGERS ENDPOINT ({sc-spring-boot-actuator}/logging/LoggersEndpoint.{sc-ext}[LoggersEndpoint]) endpoints.loggers.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.loggers.enabled= # Enable the loggers endpoint. endpoints.loggers.jmx.enabled= # Expose the loggers endpoint as a JMX MBean. endpoints.loggers.web.enabled= # Expose the loggers endpoint as a Web endpoint. + endpoints.loggers.web.path=loggers # Path of the loggers endpoint. # REQUEST MAPPING ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/web/servlet/RequestMappingEndpoint.{sc-ext}[RequestMappingEndpoint]) endpoints.mappings.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.mappings.enabled= # Enable the mappings endpoint. endpoints.mappings.jmx.enabled= # Expose the mappings endpoint as a JMX MBean. endpoints.mappings.web.enabled= # Expose the mappings endpoint as a Web endpoint. + endpoints.mappings.web.path=mappings # Path of the mappings endpoint. # METRICS ENDPOINT ({sc-spring-boot-actuator}/metrics/MetricsEndpoint.{sc-ext}[MetricsEndpoint]) endpoints.metrics.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.metrics.enabled= # Enable the metrics endpoint. endpoints.metrics.jmx.enabled= # Expose the metrics endpoint as a JMX MBean. endpoints.metrics.web.enabled= # Expose the metrics endpoint as a Web endpoint. + endpoints.metrics.web.path=metrics # Path of the metrics endpoint. # PROMETHEUS ENDPOINT ({sc-spring-boot-actuator}/metrics/export/prometheus/PrometheusScrapeEndpoint.{sc-ext}[PrometheusScrapeEndpoint]) endpoints.prometheus.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.prometheus.enabled= # Enable the metrics endpoint. endpoints.prometheus.web.enabled= # Expose the metrics endpoint as a Web endpoint. + endpoints.prometheus.web.path=prometheus # Path of the prometheus endpoint. # SESSIONS ENDPOINT ({sc-spring-boot-actuator}/session/SessionsEndpoint.{sc-ext}[SessionsEndpoint]) endpoints.sessions.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.sessions.enabled= # Enable the sessions endpoint. endpoints.sessions.jmx.enabled= # Expose the sessions endpoint as a JMX MBean. endpoints.sessions.web.enabled= # Expose the sessions endpoint as a Web endpoint. + endpoints.sessions.web.path=sessions # Path of the sessions endpoint. # SHUTDOWN ENDPOINT ({sc-spring-boot-actuator}/context/ShutdownEndpoint.{sc-ext}[ShutdownEndpoint]) endpoints.shutdown.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.shutdown.enabled=false # Enable the shutdown endpoint. endpoints.shutdown.jmx.enabled=false # Expose the shutdown endpoint as a JMX MBean. endpoints.shutdown.web.enabled=false # Expose the shutdown endpoint as a Web endpoint. + endpoints.shutdown.web.path=shutdown # Path of the shutdown endpoint. # STATUS ENDPOINT ({sc-spring-boot-actuator}/health/StatusEndpoint.{sc-ext}[StatusEndpoint]) endpoints.status.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.status.enabled=true # Enable the status endpoint. endpoints.status.jmx.enabled=true # Expose the status endpoint as a JMX MBean. endpoints.status.web.enabled=true # Expose the status endpoint as a Web endpoint. + endpoints.status.web.path=status # Path of the status endpoint. # THREAD DUMP ENDPOINT ({sc-spring-boot-actuator}/management/ThreadDumpEndpoint.{sc-ext}[ThreadDumpEndpoint]) endpoints.threaddump.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.threaddump.enabled= # Enable the threaddump endpoint. endpoints.threaddump.jmx.enabled= # Expose the threaddump endpoint as a JMX MBean. endpoints.threaddump.web.enabled= # Expose the threaddump endpoint as a Web endpoint. + endpoints.threaddump.web.path=threaddump # Path of the threaddump endpoint. # TRACE ENDPOINT ({sc-spring-boot-actuator}/trace/TraceEndpoint.{sc-ext}[TraceEndpoint]) endpoints.trace.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.trace.enabled= # Enable the trace endpoint. endpoints.trace.jmx.enabled= # Expose the trace endpoint as a JMX MBean. endpoints.trace.web.enabled= # Expose the trace endpoint as a Web endpoint. + endpoints.trace.web.path=trace # Path of the trace endpoint. # MANAGEMENT HTTP SERVER ({sc-spring-boot-actuator-autoconfigure}/web/server/ManagementServerProperties.{sc-ext}[ManagementServerProperties]) management.server.add-application-context-header=false # Add the "X-Application-Context" HTTP header in each response. Requires a custom management.server.port. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 1d264fe818..7c798bde0d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -394,6 +394,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor type, null, String.format("Expose the %s endpoint as a Web endpoint.", endpointId), enabledByDefault, null)); + this.metadataCollector.add(ItemMetadata.newProperty( + endpointKey(endpointId), "web.path", String.class.getName(), type, + null, String.format("Path of the %s endpoint.", endpointId), + endpointId, null)); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 111a6ad3bb..c4b47c3402 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -539,8 +539,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("simple", null)); assertThat(metadata).has(jmxEnabledFlag("simple", null)); assertThat(metadata).has(webEnabledFlag("simple", null)); + assertThat(metadata).has(webPath("simple")); assertThat(metadata).has(cacheTtl("simple")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); } @Test @@ -551,8 +552,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("disabled", false)); assertThat(metadata).has(jmxEnabledFlag("disabled", false)); assertThat(metadata).has(webEnabledFlag("disabled", false)); + assertThat(metadata).has(webPath("disabled")); assertThat(metadata).has(cacheTtl("disabled")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); } @Test @@ -563,8 +565,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("enabled", true)); assertThat(metadata).has(jmxEnabledFlag("enabled", true)); assertThat(metadata).has(webEnabledFlag("enabled", true)); + assertThat(metadata).has(webPath("enabled")); assertThat(metadata).has(cacheTtl("enabled")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); } @Test @@ -577,8 +580,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("customprops", null)); assertThat(metadata).has(jmxEnabledFlag("customprops", null)); assertThat(metadata).has(webEnabledFlag("customprops", null)); + assertThat(metadata).has(webPath("customprops")); assertThat(metadata).has(cacheTtl("customprops")); - assertThat(metadata.getItems()).hasSize(6); + assertThat(metadata.getItems()).hasSize(7); } @Test @@ -599,8 +603,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { Metadata.withGroup("endpoints.web").fromSource(OnlyWebEndpoint.class)); assertThat(metadata).has(enabledFlag("web", null)); assertThat(metadata).has(webEnabledFlag("web", null)); + assertThat(metadata).has(webPath("web")); assertThat(metadata).has(cacheTtl("web")); - assertThat(metadata.getItems()).hasSize(4); + assertThat(metadata.getItems()).hasSize(5); } @Test @@ -613,8 +618,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("incremental", null)); assertThat(metadata).has(jmxEnabledFlag("incremental", null)); assertThat(metadata).has(webEnabledFlag("incremental", null)); + assertThat(metadata).has(webPath("incremental")); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); project.replaceText(IncrementalEndpoint.class, "id = \"incremental\"", "id = \"incremental\", defaultEnablement = org.springframework.boot." + "configurationsample.DefaultEnablement.DISABLED"); @@ -624,8 +630,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("incremental", false)); assertThat(metadata).has(jmxEnabledFlag("incremental", false)); assertThat(metadata).has(webEnabledFlag("incremental", false)); + assertThat(metadata).has(webPath("incremental")); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); } @Test @@ -638,8 +645,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("incremental", null)); assertThat(metadata).has(jmxEnabledFlag("incremental", null)); assertThat(metadata).has(webEnabledFlag("incremental", null)); + assertThat(metadata).has(webPath("incremental")); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); project.replaceText(IncrementalEndpoint.class, "id = \"incremental\"", "id = \"incremental\", exposure = org.springframework.boot." + "configurationsample.EndpointExposure.WEB"); @@ -648,8 +656,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { .fromSource(IncrementalEndpoint.class)); assertThat(metadata).has(enabledFlag("incremental", null)); assertThat(metadata).has(webEnabledFlag("incremental", null)); + assertThat(metadata).has(webPath("incremental")); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(4); + assertThat(metadata.getItems()).hasSize(5); } @Test @@ -671,8 +680,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(enabledFlag("incremental", null)); assertThat(metadata).has(jmxEnabledFlag("incremental", null)); assertThat(metadata).has(webEnabledFlag("incremental", null)); + assertThat(metadata).has(webPath("incremental")); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(5); + assertThat(metadata.getItems()).hasSize(6); } private Metadata.MetadataItemCondition enabledFlag(String endpointId, @@ -696,6 +706,12 @@ public class ConfigurationMetadataAnnotationProcessorTests { .format("Expose the %s endpoint as a Web endpoint.", endpointId)); } + private Metadata.MetadataItemCondition webPath(String endpointId) { + return Metadata.withProperty("endpoints." + endpointId + ".web.path") + .ofType(String.class).withDefaultValue(endpointId).withDescription(String + .format("Path of the %s endpoint.", endpointId)); + } + private Metadata.MetadataItemCondition cacheTtl(String endpointId) { return Metadata.withProperty("endpoints." + endpointId + ".cache.time-to-live") .ofType(Long.class).withDefaultValue(0).withDescription(