diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfiguration.java index 4b36b16cbcf..756de190366 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfiguration.java @@ -18,8 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.audit; import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.AuditEventsEndpoint; +import org.springframework.boot.actuate.audit.AuditEventsEndpointWebExtension; import org.springframework.boot.actuate.audit.AuditEventsJmxEndpointExtension; -import org.springframework.boot.actuate.audit.AuditEventsWebEndpointExtension; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.logging.LoggersEndpoint; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -63,9 +63,9 @@ public class AuditEventsEndpointAutoConfiguration { @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(AuditEventsEndpoint.class) - public AuditEventsWebEndpointExtension auditEventsWebEndpointExtension( + public AuditEventsEndpointWebExtension auditEventsWebEndpointExtension( AuditEventsEndpoint auditEventsEndpoint) { - return new AuditEventsWebEndpointExtension(auditEventsEndpoint); + return new AuditEventsEndpointWebExtension(auditEventsEndpoint); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java index e3f3b016298..d7bcf4a9414 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java @@ -32,13 +32,13 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityRespo import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.OperationType; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.Link; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.http.HttpMethod; @@ -81,7 +81,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping } @Override - protected void registerMappingForOperation(WebEndpointOperation operation) { + protected void registerMappingForOperation(WebOperation operation) { OperationType operationType = operation.getType(); OperationInvoker operationInvoker = operation.getInvoker(); if (operation.isBlocking()) { @@ -135,7 +135,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping * @param securityInterceptor the Security Interceptor */ CloudFoundryWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, ReactiveCloudFoundrySecurityInterceptor securityInterceptor) { super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java index 14c753f46d0..ffa8ef73fd2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java @@ -21,10 +21,9 @@ import java.util.Collections; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.actuate.autoconfigure.endpoint.DefaultCachingConfigurationFactory; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; -import org.springframework.boot.actuate.endpoint.ParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -68,17 +67,16 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration { @Bean public CloudFoundryWebFluxEndpointHandlerMapping cloudFoundryWebFluxEndpointHandlerMapping( ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes, - WebClient.Builder webClientBuilder, Environment environment, - DefaultCachingConfigurationFactory cachingConfigurationFactory, - WebEndpointProperties webEndpointProperties) { + WebClient.Builder webClientBuilder) { WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( - this.applicationContext, parameterMapper, cachingConfigurationFactory, - endpointMediaTypes, (id) -> id); + this.applicationContext, parameterMapper, endpointMediaTypes, + EndpointPathResolver.useEndpointId(), null, null); + ReactiveCloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor( + webClientBuilder, this.applicationContext.getEnvironment()); return new CloudFoundryWebFluxEndpointHandlerMapping( new EndpointMapping("/cloudfoundryapplication"), endpointDiscoverer.discoverEndpoints(), endpointMediaTypes, - getCorsConfiguration(), - getSecurityInterceptor(webClientBuilder, environment)); + getCorsConfiguration(), securityInterceptor); } private ReactiveCloudFoundrySecurityInterceptor getSecurityInterceptor( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java index 49b469ed10c..c048d7872be 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java @@ -18,9 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet; import java.util.Arrays; -import org.springframework.boot.actuate.autoconfigure.endpoint.DefaultCachingConfigurationFactory; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; -import org.springframework.boot.actuate.endpoint.ParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; @@ -71,17 +70,17 @@ public class CloudFoundryActuatorAutoConfiguration { @Bean public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping( - ParameterMapper parameterMapper, - DefaultCachingConfigurationFactory cachingConfigurationFactory, - EndpointMediaTypes endpointMediaTypes, Environment environment, - RestTemplateBuilder builder) { + ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes, + RestTemplateBuilder restTemplateBuilder) { WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( - this.applicationContext, parameterMapper, cachingConfigurationFactory, - endpointMediaTypes, EndpointPathResolver.useEndpointId()); + this.applicationContext, parameterMapper, endpointMediaTypes, + EndpointPathResolver.useEndpointId(), null, null); + CloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor( + restTemplateBuilder, this.applicationContext.getEnvironment()); return new CloudFoundryWebEndpointServletHandlerMapping( new EndpointMapping("/cloudfoundryapplication"), endpointDiscoverer.discoverEndpoints(), endpointMediaTypes, - getCorsConfiguration(), getSecurityInterceptor(builder, environment)); + getCorsConfiguration(), securityInterceptor); } private CloudFoundrySecurityInterceptor getSecurityInterceptor( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java index a4d90783d84..b50e1759f4e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java @@ -35,13 +35,13 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.Link; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.http.HttpMethod; @@ -78,7 +78,7 @@ class CloudFoundryWebEndpointServletHandlerMapping private final EndpointLinksResolver endpointLinksResolver = new EndpointLinksResolver(); CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor) { super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration); @@ -125,7 +125,7 @@ class CloudFoundryWebEndpointServletHandlerMapping } @Override - protected void registerMappingForOperation(WebEndpointOperation operation) { + protected void registerMappingForOperation(WebOperation operation) { registerMapping(createRequestMappingInfo(operation), new OperationHandler(operation.getInvoker(), operation.getId(), this.securityInterceptor), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointProperties.java index 59bf78203c5..8eb4684ba36 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointProperties.java @@ -25,7 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Stephane Nicoll * @since 2.0.0 */ -@ConfigurationProperties("endpoints.configprops") +@ConfigurationProperties("management.endpoint.configprops") public class ConfigurationPropertiesReportEndpointProperties { /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactory.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactory.java deleted file mode 100644 index 603f5447f4a..00000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.cache.CachingConfiguration; -import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; -import org.springframework.core.env.Environment; - -/** - * Default {@link CachingConfigurationFactory} implementation that use the - * {@link Environment} to extract the caching settings of each endpoint. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class DefaultCachingConfigurationFactory implements CachingConfigurationFactory { - - private final Environment environment; - - /** - * Create a new instance with the {@link Environment} to use. - * @param environment the environment - */ - DefaultCachingConfigurationFactory(Environment environment) { - this.environment = environment; - } - - @Override - public CachingConfiguration getCachingConfiguration(String endpointId) { - String key = String.format("endpoints.%s.cache.time-to-live", endpointId); - Long timeToLive = this.environment.getProperty(key, Long.class, 0L); - return new CachingConfiguration(timeToLive); - } -} 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 62dfe4e2642..41e554d40a3 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 @@ -16,23 +16,12 @@ package org.springframework.boot.actuate.autoconfigure.endpoint; -import java.util.Arrays; -import java.util.List; - -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.actuate.endpoint.ParameterMapper; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; +import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; -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.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -54,49 +43,11 @@ public class EndpointAutoConfiguration { } @Bean - @ConditionalOnMissingBean(CachingConfigurationFactory.class) - public DefaultCachingConfigurationFactory endpointCacheConfigurationFactory( + @ConditionalOnMissingBean + public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor( Environment environment) { - return new DefaultCachingConfigurationFactory(environment); - } - - @Configuration - @ConditionalOnWebApplication - static class EndpointWebConfiguration { - - private static final List MEDIA_TYPES = Arrays - .asList(ActuatorMediaType.V2_JSON, "application/json"); - - private final ApplicationContext applicationContext; - - EndpointWebConfiguration(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Bean - public EndpointMediaTypes endpointMediaTypes() { - return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES); - } - - @Bean - @ConditionalOnMissingBean - public EndpointPathResolver endpointPathResolver(Environment environment) { - return new DefaultEndpointPathResolver(environment); - } - - @Bean - public EndpointProvider webEndpointProvider( - ParameterMapper parameterMapper, - DefaultCachingConfigurationFactory cachingConfigurationFactory, - EndpointPathResolver endpointPathResolver) { - Environment environment = this.applicationContext.getEnvironment(); - WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( - this.applicationContext, parameterMapper, cachingConfigurationFactory, - endpointMediaTypes(), endpointPathResolver); - return new EndpointProvider<>(environment, endpointDiscoverer, - EndpointExposure.WEB); - } - + return new CachingOperationInvokerAdvisor( + new EndpointIdTimeToLivePropertyFunction(environment)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablement.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablement.java deleted file mode 100644 index 840aae0d80e..00000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablement.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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; - -/** - * Determines if an endpoint is enabled or not. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public final class EndpointEnablement { - - private final boolean enabled; - - private final String reason; - - /** - * Creates a new instance. - * @param enabled whether or not the endpoint is enabled - * @param reason a human readable reason of the decision - */ - EndpointEnablement(boolean enabled, String reason) { - this.enabled = enabled; - this.reason = reason; - } - - /** - * Return whether or not the endpoint is enabled. - * @return {@code true} if the endpoint is enabled, {@code false} otherwise - */ - public boolean isEnabled() { - return this.enabled; - } - - /** - * Return a human readable reason of the decision. - * @return the reason of the endpoint's enablement - */ - public String getReason() { - return this.reason; - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProvider.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProvider.java deleted file mode 100644 index 432b9f1aba6..00000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProvider.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.DefaultEnablement; -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.core.env.Environment; -import org.springframework.util.Assert; - -/** - * Determines an endpoint's enablement based on the current {@link Environment}. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class EndpointEnablementProvider { - - private final Environment environment; - - /** - * Creates a new instance with the {@link Environment} to use. - * @param environment the environment - */ - public EndpointEnablementProvider(Environment environment) { - this.environment = environment; - } - - /** - * Return the {@link EndpointEnablement} of an endpoint with no specific tech - * exposure. - * @param endpointId the id of the endpoint - * @param defaultEnablement the {@link DefaultEnablement} of the endpoint - * @return the {@link EndpointEnablement} of that endpoint - */ - public EndpointEnablement getEndpointEnablement(String endpointId, - DefaultEnablement defaultEnablement) { - return getEndpointEnablement(endpointId, defaultEnablement, null); - } - - /** - * Return the {@link EndpointEnablement} of an endpoint for a specific tech exposure. - * @param endpointId the id of the endpoint - * @param defaultEnablement the {@link DefaultEnablement} of the endpoint - * @param exposure the requested {@link EndpointExposure} - * @return the {@link EndpointEnablement} of that endpoint for the specified - * {@link EndpointExposure} - */ - public EndpointEnablement getEndpointEnablement(String endpointId, - DefaultEnablement defaultEnablement, EndpointExposure exposure) { - Assert.hasText(endpointId, "Endpoint id must have a value"); - Assert.isTrue(!endpointId.equals("default"), - "Endpoint id 'default' is a reserved " - + "value and cannot be used by an endpoint"); - EndpointEnablement result = findEnablement(endpointId, exposure); - if (result != null) { - return result; - } - result = findEnablement(getKey(endpointId, "enabled")); - if (result != null) { - return result; - } - // All endpoints specific attributes have been looked at. Checking default value - // for the endpoint - if (defaultEnablement != DefaultEnablement.NEUTRAL) { - return getDefaultEndpointEnablement(endpointId, - (defaultEnablement == DefaultEnablement.ENABLED), exposure); - } - return getGlobalEndpointEnablement(endpointId, defaultEnablement, exposure); - } - - private EndpointEnablement findEnablement(String endpointId, - EndpointExposure exposure) { - if (exposure != null) { - return findEnablement(getKey(endpointId, exposure)); - } - return findEnablementForAnyExposureTechnology(endpointId); - } - - private EndpointEnablement getGlobalEndpointEnablement(String endpointId, - DefaultEnablement defaultEnablement, EndpointExposure exposure) { - EndpointEnablement result = findGlobalEndpointEnablement(exposure); - if (result != null) { - return result; - } - result = findEnablement(getKey("default", "enabled")); - if (result != null) { - return result; - } - boolean enablement = determineGlobalDefaultEnablement(defaultEnablement, - exposure); - String message = determineGlobalDefaultMessage(endpointId, enablement, exposure, - defaultEnablement); - return new EndpointEnablement(enablement, message); - } - - private boolean determineGlobalDefaultEnablement(DefaultEnablement defaultEnablement, - EndpointExposure exposure) { - if (defaultEnablement == DefaultEnablement.NEUTRAL) { - return exposure == null || exposure.isEnabledByDefault(); - } - return (defaultEnablement == DefaultEnablement.ENABLED); - } - - private String determineGlobalDefaultMessage(String endpointId, boolean enablement, - EndpointExposure exposure, DefaultEnablement defaultEnablement) { - StringBuilder message = new StringBuilder(); - message.append(String.format("endpoint '%s' ", endpointId)); - if (exposure != null) { - message.append(String.format("(%s) ", exposure.name().toLowerCase())); - } - message.append(String.format("is %s ", (enablement ? "enabled" : "disabled"))); - if (defaultEnablement == DefaultEnablement.NEUTRAL) { - if (exposure != null) { - message.append(String.format("(default for %s endpoints)", - exposure.name().toLowerCase())); - } - else { - message.append("(default)"); - } - } - else { - message.append("by default"); - } - return message.toString(); - - } - - private EndpointEnablement findGlobalEndpointEnablement(EndpointExposure exposure) { - if (exposure != null) { - EndpointEnablement result = findEnablement(getKey("default", exposure)); - if (result != null) { - return result; - } - if (!exposure.isEnabledByDefault()) { - return getDefaultEndpointEnablement("default", false, exposure); - } - return null; - } - return findEnablementForAnyExposureTechnology("default"); - } - - private EndpointEnablement findEnablementForAnyExposureTechnology(String endpointId) { - for (EndpointExposure candidate : EndpointExposure.values()) { - EndpointEnablement result = findEnablementForExposureTechnology(endpointId, - candidate); - if (result != null && result.isEnabled()) { - return result; - } - } - return null; - } - - private EndpointEnablement findEnablementForExposureTechnology(String endpointId, - EndpointExposure exposure) { - String endpointTypeKey = getKey(endpointId, exposure); - return findEnablement(endpointTypeKey); - } - - private EndpointEnablement getDefaultEndpointEnablement(String endpointId, - boolean enabledByDefault, EndpointExposure exposure) { - return new EndpointEnablement(enabledByDefault, - createDefaultEnablementMessage(endpointId, enabledByDefault, exposure)); - } - - private String createDefaultEnablementMessage(String endpointId, - boolean enabledByDefault, EndpointExposure exposure) { - StringBuilder message = new StringBuilder(); - message.append(String.format("endpoint '%s' ", endpointId)); - if (exposure != null) { - message.append(String.format("(%s) ", exposure.name().toLowerCase())); - } - message.append(String.format("is %s by default", - (enabledByDefault ? "enabled" : "disabled"))); - return message.toString(); - } - - private String getKey(String endpointId, EndpointExposure exposure) { - return getKey(endpointId, exposure.name().toLowerCase() + ".enabled"); - } - - private String getKey(String endpointId, String suffix) { - return "endpoints." + endpointId + "." + suffix; - } - - /** - * Return an {@link EndpointEnablement} for the specified key if it is set or - * {@code null} if the key is not present in the environment. - * @param key the key to check - * @return the outcome or {@code null} if the key is no set - */ - private EndpointEnablement findEnablement(String key) { - if (this.environment.containsProperty(key)) { - boolean match = this.environment.getProperty(key, Boolean.class, true); - return new EndpointEnablement(match, String.format("found property %s", key)); - } - return null; - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunction.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunction.java new file mode 100644 index 00000000000..e3700044d7b --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunction.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 java.util.function.Function; + +import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvokerAdvisor; +import org.springframework.core.env.PropertyResolver; + +/** + * Function for use with {@link CachingOperationInvokerAdvisor} that extracts caching + * time-to-live from a {@link PropertyResolver resolved property}. + * + * @author Stephane Nicoll + * @author Phillip Webb + */ +class EndpointIdTimeToLivePropertyFunction implements Function { + + private final PropertyResolver propertyResolver; + + /** + * Create a new instance with the {@link PropertyResolver} to use. + * @param propertyResolver the environment + */ + EndpointIdTimeToLivePropertyFunction(PropertyResolver propertyResolver) { + this.propertyResolver = propertyResolver; + } + + @Override + public Long apply(String endpointId) { + String key = String.format("management.endpoint.%s.cache.time-to-live", + endpointId); + return this.propertyResolver.getProperty(key, Long.class); + } +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointProvider.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointProvider.java deleted file mode 100644 index 7b546794fee..00000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointProvider.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 java.util.Collection; -import java.util.stream.Collectors; - -import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.Operation; -import org.springframework.core.env.Environment; - -/** - * Provides the endpoints that are enabled according to an {@link EndpointDiscoverer} and - * the current {@link Environment}. - * - * @param the endpoint operation type - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class EndpointProvider { - - private final EndpointDiscoverer discoverer; - - private final EndpointEnablementProvider endpointEnablementProvider; - - private final EndpointExposure exposure; - - /** - * Creates a new instance. - * @param environment the environment to use to check the endpoints that are enabled - * @param discoverer the discoverer to get the initial set of endpoints - * @param exposure the exposure technology for the endpoint - */ - public EndpointProvider(Environment environment, EndpointDiscoverer discoverer, - EndpointExposure exposure) { - this.discoverer = discoverer; - this.endpointEnablementProvider = new EndpointEnablementProvider(environment); - this.exposure = exposure; - } - - public Collection> getEndpoints() { - return this.discoverer.discoverEndpoints().stream().filter(this::isEnabled) - .collect(Collectors.toList()); - } - - private boolean isEnabled(EndpointInfo endpoint) { - return this.endpointEnablementProvider.getEndpointEnablement(endpoint.getId(), - endpoint.getDefaultEnablement(), this.exposure).isEnabled(); - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilter.java new file mode 100644 index 00000000000..a5139e0cec0 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilter.java @@ -0,0 +1,119 @@ +/* + * 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * {@link EndpointFilter} that will filter endpoints based on {@code expose} and + * {@code exclude} properties. + * + * @param The operation type + * @author Phillip Webb + * @since 2.0.0 + */ +public class ExposeExcludePropertyEndpointFilter + implements EndpointFilter { + + private final Class> discovererType; + + private final Set expose; + + private final Set exclude; + + private final Set exposeDefaults; + + public ExposeExcludePropertyEndpointFilter( + Class> discovererType, + Environment environment, String prefix, String... exposeDefaults) { + Assert.notNull(discovererType, "Discoverer Type must not be null"); + Assert.notNull(environment, "Environment must not be null"); + Assert.hasText(prefix, "Prefix must not be empty"); + Binder binder = Binder.get(environment); + this.discovererType = discovererType; + this.expose = bind(binder, prefix + ".expose"); + this.exclude = bind(binder, prefix + ".exclude"); + this.exposeDefaults = asSet(Arrays.asList(exposeDefaults)); + } + + public ExposeExcludePropertyEndpointFilter( + Class> discovererType, + Collection expose, Collection exclude, + String... exposeDefaults) { + Assert.notNull(discovererType, "Discoverer Type must not be null"); + this.discovererType = discovererType; + this.expose = asSet(expose); + this.exclude = asSet(exclude); + this.exposeDefaults = asSet(Arrays.asList(exposeDefaults)); + } + + private Set bind(Binder binder, String name) { + return asSet(binder.bind(name, Bindable.listOf(String.class)) + .orElseGet(ArrayList::new)); + } + + private Set asSet(Collection items) { + if (items == null) { + return Collections.emptySet(); + } + return items.stream().map(String::toLowerCase) + .collect(Collectors.toCollection(HashSet::new)); + } + + @Override + public boolean match(EndpointInfo info, EndpointDiscoverer discoverer) { + if (this.discovererType.isInstance(discoverer)) { + return isExposed(info) && !isExcluded(info); + } + return true; + } + + private boolean isExposed(EndpointInfo info) { + if (this.expose.isEmpty()) { + return this.exposeDefaults.contains("*") + || contains(this.exposeDefaults, info); + } + return this.expose.contains("*") || contains(this.expose, info); + } + + private boolean isExcluded(EndpointInfo info) { + if (this.exclude.isEmpty()) { + return false; + } + return this.exclude.contains("*") || contains(this.exclude, info); + } + + private boolean contains(Set items, EndpointInfo info) { + return items.contains(info.getId().toLowerCase()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpoint.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpoint.java index 8ed4359f6ce..52664b9d582 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpoint.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpoint.java @@ -22,63 +22,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.context.annotation.Conditional; +import org.springframework.core.env.Environment; /** * {@link Conditional} that checks whether an endpoint is enabled or not. Matches - * according to the {@code defaultEnablement} and {@code types} flag that the - * {@link Endpoint} may be restricted to. - *

- * When an endpoint uses {@link DefaultEnablement#DISABLED}, it will only be enabled if - * {@code endpoint..enabled}, {@code endpoint..jmx.enabled} or - * {@code endpoint..web.enabled} is {@code true}. - *

- * When an endpoint uses {@link DefaultEnablement#ENABLED}, it will be enabled unless - * {@code endpoint..enabled}, {@code endpoint..jmx.enabled} or - * {@code endpoint..web.enabled} is {@code false}. - *

- * When an endpoint uses {@link DefaultEnablement#NEUTRAL}, it will be enabled if - * {@code endpoint.default.enabled}, {@code endpoint.default.jmx.enabled} or - * {@code endpoint.default.web.enabled} is {@code true} and - * {@code endpoint..enabled}, {@code endpoint..jmx.enabled} or - * {@code endpoint..web.enabled} has not been set to {@code false}. - *

- * If any properties are set, they are evaluated from most to least specific, e.g. - * considering a web endpoint with id {@code foo}: - *

    - *
  1. endpoints.foo.web.enabled
  2. - *
  3. endpoints.foo.enabled
  4. - *
  5. endpoints.default.web.enabled
  6. - *
  7. endpoints.default.enabled
  8. - *
- * For instance if {@code endpoints.default.enabled} is {@code false} but - * {@code endpoints..enabled} is {@code true}, the condition will match. - *

- * This condition must be placed on a {@code @Bean} method producing an endpoint as its id - * and other attributes are inferred from the {@link Endpoint} annotation set on the - * return type of the factory method. Consider the following valid example: - * - *

- * @Configuration
- * public class MyAutoConfiguration {
- *
- *     @ConditionalOnEnabledEndpoint
- *     @Bean
- *     public MyEndpoint myEndpoint() {
- *         ...
- *     }
- *
- *     @Endpoint(id = "my", defaultEnablement = DefaultEnablement.DISABLED)
- *     static class MyEndpoint { ... }
- *
- * }
- *

- * - * In the sample above the condition will be evaluated with the attributes specified on - * {@code MyEndpoint}. In particular, in the absence of any property in the environment, - * the condition will not match as this endpoint is disabled by default. + * according to the endpoints specific {@link Environment} property, falling back to + * {@code management.endpoints.enabled-by-default} or failing that + * {@link Endpoint#enableByDefault()}. * * @author Stephane Nicoll * @since 2.0.0 diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnEnabledEndpointCondition.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnEnabledEndpointCondition.java index bb1166215e2..f58be42cfe7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnEnabledEndpointCondition.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnEnabledEndpointCondition.java @@ -16,45 +16,62 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.condition; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointEnablement; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointEnablementProvider; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * A condition that checks if an endpoint is enabled. * * @author Stephane Nicoll * @author Andy Wilkinson + * @author Phillip Webb + * @see ConditionalOnEnabledEndpoint */ class OnEnabledEndpointCondition extends SpringBootCondition { + private static final String ENABLED_BY_DEFAULT_KEY = "management.endpoints.enabled-by-default"; + @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - EndpointAttributes attributes = getEndpointAttributes(context, metadata); - EndpointEnablement endpointEnablement = attributes - .getEnablement(new EndpointEnablementProvider(context.getEnvironment())); - return new ConditionOutcome(endpointEnablement.isEnabled(), - ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class) - .because(endpointEnablement.getReason())); + AnnotationAttributes attributes = getEndpointAttributes(context, metadata); + String id = attributes.getString("id"); + String key = "management.endpoint." + id + ".enabled"; + Boolean userDefinedEnabled = context.getEnvironment().getProperty(key, + Boolean.class); + if (userDefinedEnabled != null) { + return new ConditionOutcome(userDefinedEnabled, + ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class) + .because("found property " + key + " with value " + + userDefinedEnabled)); + } + Boolean userDefinedDefault = context.getEnvironment() + .getProperty(ENABLED_BY_DEFAULT_KEY, Boolean.class); + if (userDefinedDefault != null) { + return new ConditionOutcome(userDefinedDefault, + ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class) + .because("no property " + key + + " found so using user defined default from " + + ENABLED_BY_DEFAULT_KEY)); + } + boolean endpointDefault = attributes.getBoolean("enableByDefault"); + return new ConditionOutcome(endpointDefault, + ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class).because( + "no property " + key + " found so using endpoint default")); } - private EndpointAttributes getEndpointAttributes(ConditionContext context, + private AnnotationAttributes getEndpointAttributes(ConditionContext context, AnnotatedTypeMetadata metadata) { Assert.state( metadata instanceof MethodMetadata @@ -63,77 +80,35 @@ class OnEnabledEndpointCondition extends SpringBootCondition { return getEndpointAttributes(context, (MethodMetadata) metadata); } - private EndpointAttributes getEndpointAttributes(ConditionContext context, - MethodMetadata methodMetadata) { + private AnnotationAttributes getEndpointAttributes(ConditionContext context, + MethodMetadata metadata) { + // We should be safe to load at this point since we are in the REGISTER_BEAN phase try { - // We should be safe to load at this point since we are in the - // REGISTER_BEAN phase - Class returnType = ClassUtils.forName(methodMetadata.getReturnTypeName(), + Class returnType = ClassUtils.forName(metadata.getReturnTypeName(), context.getClassLoader()); - return extractEndpointAttributes(returnType); + return getEndpointAttributes(returnType); } catch (Throwable ex) { throw new IllegalStateException("Failed to extract endpoint id for " - + methodMetadata.getDeclaringClassName() + "." - + methodMetadata.getMethodName(), ex); + + metadata.getDeclaringClassName() + "." + metadata.getMethodName(), + ex); } } - protected EndpointAttributes extractEndpointAttributes(Class type) { - EndpointAttributes attributes = extractEndpointAttributesFromEndpoint(type); - if (attributes != null) { - return attributes; - } - JmxEndpointExtension jmxExtension = AnnotationUtils.findAnnotation(type, - JmxEndpointExtension.class); - if (jmxExtension != null) { - return extractEndpointAttributes(jmxExtension.endpoint()); - } - WebEndpointExtension webExtension = AnnotationUtils.findAnnotation(type, - WebEndpointExtension.class); - if (webExtension != null) { - return extractEndpointAttributes(webExtension.endpoint()); - } - throw new IllegalStateException( - "OnEnabledEndpointCondition may only be used on @Bean methods that return" - + " @Endpoint, @JmxEndpointExtension, or @WebEndpointExtension"); - } - - private EndpointAttributes extractEndpointAttributesFromEndpoint( - Class endpointClass) { - Endpoint endpoint = AnnotationUtils.findAnnotation(endpointClass, Endpoint.class); - if (endpoint == null) { - return null; - } - // If both types are set, all exposure technologies are exposed - EndpointExposure[] exposures = endpoint.exposure(); - return new EndpointAttributes(endpoint.id(), endpoint.defaultEnablement(), - (exposures.length == 1 ? exposures[0] : null)); - } - - private static class EndpointAttributes { - - private final String id; - - private final DefaultEnablement defaultEnablement; - - private final EndpointExposure exposure; - - EndpointAttributes(String id, DefaultEnablement defaultEnablement, - EndpointExposure exposure) { - if (!StringUtils.hasText(id)) { - throw new IllegalStateException("Endpoint id could not be determined"); + protected AnnotationAttributes getEndpointAttributes(Class type) { + AnnotationAttributes attributes = AnnotatedElementUtils + .findMergedAnnotationAttributes(type, Endpoint.class, true, true); + if (attributes == null) { + attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(type, + EndpointExtension.class, false, true); + if (attributes != null) { + return getEndpointAttributes(attributes.getClass("endpoint")); } - this.id = id; - this.defaultEnablement = defaultEnablement; - this.exposure = exposure; } - - public EndpointEnablement getEnablement(EndpointEnablementProvider provider) { - return provider.getEndpointEnablement(this.id, this.defaultEnablement, - this.exposure); - } - + Assert.state(attributes != null, + "OnEnabledEndpointCondition may only be used on @Bean methods that " + + "return an @Endpoint or and @EndpointExtension"); + return attributes; } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.java index 03d8411c51b..daa794a2d99 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.java @@ -36,13 +36,13 @@ import org.springframework.util.StringUtils; */ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory { - private final JmxEndpointExporterProperties properties; + private final JmxEndpointProperties properties; private final MBeanServer mBeanServer; private final String contextId; - DefaultEndpointObjectNameFactory(JmxEndpointExporterProperties properties, + DefaultEndpointObjectNameFactory(JmxEndpointProperties properties, MBeanServer mBeanServer, String contextId) { this.properties = properties; this.mBeanServer = mBeanServer; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java index 78c64f9cee8..0bbea34ef14 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java @@ -16,21 +16,25 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.jmx; +import java.util.Collection; + import javax.management.MBeanServer; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.DefaultCachingConfigurationFactory; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.actuate.endpoint.ParameterMapper; +import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanRegistrar; -import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperation; +import org.springframework.boot.actuate.endpoint.jmx.EndpointObjectNameFactory; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxAnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -47,36 +51,49 @@ import org.springframework.util.ObjectUtils; * @since 2.0.0 */ @AutoConfigureAfter(JmxAutoConfiguration.class) -@EnableConfigurationProperties(JmxEndpointExporterProperties.class) +@EnableConfigurationProperties(JmxEndpointProperties.class) +@ConditionalOnProperty(name = "management.endpoints.jmx.enabled", matchIfMissing = true) public class JmxEndpointAutoConfiguration { private final ApplicationContext applicationContext; - public JmxEndpointAutoConfiguration(ApplicationContext applicationContext) { + private final JmxEndpointProperties properties; + + public JmxEndpointAutoConfiguration(ApplicationContext applicationContext, + JmxEndpointProperties properties) { this.applicationContext = applicationContext; + this.properties = properties; } @Bean - public JmxAnnotationEndpointDiscoverer jmxEndpointDiscoverer( + public JmxAnnotationEndpointDiscoverer jmxAnnotationEndpointDiscoverer( ParameterMapper parameterMapper, - DefaultCachingConfigurationFactory cachingConfigurationFactory) { + Collection invokerAdvisors, + Collection> filters) { return new JmxAnnotationEndpointDiscoverer(this.applicationContext, - parameterMapper, cachingConfigurationFactory); + parameterMapper, invokerAdvisors, filters); } - @ConditionalOnSingleCandidate(MBeanServer.class) @Bean - public JmxEndpointExporter jmxMBeanExporter(JmxEndpointExporterProperties properties, + @ConditionalOnSingleCandidate(MBeanServer.class) + public JmxEndpointExporter jmxMBeanExporter( + JmxAnnotationEndpointDiscoverer jmxAnnotationEndpointDiscoverer, MBeanServer mBeanServer, JmxAnnotationEndpointDiscoverer endpointDiscoverer, ObjectProvider objectMapper) { - EndpointProvider endpointProvider = new EndpointProvider<>( - this.applicationContext.getEnvironment(), endpointDiscoverer, - EndpointExposure.JMX); - EndpointMBeanRegistrar endpointMBeanRegistrar = new EndpointMBeanRegistrar( - mBeanServer, new DefaultEndpointObjectNameFactory(properties, mBeanServer, - ObjectUtils.getIdentityHexString(this.applicationContext))); - return new JmxEndpointExporter(endpointProvider, endpointMBeanRegistrar, + EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory( + this.properties, mBeanServer, + ObjectUtils.getIdentityHexString(this.applicationContext)); + EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(mBeanServer, + objectNameFactory); + return new JmxEndpointExporter(jmxAnnotationEndpointDiscoverer, registrar, objectMapper.getIfAvailable(ObjectMapper::new)); } + @Bean + public ExposeExcludePropertyEndpointFilter jmxIncludeExcludePropertyEndpointFilter() { + return new ExposeExcludePropertyEndpointFilter<>( + JmxAnnotationEndpointDiscoverer.class, this.properties.getExpose(), + this.properties.getExclude(), "*"); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporter.java index 397eaefc70c..4f479451aae 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporter.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -29,13 +30,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; +import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.jmx.EndpointMBean; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanRegistrar; import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointMBeanFactory; -import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperation; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; import org.springframework.boot.actuate.endpoint.jmx.JmxOperationResponseMapper; +import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxAnnotationEndpointDiscoverer; /** * Exports all available {@link Endpoint} to a configurable {@link MBeanServer}. @@ -44,7 +45,7 @@ import org.springframework.boot.actuate.endpoint.jmx.JmxOperationResponseMapper; */ class JmxEndpointExporter implements InitializingBean, DisposableBean { - private final EndpointProvider endpointProvider; + private final JmxAnnotationEndpointDiscoverer endpointDiscoverer; private final EndpointMBeanRegistrar endpointMBeanRegistrar; @@ -52,9 +53,9 @@ class JmxEndpointExporter implements InitializingBean, DisposableBean { private Collection registeredObjectNames; - JmxEndpointExporter(EndpointProvider endpointProvider, + JmxEndpointExporter(JmxAnnotationEndpointDiscoverer endpointDiscoverer, EndpointMBeanRegistrar endpointMBeanRegistrar, ObjectMapper objectMapper) { - this.endpointProvider = endpointProvider; + this.endpointDiscoverer = endpointDiscoverer; this.endpointMBeanRegistrar = endpointMBeanRegistrar; DataConverter dataConverter = new DataConverter(objectMapper); this.mBeanFactory = new JmxEndpointMBeanFactory(dataConverter); @@ -65,21 +66,19 @@ class JmxEndpointExporter implements InitializingBean, DisposableBean { this.registeredObjectNames = registerEndpointMBeans(); } + private Collection registerEndpointMBeans() { + Collection> endpoints = this.endpointDiscoverer + .discoverEndpoints(); + return this.mBeanFactory.createMBeans(endpoints).stream() + .map(this.endpointMBeanRegistrar::registerEndpointMBean) + .collect(Collectors.toCollection(ArrayList::new)); + } + @Override public void destroy() throws Exception { unregisterEndpointMBeans(this.registeredObjectNames); } - private Collection registerEndpointMBeans() { - List objectNames = new ArrayList<>(); - Collection mBeans = this.mBeanFactory - .createMBeans(this.endpointProvider.getEndpoints()); - for (EndpointMBean mBean : mBeans) { - objectNames.add(this.endpointMBeanRegistrar.registerEndpointMBean(mBean)); - } - return objectNames; - } - private void unregisterEndpointMBeans(Collection objectNames) { objectNames.forEach(this.endpointMBeanRegistrar::unregisterEndpointMbean); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporterProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.java similarity index 69% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporterProperties.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.java index c44792068fd..60ab87bf4fc 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporterProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.java @@ -16,7 +16,9 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.jmx; +import java.util.LinkedHashSet; import java.util.Properties; +import java.util.Set; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.env.Environment; @@ -29,7 +31,22 @@ import org.springframework.util.StringUtils; * @since 2.0.0 */ @ConfigurationProperties("management.endpoints.jmx") -public class JmxEndpointExporterProperties { +public class JmxEndpointProperties { + + /** + * Whether JMX endpoints are enabled. + */ + private boolean enabled; + + /** + * The IDs of endpoints that should be exposed or '*' for all. + */ + private Set expose = new LinkedHashSet<>(); + + /** + * The IDs of endpoints that should be excluded. + */ + private Set exclude = new LinkedHashSet<>(); /** * Endpoints JMX domain name. Fallback to 'spring.jmx.default-domain' if set. @@ -47,13 +64,37 @@ public class JmxEndpointExporterProperties { */ private final Properties staticNames = new Properties(); - public JmxEndpointExporterProperties(Environment environment) { + public JmxEndpointProperties(Environment environment) { String defaultDomain = environment.getProperty("spring.jmx.default-domain"); if (StringUtils.hasText(defaultDomain)) { this.domain = defaultDomain; } } + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public Set getExpose() { + return this.expose; + } + + public void setExpose(Set expose) { + this.expose = expose; + } + + public Set getExclude() { + return this.exclude; + } + + public void setExclude(Set exclude) { + this.exclude = exclude; + } + public String getDomain() { return this.domain; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProvider.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProvider.java index 99c13a4f596..1965297f2ce 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProvider.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProvider.java @@ -16,13 +16,13 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.util.Assert; /** @@ -33,29 +33,34 @@ import org.springframework.util.Assert; */ public class DefaultEndpointPathProvider implements EndpointPathProvider { - private final Collection> endpoints; - private final String basePath; - public DefaultEndpointPathProvider(EndpointProvider provider, + private final EndpointDiscoverer endpointDiscoverer; + + public DefaultEndpointPathProvider( + EndpointDiscoverer endpointDiscoverer, WebEndpointProperties webEndpointProperties) { - this.endpoints = provider.getEndpoints(); + this.endpointDiscoverer = endpointDiscoverer; this.basePath = webEndpointProperties.getBasePath(); } @Override public List getPaths() { - return this.endpoints.stream().map(this::getPath).collect(Collectors.toList()); + return getEndpoints().map(this::getPath).collect(Collectors.toList()); } @Override public String getPath(String id) { Assert.notNull(id, "ID must not be null"); - return this.endpoints.stream().filter((info) -> id.equals(info.getId())) - .findFirst().map(this::getPath).orElse(null); + return getEndpoints().filter((info) -> id.equals(info.getId())).findFirst() + .map(this::getPath).orElse(null); } - private String getPath(EndpointInfo endpointInfo) { + private Stream> getEndpoints() { + return this.endpointDiscoverer.discoverEndpoints().stream(); + } + + private String getPath(EndpointInfo endpointInfo) { return this.basePath + "/" + endpointInfo.getId(); } 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/web/DefaultEndpointPathResolver.java similarity index 76% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolver.java index 66ff39d9d56..03ea2bb7854 100644 --- 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/web/DefaultEndpointPathResolver.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.endpoint; +package org.springframework.boot.actuate.autoconfigure.endpoint.web; + +import java.util.Map; import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; import org.springframework.core.env.Environment; @@ -27,16 +29,15 @@ import org.springframework.core.env.Environment; */ class DefaultEndpointPathResolver implements EndpointPathResolver { - private final Environment environment; + private final Map pathMapping; - DefaultEndpointPathResolver(Environment environment) { - this.environment = environment; + DefaultEndpointPathResolver(Map pathMapping) { + this.pathMapping = pathMapping; } @Override public String resolvePath(String endpointId) { - String key = String.format("endpoints.%s.web.path", endpointId); - return this.environment.getProperty(key, String.class, endpointId); + return this.pathMapping.getOrDefault(endpointId, endpointId); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java new file mode 100644 index 00000000000..1fd830d89fa --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java @@ -0,0 +1,111 @@ +/* + * 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.web; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; +import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; +import org.springframework.boot.actuate.endpoint.web.WebOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for web {@link Endpoint} support. + * + * @author Phillip Webb + * @author Stephane Nicoll + * @author Phillip Webb + * @since 2.0.0 + */ +@Configuration +@ConditionalOnWebApplication +@AutoConfigureAfter(EndpointAutoConfiguration.class) +@EnableConfigurationProperties(WebEndpointProperties.class) +@ConditionalOnProperty(name = "management.endpoints.web.enabled", matchIfMissing = true) +public class WebEndpointAutoConfiguration { + + private static final List MEDIA_TYPES = Arrays + .asList(ActuatorMediaType.V2_JSON, "application/json"); + + private final ApplicationContext applicationContext; + + private final WebEndpointProperties properties; + + public WebEndpointAutoConfiguration(ApplicationContext applicationContext, + WebEndpointProperties properties) { + this.applicationContext = applicationContext; + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean + public EndpointPathResolver endpointPathResolver() { + return new DefaultEndpointPathResolver(this.properties.getPathMapping()); + } + + @Bean + @ConditionalOnMissingBean + public WebAnnotationEndpointDiscoverer webAnnotationEndpointDiscoverer( + ParameterMapper parameterMapper, EndpointPathResolver endpointPathResolver, + Collection invokerAdvisors, + Collection> filters) { + return new WebAnnotationEndpointDiscoverer(this.applicationContext, + parameterMapper, endpointMediaTypes(), endpointPathResolver, + invokerAdvisors, filters); + } + + @Bean + @ConditionalOnMissingBean + public EndpointMediaTypes endpointMediaTypes() { + return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES); + } + + @Bean + @ConditionalOnMissingBean + public EndpointPathProvider endpointPathProvider( + EndpointDiscoverer endpointDiscoverer, + WebEndpointProperties webEndpointProperties) { + return new DefaultEndpointPathProvider(endpointDiscoverer, webEndpointProperties); + } + + @Bean + public ExposeExcludePropertyEndpointFilter webIncludeExcludePropertyEndpointFilter() { + return new ExposeExcludePropertyEndpointFilter<>( + WebAnnotationEndpointDiscoverer.class, this.properties.getExpose(), + this.properties.getExclude(), "info", "status"); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointProperties.java index fb97a4a3417..129423a6724 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointProperties.java @@ -16,23 +16,57 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + import org.springframework.boot.context.properties.ConfigurationProperties; /** * Configuration properties for web management endpoints. * * @author Madhura Bhave + * @author Phillip Webb * @since 2.0.0 */ @ConfigurationProperties(prefix = "management.endpoints.web") public class WebEndpointProperties { + /** + * Whether web endpoints are enabled. + */ + private boolean enabled; + /** * The base-path for the web endpoints. Relative to `server.context-path` or * `management.server.context-path`, if `management.server.port` is different. */ private String basePath = "/application"; + /** + * The IDs of endpoints that should be exposed or '*' for all. + */ + private Set expose = new LinkedHashSet<>(); + + /** + * The IDs of endpoints that should be excluded. + */ + private Set exclude = new LinkedHashSet<>(); + + /** + * Mapping between endpoint IDs and the path that should expose them. + */ + private Map pathMapping = new LinkedHashMap<>(); + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + public String getBasePath() { return this.basePath; } @@ -41,4 +75,28 @@ public class WebEndpointProperties { this.basePath = basePath; } + public Set getExpose() { + return this.expose; + } + + public void setExpose(Set expose) { + this.expose = expose; + } + + public Set getExclude() { + return this.exclude; + } + + public void setExclude(Set exclude) { + this.exclude = exclude; + } + + public Map getPathMapping() { + return this.pathMapping; + } + + public void setPathMapping(Map pathMapping) { + this.pathMapping = pathMapping; + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java index fab0195f347..7ef32c67075 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java @@ -20,14 +20,11 @@ import java.util.HashSet; import org.glassfish.jersey.server.ResourceConfig; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -49,27 +46,19 @@ import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(ResourceConfig.class) -@ConditionalOnBean(ResourceConfig.class) +@ConditionalOnBean({ ResourceConfig.class, WebAnnotationEndpointDiscoverer.class }) @ConditionalOnMissingBean(type = "org.springframework.web.servlet.DispatcherServlet") class JerseyWebEndpointManagementContextConfiguration { @Bean public ResourceConfigCustomizer webEndpointRegistrar( - EndpointProvider provider, + WebAnnotationEndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes, WebEndpointProperties webEndpointProperties) { return (resourceConfig) -> resourceConfig.registerResources( new HashSet<>(new JerseyEndpointResourceFactory().createEndpointResources( new EndpointMapping(webEndpointProperties.getBasePath()), - provider.getEndpoints(), endpointMediaTypes))); - } - - @Bean - @ConditionalOnMissingBean - public EndpointPathProvider endpointPathProvider( - EndpointProvider provider, - WebEndpointProperties webEndpointProperties) { - return new DefaultEndpointPathProvider(provider, webEndpointProperties); + endpointDiscoverer.discoverEndpoints(), endpointMediaTypes))); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java index d8034449b94..d09729664ba 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java @@ -16,15 +16,13 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; @@ -40,25 +38,18 @@ import org.springframework.context.annotation.Bean; */ @ManagementContextConfiguration @ConditionalOnWebApplication(type = Type.REACTIVE) +@ConditionalOnBean(WebAnnotationEndpointDiscoverer.class) public class WebFluxEndpointManagementContextConfiguration { @Bean @ConditionalOnMissingBean public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping( - EndpointProvider provider, + WebAnnotationEndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes, WebEndpointProperties webEndpointProperties) { return new WebFluxEndpointHandlerMapping( new EndpointMapping(webEndpointProperties.getBasePath()), - provider.getEndpoints(), endpointMediaTypes); - } - - @Bean - @ConditionalOnMissingBean - public EndpointPathProvider endpointPathProvider( - EndpointProvider provider, - WebEndpointProperties webEndpointProperties) { - return new DefaultEndpointPathProvider(provider, webEndpointProperties); + endpointDiscoverer.discoverEndpoints(), endpointMediaTypes); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/CorsEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/CorsEndpointProperties.java index ec1c5412f73..b00a574d7a5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/CorsEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/CorsEndpointProperties.java @@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Andy Wilkinson * @since 2.0.0 */ -@ConfigurationProperties(prefix = "management.endpoints.cors") +@ConfigurationProperties(prefix = "management.endpoints.web.cors") public class CorsEndpointProperties { /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java index 984cc265b01..35d3a3b80b3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java @@ -16,15 +16,11 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; -import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -45,35 +41,27 @@ import org.springframework.web.servlet.DispatcherServlet; * @author Phillip Webb * @since 2.0.0 */ + @ManagementContextConfiguration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) -@ConditionalOnBean(DispatcherServlet.class) -@EnableConfigurationProperties({ CorsEndpointProperties.class, - WebEndpointProperties.class, ManagementServerProperties.class }) +@ConditionalOnBean({ DispatcherServlet.class, WebAnnotationEndpointDiscoverer.class }) +@EnableConfigurationProperties(CorsEndpointProperties.class) public class WebMvcEndpointManagementContextConfiguration { @Bean @ConditionalOnMissingBean public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping( - EndpointProvider provider, + WebAnnotationEndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties) { WebMvcEndpointHandlerMapping handlerMapping = new WebMvcEndpointHandlerMapping( new EndpointMapping(webEndpointProperties.getBasePath()), - provider.getEndpoints(), endpointMediaTypes, + endpointDiscoverer.discoverEndpoints(), endpointMediaTypes, getCorsConfiguration(corsProperties)); return handlerMapping; } - @Bean - @ConditionalOnMissingBean - public EndpointPathProvider endpointPathProvider( - EndpointProvider provider, - WebEndpointProperties webEndpointProperties) { - return new DefaultEndpointPathProvider(provider, webEndpointProperties); - } - private CorsConfiguration getCorsConfiguration(CorsEndpointProperties properties) { if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) { return null; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java index c460d013493..a378d46b23f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java @@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.env; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.env.EnvironmentEndpoint; -import org.springframework.boot.actuate.env.EnvironmentWebEndpointExtension; +import org.springframework.boot.actuate.env.EnvironmentEndpointWebExtension; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -61,9 +61,9 @@ public class EnvironmentEndpointAutoConfiguration { @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(EnvironmentEndpoint.class) - public EnvironmentWebEndpointExtension environmentWebEndpointExtension( + public EnvironmentEndpointWebExtension environmentWebEndpointExtension( EnvironmentEndpoint environmentEndpoint) { - return new EnvironmentWebEndpointExtension(environmentEndpoint); + return new EnvironmentEndpointWebExtension(environmentEndpoint); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointProperties.java index 0d0e0282c03..59934895395 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointProperties.java @@ -25,7 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Stephane Nicoll * @since 2.0.0 */ -@ConfigurationProperties("endpoints.env") +@ConfigurationProperties("management.endpoint.env") public class EnvironmentEndpointProperties { /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java index 3a490561367..8d6a7d705f4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java @@ -25,15 +25,15 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfi import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicatorFactory; import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthEndpoint; +import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.HealthReactiveWebEndpointExtension; import org.springframework.boot.actuate.health.HealthStatusHttpMapper; -import org.springframework.boot.actuate.health.HealthWebEndpointExtension; import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension; import org.springframework.boot.actuate.health.StatusEndpoint; -import org.springframework.boot.actuate.health.StatusReactiveWebEndpointExtension; -import org.springframework.boot.actuate.health.StatusWebEndpointExtension; +import org.springframework.boot.actuate.health.StatusEndpointWebExtension; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; @@ -84,9 +84,9 @@ public class HealthWebEndpointManagementContextConfiguration { @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(HealthEndpoint.class) - public HealthReactiveWebEndpointExtension healthWebEndpointExtension( + public ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension( HealthStatusHttpMapper healthStatusHttpMapper) { - return new HealthReactiveWebEndpointExtension(this.reactiveHealthIndicator, + return new ReactiveHealthEndpointWebExtension(this.reactiveHealthIndicator, healthStatusHttpMapper); } @@ -94,9 +94,9 @@ public class HealthWebEndpointManagementContextConfiguration { @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(StatusEndpoint.class) - public StatusReactiveWebEndpointExtension statusWebEndpointExtension( + public ReactiveStatusEndpointWebExtension reactiveStatusEndpointWebExtension( HealthStatusHttpMapper healthStatusHttpMapper) { - return new StatusReactiveWebEndpointExtension(this.reactiveHealthIndicator, + return new ReactiveStatusEndpointWebExtension(this.reactiveHealthIndicator, healthStatusHttpMapper); } @@ -110,18 +110,18 @@ public class HealthWebEndpointManagementContextConfiguration { @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(HealthEndpoint.class) - public HealthWebEndpointExtension healthWebEndpointExtension( + public HealthEndpointWebExtension healthEndpointWebExtension( HealthEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) { - return new HealthWebEndpointExtension(delegate, healthStatusHttpMapper); + return new HealthEndpointWebExtension(delegate, healthStatusHttpMapper); } @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint @ConditionalOnBean(StatusEndpoint.class) - public StatusWebEndpointExtension statusWebEndpointExtension( + public StatusEndpointWebExtension statusEndpointWebExtension( StatusEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) { - return new StatusWebEndpointExtension(delegate, healthStatusHttpMapper); + return new StatusEndpointWebExtension(delegate, healthStatusHttpMapper); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfiguration.java index 37b19e9a4db..ba520a499eb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfiguration.java @@ -71,10 +71,11 @@ public class LogFileWebEndpointManagementContextConfiguration { return ConditionOutcome .match(message.found("logging.path").items(config)); } - config = environment.getProperty("endpoints.logfile.external-file"); + config = environment.getProperty("management.endpoint.logfile.external-file"); if (StringUtils.hasText(config)) { - return ConditionOutcome.match( - message.found("endpoints.logfile.external-file").items(config)); + return ConditionOutcome + .match(message.found("management.endpoint.logfile.external-file") + .items(config)); } return ConditionOutcome.noMatch(message.didNotFind("logging file").atAll()); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointProperties.java index 0ebb85b0bfb..095235a85ca 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointProperties.java @@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Stephane Nicoll * @since 2.0.0 */ -@ConfigurationProperties(prefix = "endpoints.logfile") +@ConfigurationProperties(prefix = "management.endpoint.logfile") public class LogFileWebEndpointProperties { /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 28d48282386..06a21e76b16 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,6 +1,6 @@ {"properties": [ { - "name": "endpoints.configprops.keys-to-sanitize", + "name": "management.endpoint.configprops.keys-to-sanitize", "defaultValue": [ "password", "secret", @@ -11,25 +11,12 @@ ] }, { - "name": "endpoints.default.enabled", + "name": "management.endpoints.enabled-by-default", "type": "java.lang.Boolean", - "description": "Enable all endpoints by default.", - "defaultValue": true + "description": "Enable or disable all endpoints by default." }, { - "name": "endpoints.default.jmx.enabled", - "type": "java.lang.Boolean", - "description": "Enable all endpoints as JMX MBeans by default.", - "defaultValue": true - }, - { - "name": "endpoints.default.web.enabled", - "type": "java.lang.Boolean", - "description": "Enable all endpoints as Web endpoints by default.", - "defaultValue": false - }, - { - "name": "endpoints.env.keys-to-sanitize", + "name": "management.endpoint.env.keys-to-sanitize", "defaultValue": [ "password", "secret", @@ -40,7 +27,7 @@ ] }, { - "name": "endpoints.trace.filter.enabled", + "name": "management.endpoint.trace.filter.enabled", "type": "java.lang.Boolean", "description": "Enable the trace servlet filter.", "defaultValue": true, @@ -321,7 +308,7 @@ "type": "java.lang.Boolean", "description": "Set whether credentials are supported. When not set, credentials are not supported.", "deprecation": { - "replacement": "management.endpoints.cors.allow-credentials", + "replacement": "management.endpoints.web.cors.allow-credentials", "level": "error" } }, @@ -330,7 +317,7 @@ "type": "java.util.List", "description": "Comma-separated list of headers to allow in a request. '*' allows all headers.", "deprecation": { - "replacement": "management.endpoints.cors.allowed-headers", + "replacement": "management.endpoints.web.cors.allowed-headers", "level": "error" } }, @@ -339,7 +326,7 @@ "type": "java.util.List", "description": "Comma-separated list of methods to allow. '*' allows all methods. When not set,\n defaults to GET.", "deprecation": { - "replacement": "management.endpoints.cors.allowed-methods", + "replacement": "management.endpoints.web.cors.allowed-methods", "level": "error" } }, @@ -348,7 +335,7 @@ "type": "java.util.List", "description": "Comma-separated list of origins to allow. '*' allows all origins. When not set,\n CORS support is disabled.", "deprecation": { - "replacement": "management.endpoints.cors.allowed-origins", + "replacement": "management.endpoints.web.cors.allowed-origins", "level": "error" } }, @@ -357,7 +344,7 @@ "type": "java.util.List", "description": "Comma-separated list of headers to include in a response.", "deprecation": { - "replacement": "management.endpoints.cors.exposed-headers", + "replacement": "management.endpoints.web.cors.exposed-headers", "level": "error" } }, @@ -367,7 +354,7 @@ "description": "How long, in seconds, the response from a pre-flight request can be cached by\n clients.", "defaultValue": 1800, "deprecation": { - "replacement": "management.endpoints.cors.max-age", + "replacement": "management.endpoints.web.cors.max-age", "level": "error" } }, @@ -1202,7 +1189,7 @@ } ],"hints": [ { - "name": "management.endpoints.cors.allowed-headers", + "name": "management.endpoints.web.cors.allowed-headers", "values": [ { "value": "*" @@ -1215,7 +1202,7 @@ ] }, { - "name": "management.endpoints.cors.allowed-methods", + "name": "management.endpoints.web.cors.allowed-methods", "values": [ { "value": "*" @@ -1228,7 +1215,7 @@ ] }, { - "name": "management.endpoints.cors.allowed-origins", + "name": "management.endpoints.web.cors.allowed-origins", "values": [ { "value": "*" diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index 930d73f44d1..98ec8c726f1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -13,6 +13,7 @@ org.springframework.boot.actuate.autoconfigure.couchbase.CouchbaseHealthIndicato org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticsearchHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.flyway.FlywayEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfigurationTests.java index b78d91a030a..309e77c325b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfigurationTests.java @@ -19,8 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.audit; import org.junit.Test; import org.springframework.boot.actuate.audit.AuditEventsEndpoint; +import org.springframework.boot.actuate.audit.AuditEventsEndpointWebExtension; import org.springframework.boot.actuate.audit.AuditEventsJmxEndpointExtension; -import org.springframework.boot.actuate.audit.AuditEventsWebEndpointExtension; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -54,17 +54,18 @@ public class AuditEventsEndpointAutoConfigurationTests { @Test public void runShouldHaveWebExtensionBean() { this.contextRunner.run((context) -> assertThat(context) - .hasSingleBean(AuditEventsWebEndpointExtension.class)); + .hasSingleBean(AuditEventsEndpointWebExtension.class)); } @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointOrExtensionBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.auditevents.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.auditevents.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(AuditEventsEndpoint.class) .doesNotHaveBean(AuditEventsJmxEndpointExtension.class) - .doesNotHaveBean(AuditEventsWebEndpointExtension.class)); + .doesNotHaveBean(AuditEventsEndpointWebExtension.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/beans/BeansEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/beans/BeansEndpointAutoConfigurationTests.java index 620e3b5c2ff..3fa11fb8c4a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/beans/BeansEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/beans/BeansEndpointAutoConfigurationTests.java @@ -44,8 +44,9 @@ public class BeansEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.beans.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(BeansEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.beans.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(BeansEndpoint.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java index bc05eef7f7b..dc4332e91ad 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java @@ -28,15 +28,16 @@ import reactor.core.publisher.Mono; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.boot.actuate.endpoint.ParameterMapper; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; @@ -217,7 +218,7 @@ public class CloudFoundryWebFluxEndpointIntegrationTests { @Bean public CloudFoundryWebFluxEndpointHandlerMapping cloudFoundryWebEndpointServletHandlerMapping( - WebAnnotationEndpointDiscoverer webEndpointDiscoverer, + EndpointDiscoverer webEndpointDiscoverer, EndpointMediaTypes endpointMediaTypes, ReactiveCloudFoundrySecurityInterceptor interceptor) { CorsConfiguration corsConfiguration = new CorsConfiguration(); @@ -236,8 +237,8 @@ public class CloudFoundryWebFluxEndpointIntegrationTests { ParameterMapper parameterMapper = new ConversionServiceParameterMapper( DefaultConversionService.getSharedInstance()); return new WebAnnotationEndpointDiscoverer(applicationContext, - parameterMapper, (id) -> new CachingConfiguration(0), - endpointMediaTypes, EndpointPathResolver.useEndpointId()); + parameterMapper, endpointMediaTypes, + EndpointPathResolver.useEndpointId(), null, null); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java index a34cb913401..9775745cfb6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java @@ -18,18 +18,20 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; @@ -190,16 +192,17 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests { } @Test - public void allEndpointsAvailableUnderCloudFoundryWithoutEnablingWeb() + public void allEndpointsAvailableUnderCloudFoundryWithoutEnablingWebInclues() throws Exception { setupContextWithCloudEnabled(); this.context.register(TestConfiguration.class); this.context.refresh(); CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(); - List> endpoints = (List>) handlerMapping + List> endpoints = (List>) handlerMapping .getEndpoints(); - assertThat(endpoints.size()).isEqualTo(1); - assertThat(endpoints.get(0).getId()).isEqualTo("test"); + List endpointIds = endpoints.stream().map(EndpointInfo::getId) + .collect(Collectors.toList()); + assertThat(endpointIds).contains("test"); } @Test @@ -208,12 +211,14 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests { this.context.register(TestConfiguration.class); this.context.refresh(); CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(); - List> endpoints = (List>) handlerMapping + 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"); + EndpointInfo endpoint = endpoints.stream() + .filter((candidate) -> "test".equals(candidate.getId())).findFirst() + .get(); + assertThat(endpoint.getOperations()).hasSize(1); + WebOperation operation = endpoint.getOperations().iterator().next(); + assertThat(operation.getRequestPredicate().getPath()).isEqualTo("test"); } private void setupContextWithCloudEnabled() { @@ -231,6 +236,7 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests { PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class, WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java index 9d77d3e16c1..1dd27bb16da 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet; import java.util.Arrays; +import java.util.Collection; import java.util.List; import org.junit.After; @@ -24,13 +25,14 @@ import org.junit.Before; import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; @@ -79,7 +81,7 @@ public class CloudFoundryActuatorAutoConfigurationTests { RestTemplateAutoConfiguration.class, ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, - EndpointAutoConfiguration.class, + EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class); } @@ -208,30 +210,34 @@ public class CloudFoundryActuatorAutoConfigurationTests { } @Test - public void allEndpointsAvailableUnderCloudFoundryWithoutEnablingWeb() + public void allEndpointsAvailableUnderCloudFoundryWithoutExposeAllOnWeb() throws Exception { this.context.register(TestConfiguration.class); this.context.refresh(); CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping(); - List> endpoints = (List>) handlerMapping + List> endpoints = (List>) handlerMapping .getEndpoints(); - assertThat(endpoints.size()).isEqualTo(1); - assertThat(endpoints.get(0).getId()).isEqualTo("test"); + assertThat(endpoints.stream() + .filter((candidate) -> "test".equals(candidate.getId())).findFirst()) + .isNotEmpty(); } @Test public void endpointPathCustomizationIsNotApplied() throws Exception { - TestPropertyValues.of("endpoints.test.web.path=another/custom") + TestPropertyValues.of("management.endpoints.web.path-mapping.test=custom") .applyTo(this.context); this.context.register(TestConfiguration.class); this.context.refresh(); CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping(); - List> endpoints = (List>) handlerMapping + 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"); + EndpointInfo endpoint = endpoints.stream() + .filter((candidate) -> "test".equals(candidate.getId())).findFirst() + .get(); + Collection operations = endpoint.getOperations(); + assertThat(operations).hasSize(1); + assertThat(operations.iterator().next().getRequestPredicate().getPath()) + .isEqualTo("test"); } private CloudFoundryWebEndpointServletHandlerMapping getHandlerMapping() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java index 49474003821..04faa3bf5e4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java @@ -27,15 +27,16 @@ import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.boot.actuate.endpoint.ParameterMapper; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; @@ -203,7 +204,7 @@ public class CloudFoundryMvcWebEndpointIntegrationTests { @Bean public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping( - WebAnnotationEndpointDiscoverer webEndpointDiscoverer, + EndpointDiscoverer webEndpointDiscoverer, EndpointMediaTypes endpointMediaTypes, CloudFoundrySecurityInterceptor interceptor) { CorsConfiguration corsConfiguration = new CorsConfiguration(); @@ -222,8 +223,8 @@ public class CloudFoundryMvcWebEndpointIntegrationTests { ParameterMapper parameterMapper = new ConversionServiceParameterMapper( DefaultConversionService.getSharedInstance()); return new WebAnnotationEndpointDiscoverer(applicationContext, - parameterMapper, (id) -> new CachingConfiguration(0), - endpointMediaTypes, EndpointPathResolver.useEndpointId()); + parameterMapper, endpointMediaTypes, + EndpointPathResolver.useEndpointId(), null, null); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfigurationTests.java index 95a3eb29c9a..a95ceb9fc8c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfigurationTests.java @@ -43,7 +43,8 @@ public class ConditionsReportEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.conditions.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.conditions.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(ConditionsReportEndpoint.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/ShutdownEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/ShutdownEndpointAutoConfigurationTests.java index 71a79169707..665207a1cf4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/ShutdownEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/ShutdownEndpointAutoConfigurationTests.java @@ -37,15 +37,18 @@ public class ShutdownEndpointAutoConfigurationTests { @Test public void runShouldHaveEndpointBean() { - this.contextRunner.withPropertyValues("endpoints.shutdown.enabled:true").run( - (context) -> assertThat(context).hasSingleBean(ShutdownEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.shutdown.enabled:true") + .run((context) -> assertThat(context) + .hasSingleBean(ShutdownEndpoint.class)); } @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.shutdown.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(ShutdownEndpoint.class)); + this.contextRunner + .withPropertyValues("management.endpoint.shutdown.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(ShutdownEndpoint.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java index c7e3701171d..f57788cb1ff 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java @@ -53,7 +53,8 @@ public class ConfigurationPropertiesReportEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.configprops.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.configprops.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(ConfigurationPropertiesReportEndpoint.class)); } @@ -62,7 +63,7 @@ public class ConfigurationPropertiesReportEndpointAutoConfigurationTests { public void keysToSanitizeCanBeConfiguredViaTheEnvironment() throws Exception { this.contextRunner.withUserConfiguration(Config.class) .withPropertyValues( - "endpoints.configprops.keys-to-sanitize: .*pass.*, property") + "management.endpoint.configprops.keys-to-sanitize: .*pass.*, property") .run(validateTestProperties("******", "******")); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProviderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProviderTests.java deleted file mode 100644 index 8485f863267..00000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointEnablementProviderTests.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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.DefaultEnablement; -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.mock.env.MockEnvironment; -import org.springframework.util.ObjectUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link EndpointEnablementProvider}. - * - * @author Stephane Nicoll - */ -public class EndpointEnablementProviderTests { - - @Test - public void defaultEnablementDisabled() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED); - validate(enablement, false, "endpoint 'foo' is disabled by default"); - } - - @Test - public void defaultEnablementDisabledWithGeneralEnablement() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED, "endpoints.default.enabled=true"); - validate(enablement, false, "endpoint 'foo' is disabled by default"); - } - - @Test - public void defaultEnablementDisabledWithGeneralTechEnablement() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED, EndpointExposure.WEB, - "endpoints.default.web.enabled=true"); - validate(enablement, false, "endpoint 'foo' (web) is disabled by default"); - } - - @Test - public void defaultEnablementDisabledWithOverride() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED, "endpoints.foo.enabled=true"); - validate(enablement, true, "found property endpoints.foo.enabled"); - } - - @Test - public void defaultEnablementDisabledWithTechOverride() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED, EndpointExposure.WEB, - "endpoints.foo.web.enabled=true"); - validate(enablement, true, "found property endpoints.foo.web.enabled"); - } - - @Test - public void defaultEnablementDisabledWithIrrelevantTechOverride() { - EndpointEnablement enablement = getEndpointEnablement("foo", - DefaultEnablement.DISABLED, EndpointExposure.WEB, - "endpoints.foo.jmx.enabled=true"); - validate(enablement, false, "endpoint 'foo' (web) is disabled by default"); - } - - @Test - public void defaultEnablementEnabled() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED); - validate(enablement, true, "endpoint 'bar' is enabled by default"); - } - - @Test - public void defaultEnablementEnabledWithGeneralDisablement() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED, "endpoints.default.enabled=false"); - validate(enablement, true, "endpoint 'bar' is enabled by default"); - } - - @Test - public void defaultEnablementEnabledWithGeneralTechDisablement() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED, EndpointExposure.JMX, - "endpoints.default.jmx.enabled=false"); - validate(enablement, true, "endpoint 'bar' (jmx) is enabled by default"); - } - - @Test - public void defaultEnablementEnabledWithOverride() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED, "endpoints.bar.enabled=false"); - validate(enablement, false, "found property endpoints.bar.enabled"); - } - - @Test - public void defaultEnablementEnabledWithTechOverride() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED, EndpointExposure.JMX, - "endpoints.bar.jmx.enabled=false"); - validate(enablement, false, "found property endpoints.bar.jmx.enabled"); - } - - @Test - public void defaultEnablementEnabledWithIrrelevantTechOverride() { - EndpointEnablement enablement = getEndpointEnablement("bar", - DefaultEnablement.ENABLED, EndpointExposure.JMX, - "endpoints.bar.web.enabled=false"); - validate(enablement, true, "endpoint 'bar' (jmx) is enabled by default"); - } - - @Test - public void defaultEnablementNeutral() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL); - validate(enablement, true, "endpoint 'biz' is enabled (default)"); - } - - @Test - public void defaultEnablementNeutralWeb() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.WEB); - validate(enablement, false, "endpoint 'default' (web) is disabled by default"); - } - - @Test - public void defaultEnablementNeutralJmx() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX); - validate(enablement, true, - "endpoint 'biz' (jmx) is enabled (default for jmx endpoints)"); - } - - @Test - public void defaultEnablementNeutralWithGeneralDisablement() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, "endpoints.default.enabled=false"); - validate(enablement, false, "found property endpoints.default.enabled"); - } - - @Test - public void defaultEnablementNeutralJmxWithTechDisablement() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.jmx.enabled=false"); - validate(enablement, false, "found property endpoints.default.jmx.enabled"); - } - - @Test - public void defaultEnablementNeutralTechTakesPrecedence() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.enabled=true", "endpoints.default.jmx.enabled=false"); - validate(enablement, false, "found property endpoints.default.jmx.enabled"); - } - - @Test - public void defaultEnablementNeutralWebWithTechEnablement() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.WEB, - "endpoints.default.web.enabled=true"); - validate(enablement, true, "found property endpoints.default.web.enabled"); - } - - @Test - public void defaultEnablementNeutralJmxWithUnrelatedTechDisablement() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.web.enabled=false"); - validate(enablement, true, - "endpoint 'biz' (jmx) is enabled (default for jmx endpoints)"); - } - - @Test - public void defaultEnablementNeutralWithOverride() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, "endpoints.biz.enabled=false"); - validate(enablement, false, "found property endpoints.biz.enabled"); - } - - @Test - public void defaultEnablementNeutralWebWithOverride() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.WEB, - "endpoints.biz.web.enabled=true"); - validate(enablement, true, "found property endpoints.biz.web.enabled"); - } - - @Test - public void defaultEnablementNeutralJmxWithOverride() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.biz.jmx.enabled=false"); - validate(enablement, false, "found property endpoints.biz.jmx.enabled"); - } - - @Test - public void defaultEnablementNeutralTechTakesPrecedenceOnEverything() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.enabled=false", "endpoints.default.jmx.enabled=false", - "endpoints.biz.enabled=false", "endpoints.biz.jmx.enabled=true"); - validate(enablement, true, "found property endpoints.biz.jmx.enabled"); - } - - @Test - public void defaultEnablementNeutralSpecificTakesPrecedenceOnDefaults() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.enabled=false", "endpoints.default.jmx.enabled=false", - "endpoints.biz.enabled=true"); - validate(enablement, true, "found property endpoints.biz.enabled"); - } - - @Test - public void defaultEnablementNeutralDefaultTechTakesPrecedenceOnGeneralDefault() { - EndpointEnablement enablement = getEndpointEnablement("biz", - DefaultEnablement.NEUTRAL, EndpointExposure.JMX, - "endpoints.default.enabled=false", "endpoints.default.jmx.enabled=true"); - validate(enablement, true, "found property endpoints.default.jmx.enabled"); - } - - private EndpointEnablement getEndpointEnablement(String id, - DefaultEnablement defaultEnablement, String... environment) { - return getEndpointEnablement(id, defaultEnablement, null, environment); - } - - private EndpointEnablement getEndpointEnablement(String id, - DefaultEnablement defaultEnablement, EndpointExposure exposure, - String... environment) { - MockEnvironment env = new MockEnvironment(); - TestPropertyValues.of(environment).applyTo(env); - EndpointEnablementProvider provider = new EndpointEnablementProvider(env); - if (exposure != null) { - return provider.getEndpointEnablement(id, defaultEnablement, exposure); - } - return provider.getEndpointEnablement(id, defaultEnablement); - } - - private void validate(EndpointEnablement enablement, boolean enabled, - String... messages) { - assertThat(enablement).isNotNull(); - assertThat(enablement.isEnabled()).isEqualTo(enabled); - if (!ObjectUtils.isEmpty(messages)) { - assertThat(enablement.getReason()).contains(messages); - } - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactoryTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunctionTests.java similarity index 56% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactoryTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunctionTests.java index fcc50187390..8e033bd102b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactoryTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunctionTests.java @@ -16,39 +16,39 @@ package org.springframework.boot.actuate.autoconfigure.endpoint; +import java.util.function.Function; + import org.junit.Test; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; -import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link DefaultCachingConfigurationFactory}. + * Tests for {@link EndpointIdTimeToLivePropertyFunction}. * * @author Stephane Nicoll + * @author Phillip Webb */ -public class DefaultCachingConfigurationFactoryTests { +public class EndpointIdTimeToLivePropertyFunctionTests { private final MockEnvironment environment = new MockEnvironment(); - private final CachingConfigurationFactory factory = new DefaultCachingConfigurationFactory( + private final Function timeToLive = new EndpointIdTimeToLivePropertyFunction( this.environment); @Test public void defaultConfiguration() { - CachingConfiguration configuration = this.factory.getCachingConfiguration("test"); - assertThat(configuration).isNotNull(); - assertThat(configuration.getTimeToLive()).isEqualTo(0); + Long result = this.timeToLive.apply("test"); + assertThat(result).isNull(); } @Test public void userConfiguration() { - this.environment.setProperty("endpoints.test.cache.time-to-live", "500"); - CachingConfiguration configuration = this.factory.getCachingConfiguration("test"); - assertThat(configuration).isNotNull(); - assertThat(configuration.getTimeToLive()).isEqualTo(500); + this.environment.setProperty("management.endpoint.test.cache.time-to-live", + "500"); + Long result = this.timeToLive.apply("test"); + assertThat(result).isEqualTo(500L); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilterTests.java new file mode 100644 index 00000000000..fc04aeb7277 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilterTests.java @@ -0,0 +1,183 @@ +/* + * 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 java.util.Collections; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ExposeExcludePropertyEndpointFilter}. + * + * @author Phillip Webb + */ +public class ExposeExcludePropertyEndpointFilterTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private MockEnvironment environment = new MockEnvironment(); + + private EndpointFilter filter; + + @Mock + private TestEndpointDiscoverer discoverer; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void createWhenDiscovererTypeIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Discoverer Type must not be null"); + new ExposeExcludePropertyEndpointFilter<>(null, this.environment, "foo"); + } + + @Test + public void createWhenEnvironmentIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Environment must not be null"); + new ExposeExcludePropertyEndpointFilter<>(TestEndpointDiscoverer.class, null, + "foo"); + } + + @Test + public void createWhenPrefixIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Prefix must not be empty"); + new ExposeExcludePropertyEndpointFilter(TestEndpointDiscoverer.class, + this.environment, null); + } + + @Test + public void createWhenPrefixIsEmptyShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Prefix must not be empty"); + new ExposeExcludePropertyEndpointFilter(TestEndpointDiscoverer.class, + this.environment, ""); + } + + @Test + public void matchWhenExposeIsEmptyAndExcludeIsEmptyAndInDefaultShouldMatch() + throws Exception { + setupFilter("", ""); + assertThat(match("def")).isTrue(); + } + + @Test + public void matchWhenExposeIsEmptyAndExcludeIsEmptyAndNotInDefaultShouldNotMatch() + throws Exception { + setupFilter("", ""); + assertThat(match("bar")).isFalse(); + } + + @Test + public void matchWhenExposeMatchesAndExcludeIsEmptyShouldMatch() throws Exception { + setupFilter("bar", ""); + assertThat(match("bar")).isTrue(); + } + + @Test + public void matchWhenExposeDoesNotMatchAndExcludeIsEmptyShouldNotMatch() + throws Exception { + setupFilter("bar", ""); + assertThat(match("baz")).isFalse(); + } + + @Test + public void matchWhenExposeMatchesAndExcludeMatchesShouldNotMatch() throws Exception { + setupFilter("bar,baz", "baz"); + assertThat(match("baz")).isFalse(); + } + + @Test + public void matchWhenExposeMatchesAndExcludeDoesNotMatchShouldMatch() + throws Exception { + setupFilter("bar,baz", "buz"); + assertThat(match("baz")).isTrue(); + } + + @Test + public void matchWhenExposeMatchesWithDifferentCaseShouldMatch() throws Exception { + setupFilter("bar", ""); + assertThat(match("bAr")).isTrue(); + } + + @Test + public void matchWhenDicovererDoesNotMatchShouldMatch() throws Exception { + this.environment.setProperty("foo.expose", "bar"); + this.environment.setProperty("foo.exclude", ""); + this.filter = new ExposeExcludePropertyEndpointFilter<>( + DifferentTestEndpointDiscoverer.class, this.environment, "foo"); + assertThat(match("baz")).isTrue(); + } + + @Test + public void matchWhenIncludeIsAsteriskShouldMatchAll() throws Exception { + setupFilter("*", "buz"); + assertThat(match("bar")).isTrue(); + assertThat(match("baz")).isTrue(); + assertThat(match("buz")).isFalse(); + } + + @Test + public void matchWhenExcludeIsAsteriskShouldMatchNone() throws Exception { + setupFilter("bar,baz,buz", "*"); + assertThat(match("bar")).isFalse(); + assertThat(match("baz")).isFalse(); + assertThat(match("buz")).isFalse(); + } + + private void setupFilter(String expose, String exclude) { + this.environment.setProperty("foo.expose", expose); + this.environment.setProperty("foo.exclude", exclude); + this.filter = new ExposeExcludePropertyEndpointFilter<>( + TestEndpointDiscoverer.class, this.environment, "foo", "def"); + } + + private boolean match(String id) { + EndpointInfo info = new EndpointInfo<>(id, true, + Collections.emptyList()); + return this.filter.match(info, this.discoverer); + } + + private abstract static class TestEndpointDiscoverer + implements EndpointDiscoverer { + + } + + private abstract static class DifferentTestEndpointDiscoverer + implements EndpointDiscoverer { + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpointTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpointTests.java index 8c3ccea6f8f..aac943b2279 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnEnabledEndpointTests.java @@ -18,11 +18,12 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.condition; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; -import org.springframework.boot.actuate.endpoint.EndpointExposure; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; +import org.springframework.boot.actuate.endpoint.Operation; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -40,324 +41,158 @@ public class ConditionalOnEnabledEndpointTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); @Test - public void enabledByDefault() { - this.contextRunner.withUserConfiguration(FooConfig.class) + public void outcomeWhenEndpointEnabledPropertyIsTrueShouldMatch() throws Exception { + this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=true") + .withUserConfiguration( + FooEndpointEnabledByDefaultFalseConfiguration.class) .run((context) -> assertThat(context).hasBean("foo")); } @Test - public void disabledViaSpecificProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.foo.enabled=false") + public void outcomeWhenEndpointEnabledPropertyIsFalseShouldNotMatch() + throws Exception { + this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=false") + .withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class) .run((context) -> assertThat(context).doesNotHaveBean("foo")); } @Test - public void disabledViaGeneralProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false") + public void outcomeWhenNoEndpointPropertyAndUserDefinedDefaultIsTrueShouldMatch() + throws Exception { + this.contextRunner + .withPropertyValues("management.endpoints.enabled-by-default=true") + .withUserConfiguration( + FooEndpointEnabledByDefaultFalseConfiguration.class) + .run((context) -> assertThat(context).hasBean("foo")); + } + + @Test + public void outcomeWhenNoEndpointPropertyAndUserDefinedDefaultIsFalseShouldNotMatch() + throws Exception { + this.contextRunner + .withPropertyValues("management.endpoints.enabled-by-default=false") + .withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class) .run((context) -> assertThat(context).doesNotHaveBean("foo")); } @Test - public void enabledOverrideViaSpecificProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.foo.enabled=true") + public void outcomeWhenNoPropertiesAndAnnotationIsEnabledByDefaultShouldMatch() + throws Exception { + this.contextRunner + .withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class) .run((context) -> assertThat(context).hasBean("foo")); } @Test - public void enabledOverrideViaSpecificWebProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.foo.enabled=false", - "endpoints.foo.web.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void enabledOverrideViaSpecificJmxProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.foo.enabled=false", - "endpoints.foo.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void enabledOverrideViaSpecificAnyProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.foo.enabled=false", - "endpoints.foo.web.enabled=false", - "endpoints.foo.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void enabledOverrideViaGeneralWebProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.default.web.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void enabledOverrideViaGeneralJmxProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.default.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void enabledOverrideViaGeneralAnyProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.default.web.enabled=false", - "endpoints.default.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("foo")); - } - - @Test - public void disabledEvenWithEnabledGeneralProperties() { - this.contextRunner.withUserConfiguration(FooConfig.class).withPropertyValues( - "endpoints.default.enabled=true", "endpoints.default.web.enabled=true", - "endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false") + public void outcomeWhenNoPropertiesAndAnnotationIsNotEnabledByDefaultShouldNotMatch() + throws Exception { + this.contextRunner + .withUserConfiguration( + FooEndpointEnabledByDefaultFalseConfiguration.class) .run((context) -> assertThat(context).doesNotHaveBean("foo")); } @Test - public void disabledByDefaultWithAnnotationFlag() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .run((context) -> assertThat(context).doesNotHaveBean("bar")); + public void outcomeWhenNoPropertiesAndExtensionAnnotationIsEnabledByDefaultShouldMatch() + throws Exception { + this.contextRunner + .withUserConfiguration( + FooEndpointAndExtensionEnabledByDefaultTrueConfiguration.class) + .run((context) -> assertThat(context).hasBean("foo").hasBean("fooExt")); } @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); + public void outcomeWhenNoPropertiesAndExtensionAnnotationIsNotEnabledByDefaultShouldNotMatch() + throws Exception { + this.contextRunner + .withUserConfiguration( + FooEndpointAndExtensionEnabledByDefaultFalseConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean("foo") + .doesNotHaveBean("fooExt")); } - @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.web.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); + @Endpoint(id = "foo", enableByDefault = true) + static class FooEndpointEnabledByDefaultTrue { + } - @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.jmx.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); + @Endpoint(id = "foo", enableByDefault = false) + static class FooEndpointEnabledByDefaultFalse { + } - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); + @EndpointExtension(endpoint = FooEndpointEnabledByDefaultTrue.class, filter = TestFilter.class) + static class FooEndpointExtensionEnabledByDefaultTrue { + } - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.web.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); + @EndpointExtension(endpoint = FooEndpointEnabledByDefaultFalse.class, filter = TestFilter.class) + static class FooEndpointExtensionEnabledByDefaultFalse { + } - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); - } + static class TestFilter implements EndpointFilter { - @Test - public void enabledOverrideWithAndAnnotationFlagAndAnyProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.web.enabled=false", - "endpoints.bar.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); - } - - @Test - public void enabledOnlyWebByDefault() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.default.web.enabled=true") - .run((context) -> assertThat(context).hasBean("onlyweb")); - } - - @Test - public void disabledOnlyWebViaEndpointProperty() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.onlyweb.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("onlyweb")); - } - - @Test - public void disabledOnlyWebViaSpecificTechProperty() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.onlyweb.web.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("onlyweb")); - } - - @Test - public void enableOverridesOnlyWebViaGeneralJmxPropertyHasNoEffect() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.default.jmx.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("onlyweb")); - } - - @Test - public void enableOverridesOnlyWebViaSpecificJmxPropertyHasNoEffect() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.onlyweb.jmx.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("onlyweb")); - } - - @Test - public void enableOverridesOnlyWebViaSpecificWebProperty() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.onlyweb.web.enabled=true") - .run((context) -> assertThat(context).hasBean("onlyweb")); - } - - @Test - public void disabledOnlyWebEvenWithEnabledGeneralProperties() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class).withPropertyValues( - "endpoints.default.enabled=true", "endpoints.default.web.enabled=true", - "endpoints.onlyweb.enabled=true", "endpoints.onlyweb.web.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("foo")); - } - - @Test - public void contextFailIfEndpointTypeIsNotDetected() { - this.contextRunner.withUserConfiguration(NonEndpointBeanConfig.class) - .run((context) -> assertThat(context).hasFailed()); - } - - @Test - public void webExtensionWithEnabledByDefaultEndpoint() { - this.contextRunner.withUserConfiguration(FooWebExtensionConfig.class) - .run((context) -> assertThat(context) - .hasSingleBean(FooWebEndpointExtension.class)); - } - - @Test - public void webExtensionWithEnabledByDefaultEndpointCanBeDisabled() { - this.contextRunner.withUserConfiguration(FooWebExtensionConfig.class) - .withPropertyValues("endpoints.foo.enabled=false") - .run((context) -> assertThat(context) - .doesNotHaveBean(FooWebEndpointExtension.class)); - } - - @Test - public void jmxExtensionWithEnabledByDefaultEndpoint() { - this.contextRunner.withUserConfiguration(FooJmxExtensionConfig.class) - .run((context) -> assertThat(context) - .hasSingleBean(FooJmxEndpointExtension.class)); - } - - @Test - public void jmxExtensionWithEnabledByDefaultEndpointCanBeDisabled() { - this.contextRunner.withUserConfiguration(FooJmxExtensionConfig.class) - .withPropertyValues("endpoints.foo.enabled=false") - .run((context) -> assertThat(context) - .doesNotHaveBean(FooJmxEndpointExtension.class)); - } - - @Configuration - static class FooConfig { - - @Bean - @ConditionalOnEnabledEndpoint - public FooEndpoint foo() { - return new FooEndpoint(); + @Override + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return true; } } - @Endpoint(id = "foo") - static class FooEndpoint { - - } - @Configuration - static class BarConfig { + static class FooEndpointEnabledByDefaultTrueConfiguration { @Bean @ConditionalOnEnabledEndpoint - public BarEndpoint bar() { - return new BarEndpoint(); + public FooEndpointEnabledByDefaultTrue foo() { + return new FooEndpointEnabledByDefaultTrue(); } } - @Endpoint(id = "bar", exposure = { EndpointExposure.WEB, - EndpointExposure.JMX }, defaultEnablement = DefaultEnablement.DISABLED) - static class BarEndpoint { - - } - @Configuration - static class OnlyWebConfig { + static class FooEndpointEnabledByDefaultFalseConfiguration { - @Bean(name = "onlyweb") + @Bean @ConditionalOnEnabledEndpoint - public OnlyWebEndpoint onlyWeb() { - return new OnlyWebEndpoint(); + public FooEndpointEnabledByDefaultFalse foo() { + return new FooEndpointEnabledByDefaultFalse(); } } - @Endpoint(id = "onlyweb", exposure = EndpointExposure.WEB) - static class OnlyWebEndpoint { - - } - @Configuration - static class NonEndpointBeanConfig { + static class FooEndpointAndExtensionEnabledByDefaultTrueConfiguration { @Bean @ConditionalOnEnabledEndpoint - public String foo() { - return "endpoint type cannot be detected"; + public FooEndpointEnabledByDefaultTrue foo() { + return new FooEndpointEnabledByDefaultTrue(); + } + + @Bean + @ConditionalOnEnabledEndpoint + public FooEndpointExtensionEnabledByDefaultTrue fooExt() { + return new FooEndpointExtensionEnabledByDefaultTrue(); } } - @JmxEndpointExtension(endpoint = FooEndpoint.class) - static class FooJmxEndpointExtension { - - } - @Configuration - static class FooJmxExtensionConfig { + static class FooEndpointAndExtensionEnabledByDefaultFalseConfiguration { @Bean @ConditionalOnEnabledEndpoint - FooJmxEndpointExtension fooJmxEndpointExtension() { - return new FooJmxEndpointExtension(); + public FooEndpointEnabledByDefaultFalse foo() { + return new FooEndpointEnabledByDefaultFalse(); } - } - - @WebEndpointExtension(endpoint = FooEndpoint.class) - static class FooWebEndpointExtension { - - } - - @Configuration - static class FooWebExtensionConfig { - @Bean @ConditionalOnEnabledEndpoint - FooWebEndpointExtension fooJmxEndpointExtension() { - return new FooWebEndpointExtension(); + public FooEndpointExtensionEnabledByDefaultFalse fooExt() { + return new FooEndpointExtensionEnabledByDefaultFalse(); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactoryTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactoryTests.java index 86b34f36c12..e401c1db470 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactoryTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactoryTests.java @@ -41,7 +41,7 @@ public class DefaultEndpointObjectNameFactoryTests { private final MockEnvironment environment = new MockEnvironment(); - private final JmxEndpointExporterProperties properties = new JmxEndpointExporterProperties( + private final JmxEndpointProperties properties = new JmxEndpointProperties( this.environment); private final MBeanServer mBeanServer = mock(MBeanServer.class); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProviderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProviderTests.java index 6a691a2f6a7..881af3bfa02 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProviderTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathProviderTests.java @@ -25,10 +25,9 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -41,7 +40,7 @@ import static org.mockito.BDDMockito.given; public class DefaultEndpointPathProviderTests { @Mock - private EndpointProvider endpointProvider; + private EndpointDiscoverer discoverer; @Before public void setup() { @@ -81,16 +80,13 @@ public class DefaultEndpointPathProviderTests { } private DefaultEndpointPathProvider createProvider(String contextPath) { - Collection> endpoints = new ArrayList<>(); - endpoints.add(new EndpointInfo<>("foo", DefaultEnablement.ENABLED, - Collections.emptyList())); - endpoints.add(new EndpointInfo<>("bar", DefaultEnablement.ENABLED, - Collections.emptyList())); - given(this.endpointProvider.getEndpoints()).willReturn(endpoints); + Collection> endpoints = new ArrayList<>(); + endpoints.add(new EndpointInfo<>("foo", true, Collections.emptyList())); + endpoints.add(new EndpointInfo<>("bar", true, Collections.emptyList())); + given(this.discoverer.discoverEndpoints()).willReturn(endpoints); WebEndpointProperties webEndpointProperties = new WebEndpointProperties(); webEndpointProperties.setBasePath(contextPath); - return new DefaultEndpointPathProvider(this.endpointProvider, - webEndpointProperties); + return new DefaultEndpointPathProvider(this.discoverer, webEndpointProperties); } } 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/web/DefaultEndpointPathResolverTests.java similarity index 67% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolverTests.java index 3e2c10fbaa5..45a58aec42c 100644 --- 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/web/DefaultEndpointPathResolverTests.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.endpoint; +package org.springframework.boot.actuate.autoconfigure.endpoint.web; + +import java.util.Collections; 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; @@ -30,20 +31,18 @@ import static org.assertj.core.api.Assertions.assertThat; */ 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"); + EndpointPathResolver resolver = new DefaultEndpointPathResolver( + Collections.emptyMap()); + assertThat(resolver.resolvePath("test")).isEqualTo("test"); } @Test public void userConfiguration() { - this.environment.setProperty("endpoints.test.web.path", "custom"); - assertThat(this.resolver.resolvePath("test")).isEqualTo("custom"); + EndpointPathResolver resolver = new DefaultEndpointPathResolver( + Collections.singletonMap("test", "custom")); + assertThat(resolver.resolvePath("test")).isEqualTo("custom"); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationTests.java similarity index 76% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationTests.java index dcfb3a73778..4488a8a6a15 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationTests.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure.endpoint; +package org.springframework.boot.actuate.autoconfigure.endpoint.web; import org.junit.Test; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -26,16 +27,16 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link EndpointAutoConfiguration}. + * Tests for {@link WebEndpointAutoConfiguration}. * * @author Andy Wilkinson */ -public class EndpointAutoConfigurationTests { +public class WebEndpointAutoConfigurationTests { @Test public void webApplicationConfiguresEndpointMediaTypes() { - new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class)) + new WebApplicationContextRunner().withConfiguration(AutoConfigurations + .of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class)) .run((context) -> { EndpointMediaTypes endpointMediaTypes = context .getBean(EndpointMediaTypes.class); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java index 2f5921b5302..9e4a1752126 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfigurationTests.java @@ -51,7 +51,7 @@ public class EnvironmentEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.env.enabled:false") + this.contextRunner.withPropertyValues("management.endpoint.env.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(EnvironmentEndpoint.class)); } @@ -59,7 +59,7 @@ public class EnvironmentEndpointAutoConfigurationTests { @Test public void keysToSanitizeCanBeConfiguredViaTheEnvironment() throws Exception { this.contextRunner.withSystemProperties("dbPassword=123456", "apiKey=123456") - .withPropertyValues("endpoints.env.keys-to-sanitize=.*pass.*") + .withPropertyValues("management.endpoint.env.keys-to-sanitize=.*pass.*") .run(validateSystemProperties("******", "123456")); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfigurationTests.java index 0d92ffb7c10..c7a7f48bd7b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfigurationTests.java @@ -49,8 +49,9 @@ public class FlywayEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.flyway.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(FlywayEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.flyway.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(FlywayEndpoint.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java index da09d4a0866..74e6eee6243 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java @@ -84,15 +84,18 @@ public class HealthEndpointAutoConfigurationTests { @Test public void runShouldHaveStatusEndpointBeanEvenIfDefaultIsDisabled() { - this.contextRunner.withPropertyValues("endpoints.default.enabled:false").run( - (context) -> assertThat(context).hasSingleBean(StatusEndpoint.class)); + // FIXME + this.contextRunner.withPropertyValues("management.endpoint.default.enabled:false") + .run((context) -> assertThat(context) + .hasSingleBean(StatusEndpoint.class)); } @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveStatusEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.status.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(StatusEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(StatusEndpoint.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointReactiveManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointReactiveManagementContextConfigurationTests.java index 2d4e918d43b..3e97ec6950d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointReactiveManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointReactiveManagementContextConfigurationTests.java @@ -20,9 +20,9 @@ import java.util.Map; import org.junit.Test; -import org.springframework.boot.actuate.health.HealthReactiveWebEndpointExtension; import org.springframework.boot.actuate.health.HealthStatusHttpMapper; -import org.springframework.boot.actuate.health.StatusReactiveWebEndpointExtension; +import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; +import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.test.util.ReflectionTestUtils; @@ -46,33 +46,34 @@ public class HealthWebEndpointReactiveManagementContextConfigurationTests { @Test public void runShouldCreateExtensionBeans() throws Exception { this.contextRunner.run((context) -> assertThat(context) - .hasSingleBean(StatusReactiveWebEndpointExtension.class) - .hasSingleBean(HealthReactiveWebEndpointExtension.class)); + .hasSingleBean(ReactiveStatusEndpointWebExtension.class) + .hasSingleBean(ReactiveHealthEndpointWebExtension.class)); } @Test public void runWhenHealthEndpointIsDisabledShouldNotCreateExtensionBeans() throws Exception { - this.contextRunner.withPropertyValues("endpoints.health.enabled:false") + this.contextRunner.withPropertyValues("management.endpoint.health.enabled:false") .run((context) -> assertThat(context) - .doesNotHaveBean(HealthReactiveWebEndpointExtension.class)); + .doesNotHaveBean(ReactiveHealthEndpointWebExtension.class)); } @Test public void runWhenStatusEndpointIsDisabledShouldNotCreateExtensionBeans() throws Exception { - this.contextRunner.withPropertyValues("endpoints.status.enabled:false") + this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false") .run((context) -> assertThat(context) - .doesNotHaveBean(StatusReactiveWebEndpointExtension.class)); + .doesNotHaveBean(ReactiveStatusEndpointWebExtension.class)); } @Test public void runWithCustomHealthMappingShouldMapStatusCode() throws Exception { this.contextRunner - .withPropertyValues("management.health.status.http-mapping.CUSTOM=500") + .withPropertyValues( + "management.health.status.http-mapping.CUSTOM=500") .run((context) -> { Object extension = context - .getBean(HealthReactiveWebEndpointExtension.class); + .getBean(ReactiveHealthEndpointWebExtension.class); HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils .getField(extension, "statusHttpMapper"); Map statusMappings = mapper.getStatusMapping(); @@ -85,10 +86,11 @@ public class HealthWebEndpointReactiveManagementContextConfigurationTests { @Test public void runWithCustomStatusMappingShouldMapStatusCode() throws Exception { this.contextRunner - .withPropertyValues("management.health.status.http-mapping.CUSTOM=500") + .withPropertyValues( + "management.health.status.http-mapping.CUSTOM=500") .run((context) -> { Object extension = context - .getBean(StatusReactiveWebEndpointExtension.class); + .getBean(ReactiveStatusEndpointWebExtension.class); HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils .getField(extension, "statusHttpMapper"); Map statusMappings = mapper.getStatusMapping(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointServletManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointServletManagementContextConfigurationTests.java index 1f285025afa..a8f700061c2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointServletManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointServletManagementContextConfigurationTests.java @@ -20,9 +20,9 @@ import java.util.Map; import org.junit.Test; +import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.HealthStatusHttpMapper; -import org.springframework.boot.actuate.health.HealthWebEndpointExtension; -import org.springframework.boot.actuate.health.StatusWebEndpointExtension; +import org.springframework.boot.actuate.health.StatusEndpointWebExtension; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.test.util.ReflectionTestUtils; @@ -46,24 +46,24 @@ public class HealthWebEndpointServletManagementContextConfigurationTests { @Test public void runShouldCreateExtensionBeans() throws Exception { this.contextRunner.run((context) -> assertThat(context) - .hasSingleBean(StatusWebEndpointExtension.class) - .hasSingleBean(HealthWebEndpointExtension.class)); + .hasSingleBean(StatusEndpointWebExtension.class) + .hasSingleBean(HealthEndpointWebExtension.class)); } @Test public void runWhenHealthEndpointIsDisabledShouldNotCreateExtensionBeans() throws Exception { - this.contextRunner.withPropertyValues("endpoints.health.enabled:false") + this.contextRunner.withPropertyValues("management.endpoint.health.enabled:false") .run((context) -> assertThat(context) - .doesNotHaveBean(HealthWebEndpointExtension.class)); + .doesNotHaveBean(HealthEndpointWebExtension.class)); } @Test public void runWhenStatusEndpointIsDisabledShouldNotCreateExtensionBeans() throws Exception { - this.contextRunner.withPropertyValues("endpoints.status.enabled:false") + this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false") .run((context) -> assertThat(context) - .doesNotHaveBean(StatusWebEndpointExtension.class)); + .doesNotHaveBean(StatusEndpointWebExtension.class)); } @Test @@ -71,7 +71,7 @@ public class HealthWebEndpointServletManagementContextConfigurationTests { this.contextRunner .withPropertyValues("management.health.status.http-mapping.CUSTOM=500") .run((context) -> { - Object extension = context.getBean(HealthWebEndpointExtension.class); + Object extension = context.getBean(HealthEndpointWebExtension.class); HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils .getField(extension, "statusHttpMapper"); Map statusMappings = mapper.getStatusMapping(); @@ -86,7 +86,7 @@ public class HealthWebEndpointServletManagementContextConfigurationTests { this.contextRunner .withPropertyValues("management.health.status.http-mapping.CUSTOM=500") .run((context) -> { - Object extension = context.getBean(StatusWebEndpointExtension.class); + Object extension = context.getBean(StatusEndpointWebExtension.class); HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils .getField(extension, "statusHttpMapper"); Map statusMappings = mapper.getStatusMapping(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoEndpointAutoConfigurationTests.java index 809f0bf2355..ef3fa568ca4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoEndpointAutoConfigurationTests.java @@ -37,21 +37,23 @@ public class InfoEndpointAutoConfigurationTests { @Test public void runShouldHaveEndpointBean() { - this.contextRunner.withPropertyValues("endpoints.shutdown.enabled:true") + this.contextRunner.withPropertyValues("management.endpoint.shutdown.enabled:true") .run((context) -> assertThat(context).hasSingleBean(InfoEndpoint.class)); } @Test public void runShouldHaveEndpointBeanEvenIfDefaultIsDisabled() { - this.contextRunner.withPropertyValues("endpoints.default.enabled:false") + // FIXME + this.contextRunner.withPropertyValues("management.endpoint.default.enabled:false") .run((context) -> assertThat(context).hasSingleBean(InfoEndpoint.class)); } @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.info.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(InfoEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.info.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(InfoEndpoint.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java index 9a732a0a130..6e8d9afd7ab 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java @@ -61,8 +61,8 @@ public class JmxEndpointIntegrationTests { } @Test - public void jmxEndpointsCanBeDisabled() { - this.contextRunner.withPropertyValues("endpoints.default.jmx.enabled:false") + public void jmxEndpointsCanBeExcluded() { + this.contextRunner.withPropertyValues("management.endpoints.jmx.exclude:*") .run((context) -> { MBeanServer mBeanServer = context.getBean(MBeanServer.class); checkEndpointMBeans(mBeanServer, new String[0], @@ -74,9 +74,9 @@ public class JmxEndpointIntegrationTests { } @Test - public void singleJmxEndpointCanBeEnabled() { - this.contextRunner.withPropertyValues("endpoints.default.jmx.enabled=false", - "endpoints.beans.jmx.enabled=true").run((context) -> { + public void singleJmxEndpointCanBeExposed() { + this.contextRunner.withPropertyValues("management.endpoints.jmx.expose=beans") + .run((context) -> { MBeanServer mBeanServer = context.getBean(MBeanServer.class); checkEndpointMBeans(mBeanServer, new String[] { "beans" }, new String[] { "conditions", "configprops", "env", "health", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JolokiaManagementContextConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JolokiaManagementContextConfigurationIntegrationTests.java index a33d9ff43e6..fd7710dc9e3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JolokiaManagementContextConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JolokiaManagementContextConfigurationIntegrationTests.java @@ -27,6 +27,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; @@ -103,6 +104,7 @@ public class JolokiaManagementContextConfigurationIntegrationTests { @MinimalWebConfiguration @Import({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class }) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java index ac4da1a7c2d..5eb7bbb9a5c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java @@ -19,10 +19,10 @@ package org.springframework.boot.actuate.autoconfigure.integrationtest; import org.junit.Test; import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.actuate.health.HealthReactiveWebEndpointExtension; -import org.springframework.boot.actuate.health.HealthWebEndpointExtension; -import org.springframework.boot.actuate.health.StatusReactiveWebEndpointExtension; -import org.springframework.boot.actuate.health.StatusWebEndpointExtension; +import org.springframework.boot.actuate.health.HealthEndpointWebExtension; +import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; +import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension; +import org.springframework.boot.actuate.health.StatusEndpointWebExtension; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; @@ -59,25 +59,25 @@ public class WebEndpointsAutoConfigurationIntegrationTests { servletWebRunner() .run((context) -> context.getBean(WebEndpointTestApplication.class)); servletWebRunner().run((context) -> assertThat(context) - .hasSingleBean(HealthWebEndpointExtension.class)); + .hasSingleBean(HealthEndpointWebExtension.class)); } @Test public void statusEndpointWebExtensionIsAutoConfigured() { servletWebRunner().run((context) -> assertThat(context) - .hasSingleBean(StatusWebEndpointExtension.class)); + .hasSingleBean(StatusEndpointWebExtension.class)); } @Test public void healthEndpointReactiveWebExtensionIsAutoConfigured() { reactiveWebRunner().run((context) -> assertThat(context) - .hasSingleBean(HealthReactiveWebEndpointExtension.class)); + .hasSingleBean(ReactiveHealthEndpointWebExtension.class)); } @Test public void statusEndpointReactiveWebExtensionIsAutoConfigured() { reactiveWebRunner().run((context) -> assertThat(context) - .hasSingleBean(StatusReactiveWebEndpointExtension.class)); + .hasSingleBean(ReactiveStatusEndpointWebExtension.class)); } private WebApplicationContextRunner servletWebRunner() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointCorsIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointCorsIntegrationTests.java index dd3e6cb33fd..bd4e150a62e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointCorsIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointCorsIntegrationTests.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; @@ -65,10 +66,11 @@ public class WebMvcEndpointCorsIntegrationTests { this.context.register(JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class, - EndpointAutoConfiguration.class, ManagementContextAutoConfiguration.class, + EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, + ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, BeansEndpointAutoConfiguration.class); - TestPropertyValues.of("endpoints.default.web.enabled:true").applyTo(this.context); + TestPropertyValues.of("management.endpoints.web.expose:*").applyTo(this.context); } @Test @@ -83,7 +85,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void settingAllowedOriginsEnablesCors() throws Exception { - TestPropertyValues.of("management.endpoints.cors.allowed-origins:foo.example.com") + TestPropertyValues + .of("management.endpoints.web.cors.allowed-origins:foo.example.com") .applyTo(this.context); createMockMvc() .perform(options("/application/beans").header("Origin", "bar.example.com") @@ -94,7 +97,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void maxAgeDefaultsTo30Minutes() throws Exception { - TestPropertyValues.of("management.endpoints.cors.allowed-origins:foo.example.com") + TestPropertyValues + .of("management.endpoints.web.cors.allowed-origins:foo.example.com") .applyTo(this.context); performAcceptedCorsRequest() .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "1800")); @@ -102,15 +106,18 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void maxAgeCanBeConfigured() throws Exception { - TestPropertyValues.of("management.endpoints.cors.allowed-origins:foo.example.com", - "management.endpoints.cors.max-age: 2400").applyTo(this.context); + TestPropertyValues + .of("management.endpoints.web.cors.allowed-origins:foo.example.com", + "management.endpoints.web.cors.max-age: 2400") + .applyTo(this.context); performAcceptedCorsRequest() .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "2400")); } @Test public void requestsWithDisallowedHeadersAreRejected() throws Exception { - TestPropertyValues.of("management.endpoints.cors.allowed-origins:foo.example.com") + TestPropertyValues + .of("management.endpoints.web.cors.allowed-origins:foo.example.com") .applyTo(this.context); createMockMvc() .perform(options("/application/beans").header("Origin", "foo.example.com") @@ -122,8 +129,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void allowedHeadersCanBeConfigured() throws Exception { TestPropertyValues - .of("management.endpoints.cors.allowed-origins:foo.example.com", - "management.endpoints.cors.allowed-headers:Alpha,Bravo") + .of("management.endpoints.web.cors.allowed-origins:foo.example.com", + "management.endpoints.web.cors.allowed-headers:Alpha,Bravo") .applyTo(this.context); createMockMvc() .perform(options("/application/beans").header("Origin", "foo.example.com") @@ -135,7 +142,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void requestsWithDisallowedMethodsAreRejected() throws Exception { - TestPropertyValues.of("management.endpoints.cors.allowed-origins:foo.example.com") + TestPropertyValues + .of("management.endpoints.web.cors.allowed-origins:foo.example.com") .applyTo(this.context); createMockMvc() .perform(options("/application/health") @@ -147,8 +155,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void allowedMethodsCanBeConfigured() throws Exception { TestPropertyValues - .of("management.endpoints.cors.allowed-origins:foo.example.com", - "management.endpoints.cors.allowed-methods:GET,HEAD") + .of("management.endpoints.web.cors.allowed-origins:foo.example.com", + "management.endpoints.web.cors.allowed-methods:GET,HEAD") .applyTo(this.context); createMockMvc() .perform(options("/application/beans") @@ -161,8 +169,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void credentialsCanBeAllowed() throws Exception { TestPropertyValues - .of("management.endpoints.cors.allowed-origins:foo.example.com", - "management.endpoints.cors.allow-credentials:true") + .of("management.endpoints.web.cors.allowed-origins:foo.example.com", + "management.endpoints.web.cors.allow-credentials:true") .applyTo(this.context); performAcceptedCorsRequest().andExpect( header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")); @@ -171,8 +179,8 @@ public class WebMvcEndpointCorsIntegrationTests { @Test public void credentialsCanBeDisabled() throws Exception { TestPropertyValues - .of("management.endpoints.cors.allowed-origins:foo.example.com", - "management.endpoints.cors.allow-credentials:false") + .of("management.endpoints.web.cors.allowed-origins:foo.example.com", + "management.endpoints.web.cors.allow-credentials:false") .applyTo(this.context); performAcceptedCorsRequest().andExpect( header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java index 077723972f7..8d85b05f4c6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.integrationtest; import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -52,7 +53,7 @@ public class WebMvcEndpointExposureIntegrationTests { JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, - EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, ManagementContextAutoConfiguration.class, @@ -79,9 +80,9 @@ public class WebMvcEndpointExposureIntegrationTests { } @Test - public void webEndpointsCanBeEnabled() { + public void webEndpointsCanBeExposed() { WebApplicationContextRunner contextRunner = this.contextRunner - .withPropertyValues("endpoints.default.web.enabled=true"); + .withPropertyValues("management.endpoints.web.expose=*"); contextRunner.run((context) -> { MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build(); assertThat(isExposed(mvc, HttpMethod.GET, "beans")).isTrue(); @@ -99,26 +100,46 @@ public class WebMvcEndpointExposureIntegrationTests { } @Test - public void singleWebEndpointCanBeEnabled() { - WebApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues( - "endpoints.default.web.enabled=false", - "endpoints.beans.web.enabled=true"); + public void singleWebEndpointCanBeExposed() { + WebApplicationContextRunner contextRunner = this.contextRunner + .withPropertyValues("management.endpoints.web.expose=beans"); contextRunner.run((context) -> { MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build(); - assertThat(isExposed(mvc, HttpMethod.GET, "autoconfig")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "beans")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "conditions")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "configprops")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "env")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse(); - assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "info")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse(); assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse(); - assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "status")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isFalse(); }); } + @Test + public void singleWebEndpointCanBeExcluded() { + WebApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues( + "management.endpoints.web.expose=*", + "management.endpoints.web.exclude=shutdown"); + contextRunner.run((context) -> { + MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build(); + assertThat(isExposed(mvc, HttpMethod.GET, "beans")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "conditions")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "configprops")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "env")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse(); + assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue(); + assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isTrue(); + }); + } + private boolean isExposed(MockMvc mockMvc, HttpMethod method, String path) throws Exception { path = "/application/" + path; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java index 66cbd4d1b0f..e0cbcefb774 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java @@ -22,6 +22,7 @@ import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -91,7 +92,7 @@ public class WebMvcEndpointIntegrationTests { this.context = new AnnotationConfigWebApplicationContext(); this.context.register(SecureConfiguration.class); TestPropertyValues.of("management.endpoints.web.base-path:/management", - "endpoints.default.web.enabled=true").applyTo(this.context); + "management.endpoints.web.expose=*").applyTo(this.context); MockMvc mockMvc = createSecureMockMvc(); mockMvc.perform(get("/management/beans")).andExpect(status().isOk()); } @@ -112,6 +113,7 @@ public class WebMvcEndpointIntegrationTests { @ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, AuditAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class, ManagementContextAutoConfiguration.class, AuditAutoConfiguration.class, diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfigurationTests.java index 5d98f702e04..aa06270d716 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfigurationTests.java @@ -49,7 +49,8 @@ public class LiquibaseEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.liquibase.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.liquibase.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(LiquibaseEndpoint.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfigurationTests.java index 4c05330a08d..1232be5a725 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointManagementContextConfigurationTests.java @@ -63,7 +63,8 @@ public class LogFileWebEndpointManagementContextConfigurationTests { @Test public void logFileWebEndpointIsAutoConfiguredWhenExternalFileIsSet() { this.contextRunner - .withPropertyValues("endpoints.logfile.external-file:external.log") + .withPropertyValues( + "management.endpoint.logfile.external-file:external.log") .run((context) -> assertThat(context) .hasSingleBean(LogFileWebEndpoint.class)); } @@ -72,7 +73,7 @@ public class LogFileWebEndpointManagementContextConfigurationTests { public void logFileWebEndpointCanBeDisabled() { this.contextRunner .withPropertyValues("logging.file:test.log", - "endpoints.logfile.enabled:false") + "management.endpoint.logfile.enabled:false") .run((context) -> assertThat(context) .hasSingleBean(LogFileWebEndpoint.class)); } @@ -81,9 +82,8 @@ public class LogFileWebEndpointManagementContextConfigurationTests { public void logFileWebEndpointUsesConfiguredExternalFile() throws IOException { File file = this.temp.newFile("logfile"); FileCopyUtils.copy("--TEST--".getBytes(), file); - this.contextRunner - .withPropertyValues( - "endpoints.logfile.external-file:" + file.getAbsolutePath()) + this.contextRunner.withPropertyValues( + "management.endpoint.logfile.external-file:" + file.getAbsolutePath()) .run((context) -> { assertThat(context).hasSingleBean(LogFileWebEndpoint.class); LogFileWebEndpoint endpoint = context diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfigurationTests.java index f4c64202862..10ec134c96c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfigurationTests.java @@ -49,8 +49,9 @@ public class LoggersEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.loggers.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(LoggersEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.loggers.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(LoggersEndpoint.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/HeapDumpWebEndpointManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/HeapDumpWebEndpointManagementContextConfigurationTests.java index 0ff4b033280..d0414e9f0e9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/HeapDumpWebEndpointManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/HeapDumpWebEndpointManagementContextConfigurationTests.java @@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class HeapDumpWebEndpointManagementContextConfigurationTests { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withPropertyValues("endpoints.default.web.enabled:true") + .withPropertyValues("management.endpoints.web.expose:*") .withUserConfiguration( HeapDumpWebEndpointManagementContextConfiguration.class); @@ -43,7 +43,8 @@ public class HeapDumpWebEndpointManagementContextConfigurationTests { @Test public void runWhenDisabledShouldNotCreateIndicator() throws Exception { - this.contextRunner.withPropertyValues("endpoints.heapdump.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.heapdump.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(HeapDumpWebEndpoint.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/ThreadDumpEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/ThreadDumpEndpointAutoConfigurationTests.java index 31e1d84dae9..369ed280296 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/ThreadDumpEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/management/ThreadDumpEndpointAutoConfigurationTests.java @@ -44,7 +44,8 @@ public class ThreadDumpEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.threaddump.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.threaddump.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(ThreadDumpEndpoint.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java index 1183afce938..af55ebebfab 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfigurationTests.java @@ -46,7 +46,8 @@ public class ScheduledTasksEndpointAutoConfigurationTests { @Test public void endpointCanBeDisabled() { - this.contextRunner.withPropertyValues("endpoints.scheduledtasks.enabled:false") + this.contextRunner + .withPropertyValues("management.endpoint.scheduledtasks.enabled:false") .run((context) -> assertThat(context) .doesNotHaveBean(ScheduledTasksEndpoint.class)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfigurationTests.java index 73baf06014d..8939c2081e8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfigurationTests.java @@ -49,8 +49,10 @@ public class SessionsEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.sessions.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(SessionsEndpoint.class)); + this.contextRunner + .withPropertyValues("management.endpoint.sessions.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(SessionsEndpoint.class)); } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/trace/TraceEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/trace/TraceEndpointAutoConfigurationTests.java index 92016058f99..e9596784213 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/trace/TraceEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/trace/TraceEndpointAutoConfigurationTests.java @@ -44,8 +44,9 @@ public class TraceEndpointAutoConfigurationTests { @Test public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() throws Exception { - this.contextRunner.withPropertyValues("endpoints.trace.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(TraceEndpoint.class)); + this.contextRunner.withPropertyValues("management.endpoint.trace.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(TraceEndpoint.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointTests.java index 8b6b364812c..dbf34d71b79 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointTests.java @@ -22,13 +22,12 @@ import java.util.Map; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationType; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -136,12 +135,12 @@ public class RequestMappingEndpointTests { OperationRequestPredicate requestPredicate = new OperationRequestPredicate("test", WebEndpointHttpMethod.GET, Collections.singletonList("application/json"), Collections.singletonList("application/json")); - WebEndpointOperation operation = new WebEndpointOperation(OperationType.READ, + WebOperation operation = new WebOperation(OperationType.READ, (arguments) -> "Invoked", true, requestPredicate, "test"); WebMvcEndpointHandlerMapping mapping = new WebMvcEndpointHandlerMapping( new EndpointMapping("application"), - Collections.singleton(new EndpointInfo<>("test", - DefaultEnablement.ENABLED, Collections.singleton(operation))), + Collections.singleton(new EndpointInfo<>("test", true, + Collections.singleton(operation))), new EndpointMediaTypes(Arrays.asList("application/vnd.test+json"), Arrays.asList("application/vnd.test+json"))); mapping.setApplicationContext(new StaticApplicationContext()); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebExtension.java similarity index 85% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebExtension.java index 3cde1d36df8..f20e9c80d0a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebExtension.java @@ -21,21 +21,21 @@ import java.util.Date; import org.springframework.boot.actuate.audit.AuditEventsEndpoint.AuditEventsDescriptor; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; import org.springframework.lang.Nullable; /** - * {@link WebEndpointExtension} for the {@link AuditEventsEndpoint}. + * {@link EndpointWebExtension} for the {@link AuditEventsEndpoint}. * * @author Vedran Pavic * @since 2.0.0 */ -@WebEndpointExtension(endpoint = AuditEventsEndpoint.class) -public class AuditEventsWebEndpointExtension { +@EndpointWebExtension(endpoint = AuditEventsEndpoint.class) +public class AuditEventsEndpointWebExtension { private final AuditEventsEndpoint delegate; - public AuditEventsWebEndpointExtension(AuditEventsEndpoint delegate) { + public AuditEventsEndpointWebExtension(AuditEventsEndpoint delegate) { this.delegate = delegate; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsJmxEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsJmxEndpointExtension.java index d6db27d368c..ee529b94344 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsJmxEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEventsJmxEndpointExtension.java @@ -20,7 +20,7 @@ import java.util.Date; import org.springframework.boot.actuate.audit.AuditEventsEndpoint.AuditEventsDescriptor; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension; +import org.springframework.boot.actuate.endpoint.jmx.annotation.EndpointJmxExtension; /** * JMX-specific extension of the {@link AuditEventsEndpoint}. @@ -29,7 +29,7 @@ import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExten * @author Andy Wilkinson * @since 2.0.0 */ -@JmxEndpointExtension(endpoint = AuditEventsEndpoint.class) +@EndpointJmxExtension(endpoint = AuditEventsEndpoint.class) public class AuditEventsJmxEndpointExtension { private final AuditEventsEndpoint delegate; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/ShutdownEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/ShutdownEndpoint.java index c5ab87fe00a..d89ee7b5efa 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/ShutdownEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/ShutdownEndpoint.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.Map; import org.springframework.beans.BeansException; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.context.ApplicationContext; @@ -35,7 +34,7 @@ import org.springframework.context.ConfigurableApplicationContext; * @author Andy Wilkinson * @since 2.0.0 */ -@Endpoint(id = "shutdown", defaultEnablement = DefaultEnablement.DISABLED) +@Endpoint(id = "shutdown", enableByDefault = false) public class ShutdownEndpoint implements ApplicationContextAware { private static final Map NO_CONTEXT_MESSAGE = Collections diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointExposure.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointExposure.java deleted file mode 100644 index 70beac47400..00000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointExposure.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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; - -/** - * An enumeration of the available exposure technologies for an endpoint. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public enum EndpointExposure { - - /** - * Expose the endpoint as a JMX MBean. - */ - JMX(true), - - /** - * Expose the endpoint as a Web endpoint. - */ - WEB(false); - - private final boolean enabledByDefault; - - EndpointExposure(boolean enabledByDefault) { - this.enabledByDefault = enabledByDefault; - } - - public boolean isEnabledByDefault() { - return this.enabledByDefault; - } - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DefaultEnablement.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointFilter.java similarity index 60% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DefaultEnablement.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointFilter.java index d8ca91b6968..a7fb1d8f192 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DefaultEnablement.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointFilter.java @@ -17,26 +17,21 @@ package org.springframework.boot.actuate.endpoint; /** - * Enumerate the enablement options for an endpoint. + * Strategy class that can be used to filter discovered endpoints. * - * @author Stephane Nicoll + * @author Phillip Webb + * @param the type of the endpoint's operations * @since 2.0.0 */ -public enum DefaultEnablement { +@FunctionalInterface +public interface EndpointFilter { /** - * The endpoint is enabled unless explicitly disabled. + * Return {@code true} if the filter matches. + * @param info the endpoint info + * @param discoverer the endpoint discoverer + * @return {@code true} if the filter matches */ - ENABLED, - - /** - * The endpoint is disabled unless explicitly enabled. - */ - DISABLED, - - /** - * The endpoint's enablement defaults to the "default" settings. - */ - NEUTRAL + boolean match(EndpointInfo info, EndpointDiscoverer discoverer); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointInfo.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointInfo.java index caf34ca71a8..90336068bf6 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointInfo.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointInfo.java @@ -17,6 +17,9 @@ package org.springframework.boot.actuate.endpoint; import java.util.Collection; +import java.util.Collections; + +import org.springframework.util.Assert; /** * Information describing an endpoint. @@ -29,7 +32,7 @@ public class EndpointInfo { private final String id; - private final DefaultEnablement defaultEnablement; + private final boolean enableByDefault; private final Collection operations; @@ -37,14 +40,15 @@ public class EndpointInfo { * Creates a new {@code EndpointInfo} describing an endpoint with the given {@code id} * and {@code operations}. * @param id the id of the endpoint - * @param defaultEnablement the {@link DefaultEnablement} of the endpoint + * @param enableByDefault if the endpoint is enabled by default * @param operations the operations of the endpoint */ - public EndpointInfo(String id, DefaultEnablement defaultEnablement, - Collection operations) { + public EndpointInfo(String id, boolean enableByDefault, Collection operations) { + Assert.hasText(id, "ID must not be empty"); + Assert.notNull(operations, "Operations must not be null"); this.id = id; - this.defaultEnablement = defaultEnablement; - this.operations = operations; + this.enableByDefault = enableByDefault; + this.operations = Collections.unmodifiableCollection(operations); } /** @@ -56,11 +60,11 @@ public class EndpointInfo { } /** - * Return the {@link DefaultEnablement} of the endpoint. - * @return the default enablement + * Returns if the endpoint is enabled by default. + * @return if the endpoint is enabled by default */ - public DefaultEnablement getDefaultEnablement() { - return this.defaultEnablement; + public boolean isEnableByDefault() { + return this.enableByDefault; } /** diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java index 15612150689..aac6a05b41c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java @@ -33,14 +33,14 @@ public class Operation { /** * Creates a new {@code EndpointOperation} for an operation of the given {@code type}. * The operation can be performed using the given {@code operationInvoker}. - * @param type the type of the operation - * @param operationInvoker used to perform the operation + * @param operationType the type of the operation + * @param invoker used to perform the operation * @param blocking whether or not this is a blocking operation */ - public Operation(OperationType type, OperationInvoker operationInvoker, + public Operation(OperationType operationType, OperationInvoker invoker, boolean blocking) { - this.type = type; - this.invoker = operationInvoker; + this.type = operationType; + this.invoker = invoker; this.blocking = blocking; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java index cfc90d05023..c51666e6cf7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java @@ -16,139 +16,178 @@ package org.springframework.boot.actuate.endpoint.annotation; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.function.Consumer; +import java.util.Map.Entry; import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; -import org.springframework.boot.actuate.endpoint.EndpointExposure; +import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.Operation; -import org.springframework.boot.actuate.endpoint.OperationType; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; -import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.context.ApplicationContext; -import org.springframework.core.MethodIntrospector; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.ObjectUtils; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; /** - * A base {@link EndpointDiscoverer} implementation that discovers {@link Endpoint} beans + * A base {@link EndpointDiscoverer} implementation that discovers + * {@link Endpoint @Endpoint} beans and {@link EndpointExtension @EndpointExtension} beans * in an application context. * - * @param the type of the operation * @param the type of the operation key + * @param the type of the operation * @author Andy Wilkinson * @author Stephane Nicoll + * @author Phillip Webb * @since 2.0.0 */ -public abstract class AnnotationEndpointDiscoverer +public abstract class AnnotationEndpointDiscoverer implements EndpointDiscoverer { private final ApplicationContext applicationContext; - private final EndpointOperationFactory operationFactory; - private final Function operationKeyFactory; - private final CachingConfigurationFactory cachingConfigurationFactory; + private final OperationsFactory operationsFactory; + private final List> filters; + + /** + * Create a new {@link AnnotationEndpointDiscoverer} instance. + * @param applicationContext the application context + * @param operationFactory a factory used to create operations + * @param operationKeyFactory a factory used to create a key for an operation + * @param parameterMapper the {@link ParameterMapper} used to convert arguments when + * an operation is invoked + * @param invokerAdvisors advisors used to add additional invoker advise + * @param filters filters that must match for an endpoint to be exposed + */ protected AnnotationEndpointDiscoverer(ApplicationContext applicationContext, - EndpointOperationFactory operationFactory, - Function operationKeyFactory, - CachingConfigurationFactory cachingConfigurationFactory) { + OperationFactory operationFactory, Function operationKeyFactory, + ParameterMapper parameterMapper, + Collection invokerAdvisors, + Collection> filters) { + Assert.notNull(applicationContext, "Application Context must not be null"); + Assert.notNull(operationFactory, "Operation Factory must not be null"); + Assert.notNull(operationKeyFactory, "Operation Key Factory must not be null"); + Assert.notNull(parameterMapper, "Parameter Mapper must not be null"); this.applicationContext = applicationContext; - this.operationFactory = operationFactory; this.operationKeyFactory = operationKeyFactory; - this.cachingConfigurationFactory = cachingConfigurationFactory; + this.operationsFactory = new OperationsFactory<>(operationFactory, + parameterMapper, invokerAdvisors); + this.filters = (filters == null ? Collections.emptyList() + : new ArrayList<>(filters)); + } + + @Override + public final Collection> discoverEndpoints() { + Class operationType = getOperationType(); + Map, DiscoveredEndpoint> endpoints = getEndpoints(operationType); + Map, DiscoveredExtension> extensions = getExtensions(operationType, + endpoints); + Collection exposed = mergeExposed(endpoints, extensions); + verify(exposed); + return exposed.stream().map(DiscoveredEndpoint::getInfo) + .collect(Collectors.toCollection(ArrayList::new)); } /** - * Perform endpoint discovery, including discovery and merging of extensions. - * @param extensionType the annotation type of the extension - * @param exposure the {@link EndpointExposure} that should be considered - * @return the list of {@link EndpointInfo EndpointInfos} that describes the - * discovered endpoints matching the specified {@link EndpointExposure} + * Return the operation type being discovered. By default this method will resolve the + * class generic "{@code }". + * @return the operation type */ - protected Collection> discoverEndpoints( - Class extensionType, EndpointExposure exposure) { - Map, EndpointInfo> endpoints = discoverEndpoints(exposure); - Map, EndpointExtensionInfo> extensions = discoverExtensions(endpoints, - extensionType, exposure); - Collection> result = new ArrayList<>(); - endpoints.forEach((endpointClass, endpointInfo) -> { - EndpointExtensionInfo extension = extensions.remove(endpointClass); - result.add(createDescriptor(endpointClass, endpointInfo, extension)); - }); - return result; + @SuppressWarnings("unchecked") + protected Class getOperationType() { + return (Class) ResolvableType + .forClass(AnnotationEndpointDiscoverer.class, getClass()) + .resolveGeneric(1); } - private Map, EndpointInfo> discoverEndpoints(EndpointExposure exposure) { + private Map, DiscoveredEndpoint> getEndpoints(Class operationType) { + Map, DiscoveredEndpoint> endpoints = new LinkedHashMap<>(); + Map endpointsById = new LinkedHashMap<>(); String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors( this.applicationContext, Endpoint.class); - Map, EndpointInfo> endpoints = new LinkedHashMap<>(); - Map> endpointsById = new LinkedHashMap<>(); for (String beanName : beanNames) { - Class beanType = this.applicationContext.getType(beanName); - AnnotationAttributes attributes = AnnotatedElementUtils - .findMergedAnnotationAttributes(beanType, Endpoint.class, true, true); - if (isExposedOver(attributes, exposure)) { - EndpointInfo info = createEndpointInfo(beanName, beanType, attributes); - EndpointInfo previous = endpointsById.putIfAbsent(info.getId(), info); - Assert.state(previous == null, () -> "Found two endpoints with the id '" - + info.getId() + "': " + info + " and " + previous); - endpoints.put(beanType, info); - } + addEndpoint(endpoints, endpointsById, beanName); } return endpoints; } - private EndpointInfo createEndpointInfo(String beanName, Class beanType, - AnnotationAttributes attributes) { - String id = attributes.getString("id"); - DefaultEnablement defaultEnablement = (DefaultEnablement) attributes - .get("defaultEnablement"); - Map operations = discoverOperations(id, beanName, beanType); - return new EndpointInfo<>(id, defaultEnablement, operations.values()); + private void addEndpoint(Map, DiscoveredEndpoint> endpoints, + Map endpointsById, String beanName) { + Class endpointType = this.applicationContext.getType(beanName); + Object target = this.applicationContext.getBean(beanName); + DiscoveredEndpoint endpoint = createEndpoint(target, endpointType); + String id = endpoint.getInfo().getId(); + DiscoveredEndpoint previous = endpointsById.putIfAbsent(id, endpoint); + Assert.state(previous == null, () -> "Found two endpoints with the id '" + id + + "': " + endpoint + " and " + previous); + endpoints.put(endpointType, endpoint); } - private Map, EndpointExtensionInfo> discoverExtensions( - Map, EndpointInfo> endpoints, - Class extensionType, EndpointExposure exposure) { - if (extensionType == null) { - return Collections.emptyMap(); - } + private DiscoveredEndpoint createEndpoint(Object target, Class endpointType) { + AnnotationAttributes annotationAttributes = AnnotatedElementUtils + .findMergedAnnotationAttributes(endpointType, Endpoint.class, true, true); + String id = annotationAttributes.getString("id"); + Assert.state(StringUtils.hasText(id), + "No @Endpoint id attribute specified for " + endpointType.getName()); + boolean enabledByDefault = (Boolean) annotationAttributes.get("enableByDefault"); + Collection operations = this.operationsFactory + .createOperations(id, target, endpointType).values(); + EndpointInfo endpointInfo = new EndpointInfo<>(id, enabledByDefault, + operations); + boolean exposed = isEndpointExposed(endpointType, endpointInfo); + return new DiscoveredEndpoint(endpointType, endpointInfo, exposed); + } + + private Map, DiscoveredExtension> getExtensions(Class operationType, + Map, DiscoveredEndpoint> endpoints) { + Map, DiscoveredExtension> extensions = new LinkedHashMap<>(); String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors( - this.applicationContext, extensionType); - Map, EndpointExtensionInfo> extensions = new HashMap<>(); + this.applicationContext, EndpointExtension.class); for (String beanName : beanNames) { - Class beanType = this.applicationContext.getType(beanName); - Class endpointType = getEndpointType(extensionType, beanType); - AnnotationAttributes endpointAttributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(endpointType, Endpoint.class); - Assert.state(isExposedOver(endpointAttributes, exposure), - () -> "Invalid extension " + beanType.getName() + "': endpoint '" + addExtension(endpoints, extensions, beanName); + } + return extensions; + } + + private void addExtension(Map, DiscoveredEndpoint> endpoints, + Map, DiscoveredExtension> extensions, String beanName) { + Class extensionType = this.applicationContext.getType(beanName); + Class endpointType = getEndpointType(extensionType); + DiscoveredEndpoint endpoint = getExtendingEndpoint(endpoints, extensionType, + endpointType); + if (isExtensionExposed(extensionType, endpoint.getInfo())) { + Assert.state(endpoint.isExposed() || isEndpointFiltered(endpoint.getInfo()), + () -> "Invalid extension " + extensionType.getName() + "': endpoint '" + endpointType.getName() + "' does not support such extension"); - EndpointInfo info = getEndpointInfo(endpoints, beanType, endpointType); - Map operations = discoverOperations(info.getId(), beanName, - beanType); - EndpointExtensionInfo extension = new EndpointExtensionInfo<>(beanType, + Object target = this.applicationContext.getBean(beanName); + Map operations = this.operationsFactory + .createOperations(endpoint.getInfo().getId(), target, extensionType); + DiscoveredExtension extension = new DiscoveredExtension(extensionType, operations.values()); - EndpointExtensionInfo previous = extensions.putIfAbsent(endpointType, + DiscoveredExtension previous = extensions.putIfAbsent(endpointType, extension); Assert.state(previous == null, () -> "Found two extensions for the same endpoint '" @@ -156,237 +195,285 @@ public abstract class AnnotationEndpointDiscoverer + extension.getExtensionType().getName() + " and " + previous.getExtensionType().getName()); } - return extensions; - } - private EndpointInfo getEndpointInfo(Map, EndpointInfo> endpoints, - Class beanType, Class endpointClass) { - EndpointInfo endpoint = endpoints.get(endpointClass); + private Class getEndpointType(Class extensionType) { + AnnotationAttributes attributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(extensionType, EndpointExtension.class); + Class endpointType = attributes.getClass("endpoint"); + Assert.state(!endpointType.equals(Void.class), () -> "Extension " + + endpointType.getName() + " does not specify an endpoint"); + return endpointType; + } + + private DiscoveredEndpoint getExtendingEndpoint( + Map, DiscoveredEndpoint> endpoints, Class extensionType, + Class endpointType) { + DiscoveredEndpoint endpoint = endpoints.get(endpointType); Assert.state(endpoint != null, - () -> "Invalid extension '" + beanType.getName() - + "': no endpoint found with type '" + endpointClass.getName() + () -> "Invalid extension '" + extensionType.getName() + + "': no endpoint found with type '" + endpointType.getName() + "'"); return endpoint; } - private Class getEndpointType(Class extensionType, - Class beanType) { - AnnotationAttributes attributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(beanType, extensionType); - return (Class) attributes.get("endpoint"); - } - - private EndpointInfoDescriptor createDescriptor(Class type, - EndpointInfo info, EndpointExtensionInfo extension) { - Map, List> operations = indexOperations(info.getId(), type, - info.getOperations()); - if (extension != null) { - operations.putAll(indexOperations(info.getId(), extension.getExtensionType(), - extension.getOperations())); - return new EndpointInfoDescriptor<>(mergeEndpoint(info, extension), - operations); + private boolean isEndpointExposed(Class endpointType, + EndpointInfo endpointInfo) { + if (isEndpointFiltered(endpointInfo)) { + return false; } - return new EndpointInfoDescriptor<>(info, operations); + AnnotationAttributes annotationAttributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(endpointType, FilteredEndpoint.class); + if (annotationAttributes == null) { + return true; + } + Class filterClass = annotationAttributes.getClass("value"); + return isFilterMatch(filterClass, endpointInfo); } - private EndpointInfo mergeEndpoint(EndpointInfo endpoint, - EndpointExtensionInfo extension) { - Map operations = new HashMap<>(); - Consumer consumer = (operation) -> operations - .put(this.operationKeyFactory.apply(operation), operation); - endpoint.getOperations().forEach(consumer); - extension.getOperations().forEach(consumer); - return new EndpointInfo<>(endpoint.getId(), endpoint.getDefaultEnablement(), - operations.values()); + private boolean isEndpointFiltered(EndpointInfo endpointInfo) { + for (EndpointFilter filter : this.filters) { + if (!isFilterMatch(filter, endpointInfo)) { + return true; + } + } + return false; } - private Map, List> indexOperations(String endpointId, - Class target, Collection operations) { - LinkedMultiValueMap, T> result = new LinkedMultiValueMap<>(); - operations.forEach((operation) -> { - K key = this.operationKeyFactory.apply(operation); - result.add(new OperationKey<>(endpointId, target, key), operation); + private boolean isExtensionExposed(Class extensionType, + EndpointInfo endpointInfo) { + AnnotationAttributes annotationAttributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(extensionType, EndpointExtension.class); + Class filterClass = annotationAttributes.getClass("filter"); + return isFilterMatch(filterClass, endpointInfo); + } + + @SuppressWarnings("unchecked") + private boolean isFilterMatch(Class filterClass, EndpointInfo endpointInfo) { + Class generic = ResolvableType.forClass(EndpointFilter.class, filterClass) + .resolveGeneric(0); + if (generic == null || generic.isAssignableFrom(getOperationType())) { + EndpointFilter filter = (EndpointFilter) BeanUtils + .instantiateClass(filterClass); + return isFilterMatch(filter, endpointInfo); + } + return false; + } + + private boolean isFilterMatch(EndpointFilter filter, + EndpointInfo endpointInfo) { + try { + return filter.match(endpointInfo, this); + } + catch (ClassCastException ex) { + String msg = ex.getMessage(); + if (msg == null || msg.startsWith(endpointInfo.getClass().getName())) { + // Possibly a lambda-defined listener which we could not resolve the + // generic event type for + Log logger = LogFactory.getLog(getClass()); + if (logger.isDebugEnabled()) { + logger.debug("Non-matching info type for lister: " + filter, ex); + } + return false; + } + else { + throw ex; + } + } + + } + + private Collection mergeExposed( + Map, DiscoveredEndpoint> endpoints, + Map, DiscoveredExtension> extensions) { + List result = new ArrayList<>(); + endpoints.forEach((endpointClass, endpoint) -> { + if (endpoint.isExposed()) { + DiscoveredExtension extension = extensions.remove(endpointClass); + result.add(endpoint.merge(extension)); + } }); return result; } - private boolean isExposedOver(AnnotationAttributes attributes, - EndpointExposure exposure) { - if (exposure == null) { - return true; - } - EndpointExposure[] supported = (EndpointExposure[]) attributes.get("exposure"); - return ObjectUtils.isEmpty(supported) - || ObjectUtils.containsElement(supported, exposure); - } - - private Map discoverOperations(String id, String name, Class type) { - return MethodIntrospector.selectMethods(type, - (MethodIntrospector.MetadataLookup) ( - method) -> createOperationIfPossible(id, name, method)); - } - - private T createOperationIfPossible(String endpointId, String beanName, - Method method) { - T operation = createReadOperationIfPossible(endpointId, beanName, method); - if (operation != null) { - return operation; - } - operation = createWriteOperationIfPossible(endpointId, beanName, method); - if (operation != null) { - return operation; - } - return createDeleteOperationIfPossible(endpointId, beanName, method); - } - - private T createReadOperationIfPossible(String endpointId, String beanName, - Method method) { - return createOperationIfPossible(endpointId, beanName, method, - ReadOperation.class, OperationType.READ); - } - - private T createWriteOperationIfPossible(String endpointId, String beanName, - Method method) { - return createOperationIfPossible(endpointId, beanName, method, - WriteOperation.class, OperationType.WRITE); - } - - private T createDeleteOperationIfPossible(String endpointId, String beanName, - Method method) { - return createOperationIfPossible(endpointId, beanName, method, - DeleteOperation.class, OperationType.DELETE); - } - - private T createOperationIfPossible(String endpointId, String beanName, Method method, - Class operationAnnotation, - OperationType operationType) { - AnnotationAttributes operationAttributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(method, operationAnnotation); - if (operationAttributes == null) { - return null; - } - CachingConfiguration cachingConfiguration = this.cachingConfigurationFactory - .getCachingConfiguration(endpointId); - return this.operationFactory.createOperation(endpointId, operationAttributes, - this.applicationContext.getBean(beanName), method, operationType, - determineTimeToLive(cachingConfiguration, operationType, method)); - } - - private long determineTimeToLive(CachingConfiguration cachingConfiguration, - OperationType operationType, Method method) { - if (cachingConfiguration != null && cachingConfiguration.getTimeToLive() > 0 - && operationType == OperationType.READ - && method.getParameters().length == 0) { - return cachingConfiguration.getTimeToLive(); - } - return 0; + /** + * Allows subclasses to verify that the descriptors are correctly configured. + * @param exposedEndpoints the discovered endpoints to verify before exposing + */ + protected void verify(Collection exposedEndpoints) { } /** - * An {@code EndpointOperationFactory} creates an {@link Operation} for an operation - * on an endpoint. - * - * @param the {@link Operation} type + * A discovered endpoint (which may not be valid and might not ultimately be exposed). */ - @FunctionalInterface - protected interface EndpointOperationFactory { + protected final class DiscoveredEndpoint { + + private final EndpointInfo info; + + private final boolean exposed; + + private final Map> operations; + + private DiscoveredEndpoint(Class type, EndpointInfo info, boolean exposed) { + Assert.notNull(info, "Info must not be null"); + this.info = info; + this.exposed = exposed; + this.operations = indexEndpointOperations(type, info); + } + + private Map> indexEndpointOperations(Class endpointType, + EndpointInfo info) { + return Collections.unmodifiableMap( + indexOperations(info.getId(), endpointType, info.getOperations())); + } + + private DiscoveredEndpoint(EndpointInfo info, boolean exposed, + Map> operations) { + Assert.notNull(info, "Info must not be null"); + this.info = info; + this.exposed = exposed; + this.operations = operations; + } /** - * Creates an {@code EndpointOperation} for an operation on an endpoint. - * @param endpointId the id of the endpoint - * @param operationAttributes the annotation attributes for the operation - * @param target the target that implements the operation - * @param operationMethod the method on the bean that implements the operation - * @param operationType the type of the operation - * @param timeToLive the caching period in milliseconds - * @return the operation info that describes the operation + * Return the {@link EndpointInfo} for the discovered endpoint. + * @return the endpoint info */ - T createOperation(String endpointId, AnnotationAttributes operationAttributes, - Object target, Method operationMethod, OperationType operationType, - long timeToLive); + public EndpointInfo getInfo() { + return this.info; + } + + /** + * Return {@code true} if the endpoint is exposed. + * @return if the is exposed + */ + private boolean isExposed() { + return this.exposed; + } + + /** + * Return all operation that were discovered. These might be different to the ones + * that are in {@link #getInfo()}. + * @return the endpoint operations + */ + public Map> getOperations() { + return this.operations; + } + + /** + * Find any duplicate operations. + * @return any duplicate operations + */ + public Map> findDuplicateOperations() { + return this.operations.entrySet().stream() + .filter((entry) -> entry.getValue().size() > 1) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (u, v) -> v, + LinkedHashMap::new)); + } + + private DiscoveredEndpoint merge(DiscoveredExtension extension) { + if (extension == null) { + return this; + } + Map> operations = mergeOperations(extension); + EndpointInfo info = new EndpointInfo<>(this.info.getId(), + this.info.isEnableByDefault(), flatten(operations).values()); + return new DiscoveredEndpoint(info, this.exposed, operations); + } + + private Map> mergeOperations( + DiscoveredExtension extension) { + MultiValueMap operations = new LinkedMultiValueMap<>( + this.operations); + operations.addAll(indexOperations(getInfo().getId(), + extension.getExtensionType(), extension.getOperations())); + return Collections.unmodifiableMap(operations); + } + + private Map flatten(Map> operations) { + Map flattened = new LinkedHashMap<>(); + operations.forEach((operationKey, value) -> flattened + .put(operationKey.getKey(), getLastValue(value))); + return Collections.unmodifiableMap(flattened); + } + + private T getLastValue(List value) { + return value.get(value.size() - 1); + } + + private MultiValueMap indexOperations(String endpointId, + Class target, Collection operations) { + LinkedMultiValueMap result = new LinkedMultiValueMap<>(); + operations.forEach((operation) -> { + K key = getOperationKey(operation); + result.add(new OperationKey(endpointId, target, key), operation); + }); + return result; + } + + private K getOperationKey(T operation) { + return AnnotationEndpointDiscoverer.this.operationKeyFactory.apply(operation); + } + + @Override + public String toString() { + return getInfo().toString(); + } } /** - * Describes a tech-specific extension of an endpoint. - * @param the type of the operation + * A discovered extension. */ - private static final class EndpointExtensionInfo { + protected final class DiscoveredExtension { private final Class extensionType; private final Collection operations; - private EndpointExtensionInfo(Class extensionType, Collection operations) { + private DiscoveredExtension(Class extensionType, Collection operations) { this.extensionType = extensionType; this.operations = operations; } - private Class getExtensionType() { + public Class getExtensionType() { return this.extensionType; } - private Collection getOperations() { + public Collection getOperations() { return this.operations; } - } - - /** - * Describes an {@link EndpointInfo endpoint} and whether or not it is valid. - * - * @param the type of the operation - * @param the type of the operation key - */ - protected static class EndpointInfoDescriptor { - - private final EndpointInfo endpointInfo; - - private final Map, List> operations; - - protected EndpointInfoDescriptor(EndpointInfo endpointInfo, - Map, List> operations) { - this.endpointInfo = endpointInfo; - this.operations = operations; - } - - public EndpointInfo getEndpointInfo() { - return this.endpointInfo; - } - - public Map, List> findDuplicateOperations() { - Map, List> duplicateOperations = new HashMap<>(); - this.operations.forEach((k, list) -> { - if (list.size() > 1) { - duplicateOperations.put(k, list); - } - }); - return duplicateOperations; + @Override + public String toString() { + return this.extensionType.getName(); } } /** * Define the key of an operation in the context of an operation's implementation. - * - * @param the type of the key */ - protected static final class OperationKey { + protected final class OperationKey { private final String endpointId; - private final Class endpointType; + private final Class target; private final K key; - public OperationKey(String endpointId, Class endpointType, K key) { + public OperationKey(String endpointId, Class target, K key) { this.endpointId = endpointId; - this.endpointType = endpointType; + this.target = target; this.key = key; } + public K getKey() { + return this.key; + } + @Override + @SuppressWarnings("unchecked") public boolean equals(Object o) { if (o == this) { return true; @@ -394,10 +481,10 @@ public abstract class AnnotationEndpointDiscoverer if (o == null || getClass() != o.getClass()) { return false; } - OperationKey other = (OperationKey) o; + OperationKey other = (OperationKey) o; Boolean result = true; result = result && this.endpointId.equals(other.endpointId); - result = result && this.endpointType.equals(other.endpointType); + result = result && this.target.equals(other.target); result = result && this.key.equals(other.key); return result; } @@ -405,11 +492,17 @@ public abstract class AnnotationEndpointDiscoverer @Override public int hashCode() { int result = this.endpointId.hashCode(); - result = 31 * result + this.endpointType.hashCode(); + result = 31 * result + this.target.hashCode(); result = 31 * result + this.key.hashCode(); return result; } + @Override + public String toString() { + return new ToStringCreator(this).append("endpointId", this.endpointId) + .append("target", this.target).append("key", this.key).toString(); + } + } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DeleteOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DeleteOperation.java index 9c60dad786b..6faca1d2a38 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DeleteOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DeleteOperation.java @@ -36,7 +36,6 @@ public @interface DeleteOperation { /** * The media type of the result of the operation. - * * @return the media type */ String[] produces() default {}; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/Endpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/Endpoint.java index 2b36b0a4114..66fcdffcc10 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/Endpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/Endpoint.java @@ -22,14 +22,28 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; -import org.springframework.boot.actuate.endpoint.EndpointExposure; - /** - * Identifies a type as being an endpoint. + * Identifies a type as being an actuator endpoint that provides information about the + * running application. Endpoints can be exposed over a variety of technologies including + * JMX and HTTP. + *

+ * Most {@code @Endpoint} classes will declare one or more + * {@link ReadOperation @ReadOperation}, {@link WriteOperation @WriteOperation}, + * {@link DeleteOperation @DeleteOperation} annotated methods which will be automatically + * adapted to the exposing technology (JMX, Spring MVC, Spring WebFlux, Jersey etc.). + *

+ * {@code @Endpoint} represents the lowest common denominator for endpoints and + * intentionally limits the sorts of operation methods that may be defined in order to + * support the broadest possible range of exposure technologies. If you need deeper + * support for a specific technology you can either write an endpoint that is + * {@link FilteredEndpoint filtered} to a certain technology, or provide + * {@link EndpointExtension extension} for the broader endpoint. * * @author Andy Wilkinson + * @author Phillip Webb * @since 2.0.0 + * @see EndpointExtension + * @see FilteredEndpoint * @see AnnotationEndpointDiscoverer */ @Target(ElementType.TYPE) @@ -41,20 +55,12 @@ public @interface Endpoint { * The id of the endpoint. * @return the id */ - String id(); + String id() default ""; /** - * Defines the {@link EndpointExposure technologies} over which the endpoint should be - * exposed. By default, all technologies are supported. - * @return the supported endpoint exposure technologies + * If the endpoint should be enabled or disabled by default. + * @return {@code true} if the endpoint is enabled by default */ - EndpointExposure[] exposure() default {}; - - /** - * Defines the {@link DefaultEnablement} of the endpoint. By default, the endpoint's - * enablement defaults to the "default" settings. - * @return the default enablement - */ - DefaultEnablement defaultEnablement() default DefaultEnablement.NEUTRAL; + boolean enableByDefault() default true; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointExtension.java new file mode 100644 index 00000000000..c78d9506e07 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointExtension.java @@ -0,0 +1,67 @@ +/* + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.core.annotation.AliasFor; + +/** + * Meta-annotation used to indicate that an annotation provides extension support for an + * endpoint. Extensions allow additional technology specific {@link Operation operations} + * to be added to an existing endpoint. For example, a web extension may offer variations + * of a read operation operation to support filtering based on a query parameter. + *

+ * Extension annotations must provide an {@link EndpointFilter} to restrict when the + * extension applies. The {@code endpoint} attribute is usually re-declared using + * {@link AliasFor @AliasFor}. For example:

+ * @EndpointExtension(filter = WebEndpointFilter.class)
+ * public @interface EndpointWebExtension {
+ *
+ *   @AliasFor(annotation = EndpointExtension.class, attribute = "endpoint")
+ *   Class endpoint();
+ *
+ * }
+ * 
+ * + * @author Phillip Webb + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface EndpointExtension { + + /** + * The filter class used to determine when the extension applies. + * @return the filter class + */ + Class> filter(); + + /** + * The class of the endpoint to extend. + * @return the class endpoint to extend + */ + Class endpoint() default Void.class; + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/FilteredEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/FilteredEndpoint.java new file mode 100644 index 00000000000..4f816148bec --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/FilteredEndpoint.java @@ -0,0 +1,56 @@ +/* + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.actuate.endpoint.EndpointFilter; + +/** + * Annotation that can be used on an {@link Endpoint @Endpoint} to implement implicit + * filtering. Often used as a meta-annotation on technology specific endpoint annotations, + * for example:
+ * @Endpoint
+ * @FilteredEndpoint(WebEndpointFilter.class)
+ * public @interface WebEndpoint {
+ *
+ *     @AliasFor(annotation = Endpoint.class, attribute = "id")
+ *     String id();
+ *
+ *     @AliasFor(annotation = Endpoint.class, attribute = "enableByDefault")
+ *     boolean enableByDefault() default true;
+ *
+ * } 
+ * @author Phillip Webb + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface FilteredEndpoint { + + /** + * The filter class to use. + * @return the filter class + */ + Class> value(); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationFactory.java new file mode 100644 index 00000000000..46db9d09224 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationFactory.java @@ -0,0 +1,46 @@ +/* + * 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.annotation; + +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; + +/** + * Factory to creates an {@link Operation} for an annotated method on an + * {@link Endpoint @Endpoint}. + * + * @param the {@link Operation} type + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Phillip Webb + */ +@FunctionalInterface +public interface OperationFactory { + + /** + * Creates an {@link Operation} for an operation on an endpoint. + * @param endpointId the id of the endpoint + * @param target the target that implements the operation + * @param methodInfo the method on the bean that implements the operation + * @param invoker the invoker that should be used for the operation + * @return the operation info that describes the operation + */ + T createOperation(String endpointId, OperationMethodInfo methodInfo, Object target, + OperationInvoker invoker); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationsFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationsFactory.java new file mode 100644 index 00000000000..ab317a3ce57 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationsFactory.java @@ -0,0 +1,113 @@ +/* + * 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.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ReflectiveOperationInvoker; +import org.springframework.core.MethodIntrospector; +import org.springframework.core.MethodIntrospector.MetadataLookup; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; + +/** + * Factory to creates an {@link Operation} for a annotated methods on an + * {@link Endpoint @Endpoint}. + * + * @param The operation type + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Phillip Webb + */ +class OperationsFactory { + + private static final Map> OPERATION_TYPES; + + static { + Map> operationTypes = new LinkedHashMap<>(); + operationTypes.put(OperationType.READ, ReadOperation.class); + operationTypes.put(OperationType.WRITE, WriteOperation.class); + operationTypes.put(OperationType.DELETE, DeleteOperation.class); + OPERATION_TYPES = Collections.unmodifiableMap(operationTypes); + } + + private final OperationFactory operationFactory; + + private final ParameterMapper parameterMapper; + + private final Collection invokerAdvisors; + + OperationsFactory(OperationFactory operationFactory, + ParameterMapper parameterMapper, + Collection invokerAdvisors) { + this.operationFactory = operationFactory; + this.parameterMapper = parameterMapper; + this.invokerAdvisors = (invokerAdvisors == null ? Collections.emptyList() + : new ArrayList<>(invokerAdvisors)); + } + + public Map createOperations(String id, Object target, Class type) { + return MethodIntrospector.selectMethods(type, + (MetadataLookup) (method) -> createOperation(id, target, method)); + } + + private T createOperation(String endpointId, Object target, Method method) { + return OPERATION_TYPES.entrySet().stream() + .map((entry) -> createOperation(endpointId, target, method, + entry.getKey(), entry.getValue())) + .filter(Objects::nonNull).findFirst().orElse(null); + } + + private T createOperation(String endpointId, Object target, Method method, + OperationType operationType, Class annotationType) { + AnnotationAttributes annotationAttributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(method, annotationType); + if (annotationAttributes == null) { + return null; + } + OperationMethodInfo methodInfo = new OperationMethodInfo(method, operationType, + annotationAttributes); + OperationInvoker invoker = new ReflectiveOperationInvoker(target, methodInfo, + this.parameterMapper); + return this.operationFactory.createOperation(endpointId, methodInfo, target, + applyAdvisors(endpointId, methodInfo, invoker)); + } + + private OperationInvoker applyAdvisors(String endpointId, + OperationMethodInfo methodInfo, OperationInvoker invoker) { + if (this.invokerAdvisors != null) { + for (OperationMethodInvokerAdvisor advisor : this.invokerAdvisors) { + invoker = advisor.apply(endpointId, methodInfo, invoker); + } + } + return invoker; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/ReadOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/ReadOperation.java index d4688f2ef2e..c1fb2027cd2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/ReadOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/ReadOperation.java @@ -35,7 +35,6 @@ public @interface ReadOperation { /** * The media type of the result of the operation. - * * @return the media type */ String[] produces() default {}; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/WriteOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/WriteOperation.java index 0fe850965de..c29c323de63 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/WriteOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/WriteOperation.java @@ -35,7 +35,6 @@ public @interface WriteOperation { /** * The media type of the result of the operation. - * * @return the media type */ String[] produces() default {}; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfiguration.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfiguration.java deleted file mode 100644 index 0021b2f7bde..00000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.cache; - -/** - * The caching configuration of an endpoint. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class CachingConfiguration { - - private final long timeToLive; - - /** - * Create a new instance with the given {@code timeToLive}. - * @param timeToLive the time to live of an operation result in milliseconds - */ - public CachingConfiguration(long timeToLive) { - this.timeToLive = timeToLive; - } - - /** - * Returns the time to live of a cached operation result. - * @return the time to live of an operation result - */ - public long getTimeToLive() { - return this.timeToLive; - } - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisor.java new file mode 100644 index 00000000000..91d72c09509 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisor.java @@ -0,0 +1,54 @@ +/* + * 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.cache; + +import java.util.function.Function; + +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; + +/** + * {@link OperationMethodInvokerAdvisor} to optionally wrap an {@link OperationInvoker} + * with a {@link CachingOperationInvoker}. + * + * @author Stephane Nicoll + * @since 2.0.0 + */ +public class CachingOperationInvokerAdvisor implements OperationMethodInvokerAdvisor { + + private final Function endpointIdTimeToLive; + + public CachingOperationInvokerAdvisor(Function endpointIdTimeToLive) { + this.endpointIdTimeToLive = endpointIdTimeToLive; + } + + @Override + public OperationInvoker apply(String endpointId, OperationMethodInfo methodInfo, + OperationInvoker invoker) { + if (methodInfo.getOperationType() == OperationType.READ + && methodInfo.getParameters().isEmpty()) { + Long timeToLive = this.endpointIdTimeToLive.apply(endpointId); + if (timeToLive != null && timeToLive > 0) { + return new CachingOperationInvoker(invoker, timeToLive); + } + } + return invoker; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapper.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapper.java index 437225af91d..3b44790b839 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapper.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapper.java @@ -16,8 +16,8 @@ package org.springframework.boot.actuate.endpoint.convert; -import org.springframework.boot.actuate.endpoint.ParameterMapper; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; import org.springframework.boot.context.properties.bind.convert.BinderConversionService; import org.springframework.core.convert.ConversionService; import org.springframework.format.support.DefaultFormattingConversionService; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java index 9119c0795e7..ab0fea59dfb 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java @@ -33,8 +33,8 @@ import javax.management.ReflectionException; import reactor.core.publisher.Mono; import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.util.ClassUtils; /** @@ -76,7 +76,7 @@ public class EndpointMBean implements DynamicMBean { @Override public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { - JmxEndpointOperation operation = this.endpointInfo.getOperations() + JmxOperation operation = this.endpointInfo.getOperations() .get(actionName); if (operation != null) { Map arguments = getArguments(params, diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfo.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfo.java index feed7ec1e95..b189c8fca59 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfo.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfo.java @@ -36,10 +36,10 @@ public final class EndpointMBeanInfo { private final MBeanInfo mBeanInfo; - private final Map operations; + private final Map operations; public EndpointMBeanInfo(String endpointId, MBeanInfo mBeanInfo, - Map operations) { + Map operations) { this.endpointId = endpointId; this.mBeanInfo = mBeanInfo; this.operations = operations; @@ -53,7 +53,7 @@ public final class EndpointMBeanInfo { return this.mBeanInfo; } - public Map getOperations() { + public Map getOperations() { return this.operations; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssembler.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssembler.java index d0379420d79..ea9f54ceca0 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssembler.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssembler.java @@ -54,12 +54,12 @@ class EndpointMBeanInfoAssembler { * @return the mbean info for the endpoint */ EndpointMBeanInfo createEndpointMBeanInfo( - EndpointInfo endpointInfo) { + EndpointInfo endpointInfo) { Map operationsMapping = getOperationInfo(endpointInfo); ModelMBeanOperationInfo[] operationsMBeanInfo = operationsMapping.values() .stream().map((t) -> t.mBeanOperationInfo).collect(Collectors.toList()) .toArray(new ModelMBeanOperationInfo[] {}); - Map operationsInfo = new LinkedHashMap<>(); + Map operationsInfo = new LinkedHashMap<>(); operationsMapping.forEach((name, t) -> operationsInfo.put(name, t.operation)); MBeanInfo info = new ModelMBeanInfoSupport(EndpointMBean.class.getName(), getDescription(endpointInfo), new ModelMBeanAttributeInfo[0], @@ -73,7 +73,7 @@ class EndpointMBeanInfoAssembler { } private Map getOperationInfo( - EndpointInfo endpointInfo) { + EndpointInfo endpointInfo) { Map operationInfos = new HashMap<>(); endpointInfo.getOperations().forEach((operationInfo) -> { String name = operationInfo.getOperationName(); @@ -88,7 +88,7 @@ class EndpointMBeanInfoAssembler { return operationInfos; } - private MBeanParameterInfo[] getMBeanParameterInfos(JmxEndpointOperation operation) { + private MBeanParameterInfo[] getMBeanParameterInfos(JmxOperation operation) { return operation.getParameters().stream() .map((operationParameter) -> new MBeanParameterInfo( operationParameter.getName(), @@ -113,10 +113,10 @@ class EndpointMBeanInfoAssembler { private final ModelMBeanOperationInfo mBeanOperationInfo; - private final JmxEndpointOperation operation; + private final JmxOperation operation; OperationInfos(ModelMBeanOperationInfo mBeanOperationInfo, - JmxEndpointOperation operation) { + JmxOperation operation) { this.mBeanOperationInfo = mBeanOperationInfo; this.operation = operation; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointMBeanFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointMBeanFactory.java index e4f884e9e2b..250e12cd487 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointMBeanFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointMBeanFactory.java @@ -50,11 +50,11 @@ public class JmxEndpointMBeanFactory { * @return the MBeans */ public Collection createMBeans( - Collection> endpoints) { + Collection> endpoints) { return endpoints.stream().map(this::createMBean).collect(Collectors.toList()); } - private EndpointMBean createMBean(EndpointInfo endpointInfo) { + private EndpointMBean createMBean(EndpointInfo endpointInfo) { EndpointMBeanInfo endpointMBeanInfo = this.assembler .createEndpointMBeanInfo(endpointInfo); return new EndpointMBean(this.resultMapper::mapResponse, endpointMBeanInfo); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxOperation.java similarity index 87% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointOperation.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxOperation.java index 446127c62e3..aabf44c992d 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxEndpointOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/JmxOperation.java @@ -22,6 +22,7 @@ import java.util.List; import org.springframework.boot.actuate.endpoint.Operation; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.core.style.ToStringCreator; /** * An operation on a JMX endpoint. @@ -30,7 +31,7 @@ import org.springframework.boot.actuate.endpoint.OperationType; * @author Andy Wilkinson * @since 2.0.0 */ -public class JmxEndpointOperation extends Operation { +public class JmxOperation extends Operation { private final String operationName; @@ -50,7 +51,7 @@ public class JmxEndpointOperation extends Operation { * @param description the description of the operation * @param parameters the parameters of the operation */ - public JmxEndpointOperation(OperationType type, OperationInvoker invoker, + public JmxOperation(OperationType type, OperationInvoker invoker, String operationName, Class outputType, String description, List parameters) { super(type, invoker, true); @@ -92,4 +93,11 @@ public class JmxEndpointOperation extends Operation { return Collections.unmodifiableList(this.parameters); } + @Override + public String toString() { + return new ToStringCreator(this).append("operationName", this.operationName) + .append("outputType", this.outputType) + .append("description", this.description).toString(); + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/EndpointJmxExtension.java similarity index 82% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/EndpointJmxExtension.java index 8ff93938d4e..652e27ed2aa 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/EndpointJmxExtension.java @@ -23,6 +23,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; +import org.springframework.core.annotation.AliasFor; /** * Identifies a type as being a JMX-specific extension of an {@link Endpoint}. @@ -34,12 +36,14 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -public @interface JmxEndpointExtension { +@EndpointExtension(filter = JmxEndpointFilter.class) +public @interface EndpointJmxExtension { /** * The {@link Endpoint endpoint} class to which this JMX extension relates. * @return the endpoint class */ + @AliasFor(annotation = EndpointExtension.class) Class endpoint(); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscoverer.java index abeea749465..466c0c27b92 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscoverer.java @@ -16,78 +16,56 @@ package org.springframework.boot.actuate.endpoint.jmx.annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Date; import java.util.List; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.OperationType; -import org.springframework.boot.actuate.endpoint.ParameterMapper; -import org.springframework.boot.actuate.endpoint.ReflectiveOperationInvoker; +import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.annotation.AnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -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.jmx.JmxEndpointOperation; -import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperationParameterInfo; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource; -import org.springframework.jmx.export.metadata.ManagedOperation; -import org.springframework.jmx.export.metadata.ManagedOperationParameter; -import org.springframework.util.StringUtils; /** * Discovers the {@link Endpoint endpoints} in an {@link ApplicationContext} with - * {@link JmxEndpointExtension JMX extensions} applied to them. + * {@link EndpointJmxExtension JMX extensions} applied to them. * * @author Stephane Nicoll * @author Andy Wilkinson * @since 2.0.0 */ public class JmxAnnotationEndpointDiscoverer - extends AnnotationEndpointDiscoverer { + extends AnnotationEndpointDiscoverer { - private static final AnnotationJmxAttributeSource jmxAttributeSource = new AnnotationJmxAttributeSource(); + static final AnnotationJmxAttributeSource jmxAttributeSource = new AnnotationJmxAttributeSource(); /** * Creates a new {@link JmxAnnotationEndpointDiscoverer} that will discover - * {@link Endpoint endpoints} and {@link JmxEndpointExtension jmx extensions} using + * {@link Endpoint endpoints} and {@link EndpointJmxExtension jmx extensions} using * the given {@link ApplicationContext}. * @param applicationContext the application context * @param parameterMapper the {@link ParameterMapper} used to convert arguments when * an operation is invoked - * @param cachingConfigurationFactory the {@link CachingConfiguration} factory to use + * @param invokerAdvisors advisors used to add additional invoker advise + * @param filters filters that must match for an endpoint to be exposed */ public JmxAnnotationEndpointDiscoverer(ApplicationContext applicationContext, ParameterMapper parameterMapper, - CachingConfigurationFactory cachingConfigurationFactory) { - super(applicationContext, new JmxEndpointOperationFactory(parameterMapper), - JmxEndpointOperation::getOperationName, cachingConfigurationFactory); + Collection invokerAdvisors, + Collection> filters) { + super(applicationContext, new JmxEndpointOperationFactory(), + JmxOperation::getOperationName, parameterMapper, invokerAdvisors, + filters); } @Override - public Collection> discoverEndpoints() { - Collection> endpointDescriptors = discoverEndpoints( - JmxEndpointExtension.class, EndpointExposure.JMX); - verifyThatOperationsHaveDistinctName(endpointDescriptors); - return endpointDescriptors.stream().map(EndpointInfoDescriptor::getEndpointInfo) - .collect(Collectors.toList()); - } - - private void verifyThatOperationsHaveDistinctName( - Collection> endpointDescriptors) { - List> clashes = new ArrayList<>(); - endpointDescriptors.forEach((descriptor) -> clashes - .addAll(descriptor.findDuplicateOperations().values())); + protected void verify(Collection exposedEndpoints) { + List> clashes = new ArrayList<>(); + exposedEndpoints.forEach((exposedEndpoint) -> clashes + .addAll(exposedEndpoint.findDuplicateOperations().values())); if (!clashes.isEmpty()) { StringBuilder message = new StringBuilder(); message.append( @@ -102,97 +80,4 @@ public class JmxAnnotationEndpointDiscoverer } } - private static class JmxEndpointOperationFactory - implements EndpointOperationFactory { - - private final ParameterMapper parameterMapper; - - JmxEndpointOperationFactory(ParameterMapper parameterMapper) { - this.parameterMapper = parameterMapper; - } - - @Override - public JmxEndpointOperation createOperation(String endpointId, - AnnotationAttributes operationAttributes, Object target, Method method, - OperationType type, long timeToLive) { - ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(target, - method, this.parameterMapper); - String operationName = method.getName(); - Class outputType = getJmxType(method.getReturnType()); - String description = getDescription(method, - () -> "Invoke " + operationName + " for endpoint " + endpointId); - List parameters = getParameters(invoker); - return new JmxEndpointOperation(type, - CachingOperationInvoker.apply(invoker, timeToLive), operationName, - outputType, description, parameters); - } - - private String getDescription(Method method, Supplier fallback) { - ManagedOperation managedOperation = jmxAttributeSource - .getManagedOperation(method); - if (managedOperation != null - && StringUtils.hasText(managedOperation.getDescription())) { - return managedOperation.getDescription(); - } - return fallback.get(); - } - - private List getParameters( - ReflectiveOperationInvoker invoker) { - if (invoker.getMethod().getParameterCount() == 0) { - return Collections.emptyList(); - } - ManagedOperationParameter[] operationParameters = jmxAttributeSource - .getManagedOperationParameters(invoker.getMethod()); - if (operationParameters.length == 0) { - return invoker.getParameters(this::getParameter); - } - return mergeParameters(invoker.getMethod().getParameters(), - operationParameters); - } - - private List mergeParameters( - Parameter[] methodParameters, - ManagedOperationParameter[] operationParameters) { - List parameters = new ArrayList<>(); - for (int i = 0; i < operationParameters.length; i++) { - ManagedOperationParameter operationParameter = operationParameters[i]; - Parameter methodParameter = methodParameters[i]; - JmxEndpointOperationParameterInfo parameter = getParameter( - operationParameter.getName(), methodParameter, - operationParameter.getDescription()); - parameters.add(parameter); - } - return parameters; - } - - private JmxEndpointOperationParameterInfo getParameter(String name, - Parameter methodParameter) { - return getParameter(name, methodParameter, null); - } - - private JmxEndpointOperationParameterInfo getParameter(String name, - Parameter methodParameter, String description) { - return new JmxEndpointOperationParameterInfo(name, - getJmxType(methodParameter.getType()), description); - } - - private Class getJmxType(Class type) { - if (type.isEnum()) { - return String.class; - } - if (Date.class.isAssignableFrom(type)) { - return String.class; - } - if (type.getName().startsWith("java.")) { - return type; - } - if (type.equals(Void.TYPE)) { - return type; - } - return Object.class; - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpoint.java new file mode 100644 index 00000000000..def482bdcda --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpoint.java @@ -0,0 +1,59 @@ +/* + * 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.jmx.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.actuate.endpoint.annotation.AnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.FilteredEndpoint; +import org.springframework.core.annotation.AliasFor; + +/** + * Identifies a type as being an endpoint that is only exposed over JMX. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 2.0.0 + * @see AnnotationEndpointDiscoverer + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Endpoint +@FilteredEndpoint(JmxEndpointFilter.class) +public @interface JmxEndpoint { + + /** + * The id of the endpoint. + * @return the id + */ + @AliasFor(annotation = Endpoint.class) + String id() default ""; + + /** + * If the endpoint should be enabled or disabled by default. + * @return {@code true} if the endpoint is enabled by default + */ + @AliasFor(annotation = Endpoint.class) + boolean enableByDefault() default true; + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointFilter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointFilter.java new file mode 100644 index 00000000000..d5519aa7932 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointFilter.java @@ -0,0 +1,38 @@ +/* + * 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.jmx.annotation; + +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; + +/** + * {@link EndpointFilter} for endpoints discovered by + * {@link JmxAnnotationEndpointDiscoverer}. + * + * @author Phillip Webb + */ +class JmxEndpointFilter implements EndpointFilter { + + @Override + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return (discoverer instanceof JmxAnnotationEndpointDiscoverer); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointOperationFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointOperationFactory.java new file mode 100644 index 00000000000..81e625914a4 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointOperationFactory.java @@ -0,0 +1,128 @@ +/* + * 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.jmx.annotation; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.boot.actuate.endpoint.annotation.OperationFactory; +import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperationParameterInfo; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; +import org.springframework.jmx.export.metadata.ManagedOperation; +import org.springframework.jmx.export.metadata.ManagedOperationParameter; +import org.springframework.util.StringUtils; + +/** + * {@link OperationFactory} for {@link JmxOperation JMX operations}. + * + * @author Stephane Nicoll + * @author Andy Wilkinson + * @author Phillip Webb + */ +class JmxEndpointOperationFactory implements OperationFactory { + + @Override + public JmxOperation createOperation(String endpointId, OperationMethodInfo methodInfo, + Object target, OperationInvoker invoker) { + Method method = methodInfo.getMethod(); + String name = method.getName(); + OperationType operationType = methodInfo.getOperationType(); + Class outputType = getJmxType(method.getReturnType()); + String description = getDescription(method, + () -> "Invoke " + name + " for endpoint " + endpointId); + return new JmxOperation(operationType, invoker, name, outputType, description, + getParameters(methodInfo)); + } + + private String getDescription(Method method, Supplier fallback) { + ManagedOperation managedOperation = JmxAnnotationEndpointDiscoverer.jmxAttributeSource + .getManagedOperation(method); + if (managedOperation != null + && StringUtils.hasText(managedOperation.getDescription())) { + return managedOperation.getDescription(); + } + return fallback.get(); + } + + private List getParameters( + OperationMethodInfo methodInfo) { + if (methodInfo.getParameters().isEmpty()) { + return Collections.emptyList(); + } + Method method = methodInfo.getMethod(); + ManagedOperationParameter[] operationParameters = JmxAnnotationEndpointDiscoverer.jmxAttributeSource + .getManagedOperationParameters(method); + if (operationParameters.length == 0) { + return methodInfo.getParameters().entrySet().stream().map(this::getParameter) + .collect(Collectors.toCollection(ArrayList::new)); + } + return mergeParameters(method.getParameters(), operationParameters); + } + + private List mergeParameters( + Parameter[] methodParameters, + ManagedOperationParameter[] operationParameters) { + List parameters = new ArrayList<>(); + for (int i = 0; i < operationParameters.length; i++) { + ManagedOperationParameter operationParameter = operationParameters[i]; + Parameter methodParameter = methodParameters[i]; + JmxEndpointOperationParameterInfo parameter = getParameter( + operationParameter.getName(), methodParameter, + operationParameter.getDescription()); + parameters.add(parameter); + } + return parameters; + } + + private JmxEndpointOperationParameterInfo getParameter( + Map.Entry entry) { + return getParameter(entry.getKey(), entry.getValue(), null); + } + + private JmxEndpointOperationParameterInfo getParameter(String name, + Parameter methodParameter, String description) { + return new JmxEndpointOperationParameterInfo(name, + getJmxType(methodParameter.getType()), description); + } + + private Class getJmxType(Class type) { + if (type.isEnum()) { + return String.class; + } + if (Date.class.isAssignableFrom(type)) { + return String.class; + } + if (type.getName().startsWith("java.")) { + return type; + } + if (type.equals(Void.TYPE)) { + return type; + } + return Object.class; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/package-info.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/package-info.java index 03610f7d712..ecf691cce65 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/package-info.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/package-info.java @@ -15,6 +15,6 @@ */ /** - * Actuator endpoint infrastructure. + * Endpoint support. */ package org.springframework.boot.actuate.endpoint; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInfo.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInfo.java new file mode 100644 index 00000000000..1a17c08968e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInfo.java @@ -0,0 +1,99 @@ +/* + * 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.reflect; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.util.Assert; + +/** + * Information describing an operation method on an endpoint method. + * + * @author Phillip Webb + * @since 2.0.0 + * @see ReflectiveOperationInvoker + */ +public final class OperationMethodInfo { + + private static final ParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); + + private final Method method; + + private final OperationType operationType; + + private final AnnotationAttributes annotationAttributes; + + private final ParameterNameDiscoverer parameterNameDiscoverer = DEFAULT_PARAMETER_NAME_DISCOVERER; + + public OperationMethodInfo(Method method, OperationType operationType, + AnnotationAttributes annotationAttributes) { + Assert.notNull(method, "Method must not be null"); + Assert.notNull(operationType, "Operation Type must not be null"); + Assert.notNull(annotationAttributes, "Annotation Attributes must not be null"); + this.method = method; + this.operationType = operationType; + this.annotationAttributes = annotationAttributes; + } + + /** + * Return the source Java method. + * @return the method + */ + public Method getMethod() { + return this.method; + } + + /** + * Return the operation type. + * @return the operation type + */ + public OperationType getOperationType() { + return this.operationType; + } + + /** + * Return the mime type that the operation produces. + * @return the produced mime type + */ + public String[] getProduces() { + return this.annotationAttributes.getStringArray("produces"); + } + + /** + * Return a map of method parameters with the key being the discovered parameter name. + * @return the method parameters + */ + public Map getParameters() { + Parameter[] parameters = this.method.getParameters(); + String[] names = this.parameterNameDiscoverer.getParameterNames(this.method); + Assert.state(names != null, + "Failed to extract parameter names for " + this.method); + Map result = new LinkedHashMap<>(); + for (int i = 0; i < names.length; i++) { + result.put(names[i], parameters[i]); + } + return result; + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfigurationFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInvokerAdvisor.java similarity index 51% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfigurationFactory.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInvokerAdvisor.java index c664eb6f611..3a92637d87e 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/cache/CachingConfigurationFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/OperationMethodInvokerAdvisor.java @@ -14,22 +14,28 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint.cache; +package org.springframework.boot.actuate.endpoint.reflect; + +import org.springframework.boot.actuate.endpoint.OperationInvoker; /** - * Factory used to return a {@link CachingConfiguration} for an endpoint ID. + * Allows additional functionality to be applied to an {@link OperationInvoker} being used + * to invoke a {@link OperationMethodInfo operation method}. * - * @author Stephane Nicoll + * @author Phillip Webb * @since 2.0.0 */ @FunctionalInterface -public interface CachingConfigurationFactory { +public interface OperationMethodInvokerAdvisor { /** - * Return the {@link CachingConfiguration} for the given endpoint ID. + * Apply additional functionality to the given invoker. * @param endpointId the endpoint ID - * @return the caching configuration + * @param methodInfo the method information + * @param invoker the invoker to advise + * @return an potentially new operation invoker with support for additional features. */ - CachingConfiguration getCachingConfiguration(String endpointId); + OperationInvoker apply(String endpointId, OperationMethodInfo methodInfo, + OperationInvoker invoker); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMapper.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMapper.java similarity index 95% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMapper.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMapper.java index 5dfcd667a86..be0fea7808e 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMapper.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMapper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.actuate.endpoint.reflect; /** * Maps parameters to the required type when invoking an endpoint. diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMappingException.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMappingException.java similarity index 96% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMappingException.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMappingException.java index f3dc19e104d..0cc45e12f0c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParameterMappingException.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParameterMappingException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.actuate.endpoint.reflect; /** * A {@code ParameterMappingException} is thrown when a failure occurs during diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParametersMissingException.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParametersMissingException.java similarity index 95% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParametersMissingException.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParametersMissingException.java index a49af1a7d3a..3f8d14a9bff 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ParametersMissingException.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ParametersMissingException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.actuate.endpoint.reflect; import java.util.Set; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ReflectiveOperationInvoker.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ReflectiveOperationInvoker.java similarity index 52% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ReflectiveOperationInvoker.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ReflectiveOperationInvoker.java index efa13dcfa03..d3294d767b0 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ReflectiveOperationInvoker.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/ReflectiveOperationInvoker.java @@ -14,20 +14,14 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.actuate.endpoint.reflect; -import java.lang.reflect.Method; import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.BiFunction; import java.util.stream.Collectors; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -41,93 +35,37 @@ import org.springframework.util.ReflectionUtils; */ public class ReflectiveOperationInvoker implements OperationInvoker { - private static final ParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); - private final Object target; - private final Method method; + private final OperationMethodInfo methodInfo; private final ParameterMapper parameterMapper; - private final ParameterNameDiscoverer parameterNameDiscoverer; - /** * Creates a new {code ReflectiveOperationInvoker} that will invoke the given * {@code method} on the given {@code target}. The given {@code parameterMapper} will * be used to map parameters to the required types and the given * {@code parameterNameMapper} will be used map parameters by name. * @param target the target of the reflective call - * @param method the method to call + * @param methodInfo the method info * @param parameterMapper the parameter mapper */ - public ReflectiveOperationInvoker(Object target, Method method, + public ReflectiveOperationInvoker(Object target, OperationMethodInfo methodInfo, ParameterMapper parameterMapper) { - this(target, method, parameterMapper, DEFAULT_PARAMETER_NAME_DISCOVERER); - } - - /** - * Creates a new {code ReflectiveOperationInvoker} that will invoke the given - * {@code method} on the given {@code target}. The given {@code parameterMapper} will - * be used to map parameters to the required types and the given - * {@code parameterNameMapper} will be used map parameters by name. - * @param target the target of the reflective call - * @param method the method to call - * @param parameterMapper the parameter mapper - * @param parameterNameDiscoverer the parameter name discoverer - */ - public ReflectiveOperationInvoker(Object target, Method method, - ParameterMapper parameterMapper, - ParameterNameDiscoverer parameterNameDiscoverer) { Assert.notNull(target, "Target must not be null"); - Assert.notNull(method, "Method must not be null"); + Assert.notNull(methodInfo, "MethodInfo must not be null"); Assert.notNull(parameterMapper, "ParameterMapper must not be null"); - Assert.notNull(parameterNameDiscoverer, - "ParameterNameDiscoverer must not be null"); - ReflectionUtils.makeAccessible(method); + ReflectionUtils.makeAccessible(methodInfo.getMethod()); this.target = target; - this.method = method; + this.methodInfo = methodInfo; this.parameterMapper = parameterMapper; - this.parameterNameDiscoverer = parameterNameDiscoverer; - } - - /** - * Return the method that will be called on invocation. - * @return the method to be called - */ - public Method getMethod() { - return this.method; - } - - /** - * Return the parameters of the method mapped using the given function. - * @param mapper a mapper {@link BiFunction} taking the discovered name and parameter - * as input and returning the mapped type. - * @param the mapped type - * @return a list of parameters mapped to the desired type - */ - public List getParameters(BiFunction mapper) { - return getParameters().entrySet().stream() - .map((entry) -> mapper.apply(entry.getKey(), entry.getValue())) - .collect(Collectors.toCollection(ArrayList::new)); - } - - private Map getParameters() { - Parameter[] parameters = this.method.getParameters(); - String[] names = this.parameterNameDiscoverer.getParameterNames(this.method); - Assert.state(names != null, - "Failed to extract parameter names for " + this.method); - Map result = new LinkedHashMap<>(); - for (int i = 0; i < names.length; i++) { - result.put(names[i], parameters[i]); - } - return result; } @Override public Object invoke(Map arguments) { - Map parameters = getParameters(); + Map parameters = this.methodInfo.getParameters(); validateRequiredParameters(parameters, arguments); - return ReflectionUtils.invokeMethod(this.method, this.target, + return ReflectionUtils.invokeMethod(this.methodInfo.getMethod(), this.target, resolveArguments(parameters, arguments)); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/EndpointExposure.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/package-info.java similarity index 85% rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/EndpointExposure.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/package-info.java index 1a82c189329..f4536f7a993 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/EndpointExposure.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/reflect/package-info.java @@ -14,12 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.configurationsample; - -public enum EndpointExposure { - - JMX, - - WEB - -} +/** + * Endpoint reflection support. + */ +package org.springframework.boot.actuate.endpoint.reflect; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java index 395c1d1f282..e3dde0783d7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java @@ -38,13 +38,13 @@ public class EndpointLinksResolver { * @return the links */ public Map resolveLinks( - Collection> webEndpoints, + Collection> webEndpoints, String requestUrl) { String normalizedUrl = normalizeRequestUrl(requestUrl); Map links = new LinkedHashMap<>(); links.put("self", new Link(normalizedUrl)); - for (EndpointInfo endpoint : webEndpoints) { - for (WebEndpointOperation operation : endpoint.getOperations()) { + for (EndpointInfo endpoint : webEndpoints) { + for (WebOperation operation : endpoint.getOperations()) { webEndpoints.stream().map(EndpointInfo::getId).forEach((id) -> links .put(operation.getId(), createLink(normalizedUrl, operation))); } @@ -59,7 +59,7 @@ public class EndpointLinksResolver { return requestUrl; } - private Link createLink(String requestUrl, WebEndpointOperation operation) { + private Link createLink(String requestUrl, WebOperation operation) { String path = operation.getRequestPredicate().getPath(); return new Link(requestUrl + (path.startsWith("/") ? path : "/" + path)); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointResponse.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointResponse.java index 6834bd9eb12..ba66c762510 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointResponse.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointResponse.java @@ -16,11 +16,11 @@ package org.springframework.boot.actuate.endpoint.web; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; /** * A {@code WebEndpointResponse} can be returned by an operation on a - * {@link WebEndpointExtension} to provide additional, web-specific information such as + * {@link EndpointWebExtension} to provide additional, web-specific information such as * the HTTP status code. * * @param the type of the response body diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointOperation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebOperation.java similarity index 94% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointOperation.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebOperation.java index c16f07de39a..a5257e5b269 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebEndpointOperation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebOperation.java @@ -26,7 +26,7 @@ import org.springframework.boot.actuate.endpoint.OperationType; * @author Andy Wilkinson * @since 2.0.0 */ -public class WebEndpointOperation extends Operation { +public class WebOperation extends Operation { private final OperationRequestPredicate requestPredicate; @@ -42,7 +42,7 @@ public class WebEndpointOperation extends Operation { * @param requestPredicate the predicate for requests that can be handled by the * @param id the id of the operation, unique within its endpoint operation */ - public WebEndpointOperation(OperationType type, OperationInvoker operationInvoker, + public WebOperation(OperationType type, OperationInvoker operationInvoker, boolean blocking, OperationRequestPredicate requestPredicate, String id) { super(type, operationInvoker, blocking); this.requestPredicate = requestPredicate; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/EndpointWebExtension.java similarity index 83% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/EndpointWebExtension.java index d09202261fb..c8e89e86ff3 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/EndpointWebExtension.java @@ -23,6 +23,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; +import org.springframework.core.annotation.AliasFor; /** * Identifies a type as being a Web-specific extension of an {@link Endpoint}. @@ -35,12 +37,14 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -public @interface WebEndpointExtension { +@EndpointExtension(filter = WebEndpointFilter.class) +public @interface EndpointWebExtension { /** * The {@link Endpoint endpoint} class to which this Web extension relates. * @return the endpoint class */ + @AliasFor(annotation = EndpointExtension.class) Class endpoint(); } 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 773140a77b0..55627354c62 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 @@ -16,221 +16,75 @@ package org.springframework.boot.actuate.endpoint.web.annotation; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.reactivestreams.Publisher; - -import org.springframework.boot.actuate.endpoint.EndpointExposure; -import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.OperationInvoker; -import org.springframework.boot.actuate.endpoint.OperationType; -import org.springframework.boot.actuate.endpoint.ParameterMapper; -import org.springframework.boot.actuate.endpoint.ReflectiveOperationInvoker; +import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.annotation.AnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.annotation.Selector; -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.reflect.OperationMethodInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; 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; -import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.context.ApplicationContext; -import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.io.Resource; -import org.springframework.util.ClassUtils; /** * Discovers the {@link Endpoint endpoints} in an {@link ApplicationContext} with - * {@link WebEndpointExtension web extensions} applied to them. + * {@link EndpointWebExtension web extensions} applied to them. * * @author Andy Wilkinson * @author Stephane Nicoll + * @author Phillip Webb * @since 2.0.0 */ -public class WebAnnotationEndpointDiscoverer extends - AnnotationEndpointDiscoverer { +public class WebAnnotationEndpointDiscoverer + extends AnnotationEndpointDiscoverer { /** * Creates a new {@link WebAnnotationEndpointDiscoverer} that will discover - * {@link Endpoint endpoints} and {@link WebEndpointExtension web extensions} using + * {@link Endpoint endpoints} and {@link EndpointWebExtension web extensions} using * the given {@link ApplicationContext}. * @param applicationContext the application context * @param parameterMapper the {@link ParameterMapper} used to convert arguments when * an operation is invoked - * @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 + * @param invokerAdvisors advisors used to add additional invoker advise + * @param filters filters that must match for an endpoint to be exposed */ public WebAnnotationEndpointDiscoverer(ApplicationContext applicationContext, - ParameterMapper parameterMapper, - CachingConfigurationFactory cachingConfigurationFactory, - EndpointMediaTypes endpointMediaTypes, - EndpointPathResolver endpointPathResolver) { + ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver, + Collection invokerAdvisors, + Collection> filters) { super(applicationContext, - new WebEndpointOperationFactory(parameterMapper, endpointMediaTypes, - endpointPathResolver), - WebEndpointOperation::getRequestPredicate, cachingConfigurationFactory); + new WebEndpointOperationFactory(endpointMediaTypes, endpointPathResolver), + WebOperation::getRequestPredicate, parameterMapper, invokerAdvisors, + filters); } @Override - public Collection> discoverEndpoints() { - Collection> endpoints = discoverEndpoints( - WebEndpointExtension.class, EndpointExposure.WEB); - verifyThatOperationsHaveDistinctPredicates(endpoints); - return endpoints.stream().map(EndpointInfoDescriptor::getEndpointInfo) - .collect(Collectors.toList()); - } - - private void verifyThatOperationsHaveDistinctPredicates( - Collection> endpointDescriptors) { - List> clashes = new ArrayList<>(); - endpointDescriptors.forEach((descriptor) -> clashes + protected void verify(Collection exposedEndpoints) { + List> clashes = new ArrayList<>(); + exposedEndpoints.forEach((descriptor) -> clashes .addAll(descriptor.findDuplicateOperations().values())); if (!clashes.isEmpty()) { StringBuilder message = new StringBuilder(); message.append(String.format( "Found multiple web operations with matching request predicates:%n")); clashes.forEach((clash) -> { - message.append(" ").append(clash.get(0).getRequestPredicate()) + message.append(" ").append(clash.get(0).getRequestPredicate()) .append(String.format(":%n")); - clash.forEach((operation) -> message.append(" ") + clash.forEach((operation) -> message.append(" ") .append(String.format("%s%n", operation))); }); throw new IllegalStateException(message.toString()); } } - private static final class WebEndpointOperationFactory - implements EndpointOperationFactory { - - private static final boolean REACTIVE_STREAMS_PRESENT = ClassUtils.isPresent( - "org.reactivestreams.Publisher", - WebEndpointOperationFactory.class.getClassLoader()); - - private final ParameterMapper parameterMapper; - - private final EndpointMediaTypes endpointMediaTypes; - - private final EndpointPathResolver endpointPathResolver; - - private WebEndpointOperationFactory(ParameterMapper parameterMapper, - EndpointMediaTypes endpointMediaTypes, - EndpointPathResolver endpointPathResolver) { - this.parameterMapper = parameterMapper; - this.endpointMediaTypes = endpointMediaTypes; - this.endpointPathResolver = endpointPathResolver; - } - - @Override - public WebEndpointOperation createOperation(String endpointId, - AnnotationAttributes operationAttributes, Object target, Method method, - OperationType type, long timeToLive) { - WebEndpointHttpMethod httpMethod = determineHttpMethod(type); - OperationRequestPredicate requestPredicate = new OperationRequestPredicate( - determinePath(endpointId, method), httpMethod, - determineConsumedMediaTypes(httpMethod, method), - determineProducedMediaTypes( - operationAttributes.getStringArray("produces"), method)); - OperationInvoker invoker = new ReflectiveOperationInvoker(target, method, - this.parameterMapper); - if (timeToLive > 0) { - invoker = new CachingOperationInvoker(invoker, timeToLive); - } - return new WebEndpointOperation(type, invoker, determineBlocking(method), - requestPredicate, determineId(endpointId, method)); - } - - private String determinePath(String endpointId, Method operationMethod) { - StringBuilder path = new StringBuilder( - this.endpointPathResolver.resolvePath(endpointId)); - Stream.of(operationMethod.getParameters()) - .filter(( - parameter) -> parameter.getAnnotation(Selector.class) != null) - .map((parameter) -> "/{" + parameter.getName() + "}") - .forEach(path::append); - return path.toString(); - } - - private String determineId(String endpointId, Method operationMethod) { - StringBuilder path = new StringBuilder(endpointId); - Stream.of(operationMethod.getParameters()) - .filter(( - parameter) -> parameter.getAnnotation(Selector.class) != null) - .map((parameter) -> "-" + parameter.getName()).forEach(path::append); - return path.toString(); - } - - private Collection determineConsumedMediaTypes( - WebEndpointHttpMethod httpMethod, Method method) { - if (WebEndpointHttpMethod.POST == httpMethod && consumesRequestBody(method)) { - return this.endpointMediaTypes.getConsumed(); - } - return Collections.emptyList(); - } - - private Collection determineProducedMediaTypes(String[] produces, - Method method) { - if (produces.length > 0) { - return Arrays.asList(produces); - } - if (Void.class.equals(method.getReturnType()) - || void.class.equals(method.getReturnType())) { - return Collections.emptyList(); - } - if (producesResourceResponseBody(method)) { - return Collections.singletonList("application/octet-stream"); - } - return this.endpointMediaTypes.getProduced(); - } - - private boolean producesResourceResponseBody(Method method) { - if (Resource.class.equals(method.getReturnType())) { - return true; - } - if (WebEndpointResponse.class.isAssignableFrom(method.getReturnType())) { - ResolvableType returnType = ResolvableType.forMethodReturnType(method); - if (ResolvableType.forClass(Resource.class) - .isAssignableFrom(returnType.getGeneric(0))) { - return true; - } - } - return false; - } - - private boolean consumesRequestBody(Method method) { - return Stream.of(method.getParameters()).anyMatch( - (parameter) -> parameter.getAnnotation(Selector.class) == null); - } - - private WebEndpointHttpMethod determineHttpMethod(OperationType operationType) { - if (operationType == OperationType.WRITE) { - return WebEndpointHttpMethod.POST; - } - if (operationType == OperationType.DELETE) { - return WebEndpointHttpMethod.DELETE; - } - return WebEndpointHttpMethod.GET; - } - - private boolean determineBlocking(Method method) { - return !REACTIVE_STREAMS_PRESENT - || !Publisher.class.isAssignableFrom(method.getReturnType()); - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpoint.java new file mode 100644 index 00000000000..2c19a331e74 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpoint.java @@ -0,0 +1,59 @@ +/* + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.actuate.endpoint.annotation.AnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.FilteredEndpoint; +import org.springframework.core.annotation.AliasFor; + +/** + * Identifies a type as being an endpoint that is only exposed over HTTP. + * + * @author Andy Wilkinson + * @author Phillip Webb + * @since 2.0.0 + * @see AnnotationEndpointDiscoverer + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Endpoint +@FilteredEndpoint(WebEndpointFilter.class) +public @interface WebEndpoint { + + /** + * The id of the endpoint. + * @return the id + */ + @AliasFor(annotation = Endpoint.class) + String id(); + + /** + * If the endpoint should be enabled or disabled by default. + * @return {@code true} if the endpoint is enabled by default + */ + @AliasFor(annotation = Endpoint.class) + boolean enableByDefault() default true; + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointFilter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointFilter.java new file mode 100644 index 00000000000..071027c98fc --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointFilter.java @@ -0,0 +1,38 @@ +/* + * 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.annotation; + +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; +import org.springframework.boot.actuate.endpoint.web.WebOperation; + +/** + * {@link EndpointFilter} for endpoints discovered by + * {@link WebAnnotationEndpointDiscoverer}. + * + * @author Phillip Webb + */ +class WebEndpointFilter implements EndpointFilter { + + @Override + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return (discoverer instanceof WebAnnotationEndpointDiscoverer); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointOperationFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointOperationFactory.java new file mode 100644 index 00000000000..3d4d77b253a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointOperationFactory.java @@ -0,0 +1,154 @@ +/* + * 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.annotation; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Stream; + +import org.reactivestreams.Publisher; + +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.boot.actuate.endpoint.annotation.OperationFactory; +import org.springframework.boot.actuate.endpoint.annotation.Selector; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; +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.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; +import org.springframework.core.ResolvableType; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; + +/** + * {@link OperationFactory} for {@link WebOperation web operations}. + * + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Phillip Webb + */ +final class WebEndpointOperationFactory implements OperationFactory { + + private static final boolean REACTIVE_STREAMS_PRESENT = ClassUtils.isPresent( + "org.reactivestreams.Publisher", + WebEndpointOperationFactory.class.getClassLoader()); + + private final EndpointMediaTypes endpointMediaTypes; + + private final EndpointPathResolver endpointPathResolver; + + WebEndpointOperationFactory(EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver) { + this.endpointMediaTypes = endpointMediaTypes; + this.endpointPathResolver = endpointPathResolver; + } + + @Override + public WebOperation createOperation(String endpointId, OperationMethodInfo methodInfo, + Object target, OperationInvoker invoker) { + Method method = methodInfo.getMethod(); + OperationType operationType = methodInfo.getOperationType(); + WebEndpointHttpMethod httpMethod = determineHttpMethod(operationType); + OperationRequestPredicate requestPredicate = new OperationRequestPredicate( + determinePath(endpointId, method), httpMethod, + determineConsumedMediaTypes(httpMethod, method), + determineProducedMediaTypes(methodInfo.getProduces(), method)); + return new WebOperation(operationType, invoker, determineBlocking(method), + requestPredicate, determineId(endpointId, method)); + } + + private String determinePath(String endpointId, Method operationMethod) { + StringBuilder path = new StringBuilder( + this.endpointPathResolver.resolvePath(endpointId)); + Stream.of(operationMethod.getParameters()) + .filter((parameter) -> parameter.getAnnotation(Selector.class) != null) + .map((parameter) -> "/{" + parameter.getName() + "}") + .forEach(path::append); + return path.toString(); + } + + private String determineId(String endpointId, Method operationMethod) { + StringBuilder path = new StringBuilder(endpointId); + Stream.of(operationMethod.getParameters()) + .filter((parameter) -> parameter.getAnnotation(Selector.class) != null) + .map((parameter) -> "-" + parameter.getName()).forEach(path::append); + return path.toString(); + } + + private Collection determineConsumedMediaTypes( + WebEndpointHttpMethod httpMethod, Method method) { + if (WebEndpointHttpMethod.POST == httpMethod && consumesRequestBody(method)) { + return this.endpointMediaTypes.getConsumed(); + } + return Collections.emptyList(); + } + + private Collection determineProducedMediaTypes(String[] produces, + Method method) { + if (produces.length > 0) { + return Arrays.asList(produces); + } + if (Void.class.equals(method.getReturnType()) + || void.class.equals(method.getReturnType())) { + return Collections.emptyList(); + } + if (producesResourceResponseBody(method)) { + return Collections.singletonList("application/octet-stream"); + } + return this.endpointMediaTypes.getProduced(); + } + + private boolean producesResourceResponseBody(Method method) { + if (Resource.class.equals(method.getReturnType())) { + return true; + } + if (WebEndpointResponse.class.isAssignableFrom(method.getReturnType())) { + ResolvableType returnType = ResolvableType.forMethodReturnType(method); + if (ResolvableType.forClass(Resource.class) + .isAssignableFrom(returnType.getGeneric(0))) { + return true; + } + } + return false; + } + + private boolean consumesRequestBody(Method method) { + return Stream.of(method.getParameters()) + .anyMatch((parameter) -> parameter.getAnnotation(Selector.class) == null); + } + + private WebEndpointHttpMethod determineHttpMethod(OperationType operationType) { + if (operationType == OperationType.WRITE) { + return WebEndpointHttpMethod.POST; + } + if (operationType == OperationType.DELETE) { + return WebEndpointHttpMethod.DELETE; + } + return WebEndpointHttpMethod.GET; + } + + private boolean determineBlocking(Method method) { + return !REACTIVE_STREAMS_PRESENT + || !Publisher.class.isAssignableFrom(method.getReturnType()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java index 73c7e519be1..20b46656a6a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.endpoint.web.jersey; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -39,22 +40,22 @@ import reactor.core.publisher.Mono; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * A factory for creating Jersey {@link Resource Resources} for - * {@link WebEndpointOperation web endpoint operations}. + * A factory for creating Jersey {@link Resource Resources} for {@link WebOperation web + * endpoint operations}. * * @author Andy Wilkinson * @since 2.0.0 @@ -72,7 +73,7 @@ public class JerseyEndpointResourceFactory { * @return the resources for the operations */ public Collection createEndpointResources(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes) { List resources = new ArrayList<>(); webEndpoints.stream() @@ -87,7 +88,7 @@ public class JerseyEndpointResourceFactory { } private Resource createResource(EndpointMapping endpointMapping, - WebEndpointOperation operation) { + WebOperation operation) { OperationRequestPredicate requestPredicate = operation.getRequestPredicate(); Builder resourceBuilder = Resource.builder() .path(endpointMapping.createSubPath(requestPredicate.getPath())); @@ -104,7 +105,7 @@ public class JerseyEndpointResourceFactory { } private Resource createEndpointLinksResource(String endpointPath, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes) { Builder resourceBuilder = Resource.builder().path(endpointPath); resourceBuilder.addMethod("GET") @@ -115,10 +116,13 @@ public class JerseyEndpointResourceFactory { return resourceBuilder.build(); } + /** + * {@link Inflector} to invoke the endpoint. + */ private static final class EndpointInvokingInflector implements Inflector { - private static final List> bodyConverters; + private static final List> BODY_CONVERTERS; static { List> converters = new ArrayList<>(); @@ -127,7 +131,7 @@ public class JerseyEndpointResourceFactory { EndpointInvokingInflector.class.getClassLoader())) { converters.add(new MonoBodyConverter()); } - bodyConverters = Collections.unmodifiableList(converters); + BODY_CONVERTERS = Collections.unmodifiableList(converters); } private final OperationInvoker operationInvoker; @@ -209,7 +213,7 @@ public class JerseyEndpointResourceFactory { } private Object convertIfNecessary(Object body) throws IOException { - for (Function converter : bodyConverters) { + for (Function converter : BODY_CONVERTERS) { body = converter.apply(body); } return body; @@ -217,6 +221,10 @@ public class JerseyEndpointResourceFactory { } + /** + * Body converter from {@link org.springframework.core.io.Resource} to + * {@link InputStream}. + */ private static final class ResourceBodyConverter implements Function { @Override @@ -234,6 +242,9 @@ public class JerseyEndpointResourceFactory { } + /** + * Body converter from {@link Mono} to {@link Mono#block()}. + */ private static final class MonoBodyConverter implements Function { @Override @@ -246,15 +257,17 @@ public class JerseyEndpointResourceFactory { } + /** + * {@link Inflector} to for endpoint links. + */ private static final class EndpointLinksInflector implements Inflector { - private final Collection> endpoints; + private final Collection> endpoints; private final EndpointLinksResolver linksResolver; - private EndpointLinksInflector( - Collection> endpoints, + private EndpointLinksInflector(Collection> endpoints, EndpointLinksResolver linksResolver) { this.endpoints = endpoints; this.linksResolver = linksResolver; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 3289a25c30c..51f379a796c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -28,7 +28,7 @@ import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; @@ -57,7 +57,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping private final EndpointMapping endpointMapping; - private final Collection> webEndpoints; + private final Collection> webEndpoints; private final EndpointMediaTypes endpointMediaTypes; @@ -71,7 +71,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping * @param endpointMediaTypes media types consumed and produced by the endpoints */ public AbstractWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> collection, + Collection> collection, EndpointMediaTypes endpointMediaTypes) { this(endpointMapping, collection, endpointMediaTypes, null); } @@ -85,7 +85,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping * @param corsConfiguration the CORS configuration for the endpoints */ public AbstractWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) { this.endpointMapping = endpointMapping; this.webEndpoints = webEndpoints; @@ -118,7 +118,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping } protected RequestMappingInfo createRequestMappingInfo( - WebEndpointOperation operationInfo) { + WebOperation operationInfo) { OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate(); PatternsRequestCondition patterns = new PatternsRequestCondition(pathPatternParser .parse(this.endpointMapping.createSubPath(requestPredicate.getPath()))); @@ -142,13 +142,13 @@ public abstract class AbstractWebFluxEndpointHandlerMapping return this.corsConfiguration; } - public Collection> getEndpoints() { + public Collection> getEndpoints() { return this.webEndpoints; } protected abstract Method getLinks(); - protected abstract void registerMappingForOperation(WebEndpointOperation operation); + protected abstract void registerMappingForOperation(WebOperation operation); @Override protected boolean isHandler(Class beanType) { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java index 809d6efe23d..e33c25a7f4f 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java @@ -29,13 +29,13 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.OperationType; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.Link; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -78,7 +78,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle * @param endpointMediaTypes media types consumed and produced by the endpoints */ public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> collection, + Collection> collection, EndpointMediaTypes endpointMediaTypes) { this(endpointMapping, collection, endpointMediaTypes, null); } @@ -92,7 +92,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle * @param corsConfiguration the CORS configuration for the endpoints */ public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) { super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration); setOrder(-100); @@ -104,7 +104,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle } @Override - protected void registerMappingForOperation(WebEndpointOperation operation) { + protected void registerMappingForOperation(WebOperation operation) { OperationType operationType = operation.getType(); OperationInvoker operationInvoker = operation.getInvoker(); if (operation.isBlocking()) { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index 745cd3357bf..c4e32069ea7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -27,7 +27,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.util.StringUtils; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; @@ -55,7 +55,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping private final EndpointMapping endpointMapping; - private final Collection> webEndpoints; + private final Collection> webEndpoints; private final EndpointMediaTypes endpointMediaTypes; @@ -69,7 +69,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping * @param endpointMediaTypes media types consumed and produced by the endpoints */ public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> collection, + Collection> collection, EndpointMediaTypes endpointMediaTypes) { this(endpointMapping, collection, endpointMediaTypes, null); } @@ -83,7 +83,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping * @param corsConfiguration the CORS configuration for the endpoints */ public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) { this.endpointMapping = endpointMapping; this.webEndpoints = webEndpoints; @@ -92,7 +92,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping setOrder(-100); } - public Collection> getEndpoints() { + public Collection> getEndpoints() { return this.webEndpoints; } @@ -130,10 +130,10 @@ public abstract class AbstractWebMvcEndpointHandlerMapping protected abstract Method getLinks(); - protected abstract void registerMappingForOperation(WebEndpointOperation operation); + protected abstract void registerMappingForOperation(WebOperation operation); protected RequestMappingInfo createRequestMappingInfo( - WebEndpointOperation operationInfo) { + WebOperation operationInfo) { OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate(); PatternsRequestCondition patterns = patternsRequestConditionForPattern( requestPredicate.getPath()); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java index 1d8bdca6fa3..46b6c610556 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java @@ -27,13 +27,13 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; -import org.springframework.boot.actuate.endpoint.ParametersMissingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParametersMissingException; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.Link; -import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -69,7 +69,7 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM * @param endpointMediaTypes media types consumed and produced by the endpoints */ public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> collection, + Collection> collection, EndpointMediaTypes endpointMediaTypes) { this(endpointMapping, collection, endpointMediaTypes, null); } @@ -83,14 +83,14 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM * @param corsConfiguration the CORS configuration for the endpoints */ public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection> webEndpoints, + Collection> webEndpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) { super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration); setOrder(-100); } @Override - protected void registerMappingForOperation(WebEndpointOperation operation) { + protected void registerMappingForOperation(WebOperation operation) { registerMapping(createRequestMappingInfo(operation), new OperationHandler(operation.getInvoker()), this.handle); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.java similarity index 86% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.java index e57707a7051..9d9f26cb111 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.java @@ -19,21 +19,21 @@ package org.springframework.boot.actuate.env; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; import org.springframework.boot.actuate.env.EnvironmentEndpoint.EnvironmentEntryDescriptor; /** - * {@link WebEndpointExtension} for the {@link EnvironmentEndpoint}. + * {@link EndpointWebExtension} for the {@link EnvironmentEndpoint}. * * @author Stephane Nicoll * @since 2.0.0 */ -@WebEndpointExtension(endpoint = EnvironmentEndpoint.class) -public class EnvironmentWebEndpointExtension { +@EndpointWebExtension(endpoint = EnvironmentEndpoint.class) +public class EnvironmentEndpointWebExtension { private final EnvironmentEndpoint delegate; - public EnvironmentWebEndpointExtension(EnvironmentEndpoint delegate) { + public EnvironmentEndpointWebExtension(EnvironmentEndpoint delegate) { this.delegate = delegate; } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java similarity index 86% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java index dea8d8f3ab0..5c4e35bd2a0 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java @@ -18,10 +18,10 @@ package org.springframework.boot.actuate.health; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; /** - * {@link WebEndpointExtension} for the {@link HealthEndpoint}. + * {@link EndpointWebExtension} for the {@link HealthEndpoint}. * * @author Christian Dupuis * @author Dave Syer @@ -31,14 +31,14 @@ import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExten * @author Madhura Bhave * @since 2.0.0 */ -@WebEndpointExtension(endpoint = HealthEndpoint.class) -public class HealthWebEndpointExtension { +@EndpointWebExtension(endpoint = HealthEndpoint.class) +public class HealthEndpointWebExtension { private final HealthEndpoint delegate; private final HealthStatusHttpMapper statusHttpMapper; - public HealthWebEndpointExtension(HealthEndpoint delegate, + public HealthEndpointWebExtension(HealthEndpoint delegate, HealthStatusHttpMapper statusHttpMapper) { this.delegate = delegate; this.statusHttpMapper = statusHttpMapper; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthReactiveWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java similarity index 85% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthReactiveWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java index 788691ec876..2d54eb11d4f 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthReactiveWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java @@ -20,22 +20,22 @@ import reactor.core.publisher.Mono; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; /** - * Reactive {@link WebEndpointExtension} for the {@link HealthEndpoint}. + * Reactive {@link EndpointWebExtension} for the {@link HealthEndpoint}. * * @author Stephane Nicoll * @since 2.0.0 */ -@WebEndpointExtension(endpoint = HealthEndpoint.class) -public class HealthReactiveWebEndpointExtension { +@EndpointWebExtension(endpoint = HealthEndpoint.class) +public class ReactiveHealthEndpointWebExtension { private final ReactiveHealthIndicator delegate; private final HealthStatusHttpMapper statusHttpMapper; - public HealthReactiveWebEndpointExtension(ReactiveHealthIndicator delegate, + public ReactiveHealthEndpointWebExtension(ReactiveHealthIndicator delegate, HealthStatusHttpMapper statusHttpMapper) { this.delegate = delegate; this.statusHttpMapper = statusHttpMapper; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusReactiveWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveStatusEndpointWebExtension.java similarity index 85% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusReactiveWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveStatusEndpointWebExtension.java index ed76c492868..09577b243bb 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusReactiveWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveStatusEndpointWebExtension.java @@ -20,22 +20,22 @@ import reactor.core.publisher.Mono; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; /** - * Reactive {@link WebEndpointExtension} for the {@link StatusEndpoint}. + * Reactive {@link EndpointWebExtension} for the {@link StatusEndpoint}. * * @author Stephane Nicoll * @since 2.0.0 */ -@WebEndpointExtension(endpoint = StatusEndpoint.class) -public class StatusReactiveWebEndpointExtension { +@EndpointWebExtension(endpoint = StatusEndpoint.class) +public class ReactiveStatusEndpointWebExtension { private final ReactiveHealthIndicator delegate; private final HealthStatusHttpMapper statusHttpMapper; - public StatusReactiveWebEndpointExtension(ReactiveHealthIndicator delegate, + public ReactiveStatusEndpointWebExtension(ReactiveHealthIndicator delegate, HealthStatusHttpMapper statusHttpMapper) { this.delegate = delegate; this.statusHttpMapper = statusHttpMapper; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpoint.java index e28715e4329..b7245761ac6 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpoint.java @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.health; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; @@ -26,7 +25,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; * @author Stephane Nicoll * @since 2.0.0 */ -@Endpoint(id = "status", defaultEnablement = DefaultEnablement.ENABLED) +@Endpoint(id = "status") public class StatusEndpoint { private final HealthIndicator healthIndicator; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusWebEndpointExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpointWebExtension.java similarity index 85% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusWebEndpointExtension.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpointWebExtension.java index f7d3789e823..5b841922486 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusWebEndpointExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusEndpointWebExtension.java @@ -18,22 +18,22 @@ package org.springframework.boot.actuate.health; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; /** - * {@link WebEndpointExtension} for the {@link StatusEndpoint}. + * {@link EndpointWebExtension} for the {@link StatusEndpoint}. * * @author Stephane Nicoll * @since 2.0.0 */ -@WebEndpointExtension(endpoint = StatusEndpoint.class) -public class StatusWebEndpointExtension { +@EndpointWebExtension(endpoint = StatusEndpoint.class) +public class StatusEndpointWebExtension { private final StatusEndpoint delegate; private final HealthStatusHttpMapper statusHttpMapper; - public StatusWebEndpointExtension(StatusEndpoint delegate, + public StatusEndpointWebExtension(StatusEndpoint delegate, HealthStatusHttpMapper statusHttpMapper) { this.delegate = delegate; this.statusHttpMapper = statusHttpMapper; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoEndpoint.java index 7a1aab8c4fb..cacbacc15b9 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoEndpoint.java @@ -19,7 +19,6 @@ package org.springframework.boot.actuate.info; import java.util.List; import java.util.Map; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.util.Assert; @@ -32,7 +31,7 @@ import org.springframework.util.Assert; * @author Stephane Nicoll * @since 2.0.0 */ -@Endpoint(id = "info", defaultEnablement = DefaultEnablement.ENABLED) +@Endpoint(id = "info") public class InfoEndpoint { private final List infoContributors; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java index 8357212efe8..d0e00c66cc3 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java @@ -21,9 +21,9 @@ import java.io.File; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; import org.springframework.boot.logging.LogFile; import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; @@ -37,7 +37,7 @@ import org.springframework.core.io.Resource; * @author Andy Wilkinson * @since 2.0.0 */ -@Endpoint(id = "logfile", exposure = EndpointExposure.WEB) +@WebEndpoint(id = "logfile") public class LogFileWebEndpoint { private static final Log logger = LogFactory.getLog(LogFileWebEndpoint.class); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java index dfd0c69ecbd..d1ab51cf73a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java @@ -36,10 +36,10 @@ import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; @@ -55,7 +55,7 @@ import org.springframework.util.ReflectionUtils; * @author Andy Wilkinson * @since 2.0.0 */ -@Endpoint(id = "heapdump", exposure = EndpointExposure.WEB) +@WebEndpoint(id = "heapdump") public class HeapDumpWebEndpoint { private final long timeout; diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java index 1d914e648e7..15d52f256f8 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java @@ -23,9 +23,9 @@ import java.io.Writer; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.common.TextFormat; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; /** * {@link Endpoint} that outputs metrics in a format that can be scraped by the Prometheus @@ -34,7 +34,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; * @author Jon Schneider * @since 2.0.0 */ -@Endpoint(id = "prometheus", exposure = EndpointExposure.WEB) +@WebEndpoint(id = "prometheus") public class PrometheusScrapeEndpoint { private final CollectorRegistry collectorRegistry; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebIntegrationTests.java index dd292d24a10..58dc634b63c 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/AuditEventsEndpointWebIntegrationTests.java @@ -99,9 +99,9 @@ public class AuditEventsEndpointWebIntegrationTests { } @Bean - public AuditEventsWebEndpointExtension auditEventsWebEndpointExtension( + public AuditEventsEndpointWebExtension auditEventsEndpointWebExtension( AuditEventsEndpoint auditEventsEndpoint) { - return new AuditEventsWebEndpointExtension(auditEventsEndpoint); + return new AuditEventsEndpointWebExtension(auditEventsEndpoint); } private AuditEvent createEvent(String instant, String principal, String type) { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscovererTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscovererTests.java index e6906867ae0..5efaf4ee2d1 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscovererTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscovererTests.java @@ -16,30 +16,39 @@ package org.springframework.boot.actuate.endpoint.annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; -import java.util.stream.Collectors; +import java.util.function.Function; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.Operation; import org.springframework.boot.actuate.endpoint.OperationInvoker; -import org.springframework.boot.actuate.endpoint.OperationType; -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.cache.CachingOperationInvokerAdvisor; +import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.AliasFor; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -127,11 +136,11 @@ public class AnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsNotCachedWithTtlSetToZero() { + Function timeToLive = (endpointId) -> 0L; load(TestEndpointConfiguration.class, (context) -> { Map> endpoints = mapEndpoints( - new TestAnnotationEndpointDiscoverer(context, - (endpointId) -> new CachingConfiguration(0)) - .discoverEndpoints()); + new TestAnnotationEndpointDiscoverer(context, timeToLive) + .discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); Map operations = mapOperations( endpoints.get("test")); @@ -143,13 +152,11 @@ public class AnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsNotCachedWithNonMatchingId() { - CachingConfigurationFactory cachingConfigurationFactory = ( - endpointId) -> (endpointId.equals("foo") ? new CachingConfiguration(500) - : new CachingConfiguration(0)); + Function timeToLive = (id) -> (id.equals("foo") ? 500L : 0L); load(TestEndpointConfiguration.class, (context) -> { Map> endpoints = mapEndpoints( - new TestAnnotationEndpointDiscoverer(context, - cachingConfigurationFactory).discoverEndpoints()); + new TestAnnotationEndpointDiscoverer(context, timeToLive) + .discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); Map operations = mapOperations( endpoints.get("test")); @@ -161,13 +168,11 @@ public class AnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsCachedWithMatchingId() { - CachingConfigurationFactory cachingConfigurationFactory = ( - endpointId) -> (endpointId.equals("test") ? new CachingConfiguration(500) - : new CachingConfiguration(0)); + Function timeToLive = (id) -> (id.equals("test") ? 500L : 0L); load(TestEndpointConfiguration.class, (context) -> { Map> endpoints = mapEndpoints( - new TestAnnotationEndpointDiscoverer(context, - cachingConfigurationFactory).discoverEndpoints()); + new TestAnnotationEndpointDiscoverer(context, timeToLive) + .discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); Map operations = mapOperations( endpoints.get("test")); @@ -187,12 +192,55 @@ public class AnnotationEndpointDiscovererTests { }); } - private Map> mapEndpoints( - Collection> endpoints) { - Map> endpointById = new LinkedHashMap<>(); + @Test + public void specializedEndpointsAreFilteredFromRegular() throws Exception { + load(TestEndpointsConfiguration.class, (context) -> { + Map> endpoints = mapEndpoints( + new TestAnnotationEndpointDiscoverer(context).discoverEndpoints()); + assertThat(endpoints).containsOnlyKeys("test"); + }); + } + + @Test + public void specializedEndpointsAreNotFilteredFromSpecialized() throws Exception { + load(TestEndpointsConfiguration.class, (context) -> { + Map> endpoints = mapEndpoints( + new SpecializedTestAnnotationEndpointDiscoverer(context) + .discoverEndpoints()); + assertThat(endpoints).containsOnlyKeys("test", "specialized"); + }); + } + + @Test + public void extensionsAreApplied() throws Exception { + load(TestEndpointsConfiguration.class, (context) -> { + Map> endpoints = mapEndpoints( + new SpecializedTestAnnotationEndpointDiscoverer(context) + .discoverEndpoints()); + Map operations = mapOperations( + endpoints.get("specialized")); + assertThat(operations).containsKeys( + ReflectionUtils.findMethod(SpecializedExtension.class, "getSpecial")); + }); + } + + @Test + public void filtersAreApplied() throws Exception { + load(TestEndpointsConfiguration.class, (context) -> { + EndpointFilter filter = (info, + discoverer) -> !(info.getId().equals("specialized")); + Map> endpoints = mapEndpoints( + new SpecializedTestAnnotationEndpointDiscoverer(context, + Collections.singleton(filter)).discoverEndpoints()); + assertThat(endpoints).containsOnlyKeys("test"); + }); + } + + private Map> mapEndpoints( + Collection> endpoints) { + Map> endpointById = new LinkedHashMap<>(); endpoints.forEach((endpoint) -> { - EndpointInfo existing = endpointById - .put(endpoint.getId(), endpoint); + EndpointInfo existing = endpointById.put(endpoint.getId(), endpoint); if (existing != null) { throw new AssertionError(String.format( "Found endpoints with duplicate id '%s'", endpoint.getId())); @@ -202,15 +250,14 @@ public class AnnotationEndpointDiscovererTests { } private Map mapOperations( - EndpointInfo endpoint) { + EndpointInfo endpoint) { Map operationByMethod = new HashMap<>(); endpoint.getOperations().forEach((operation) -> { - Operation existing = operationByMethod.put(operation.getOperationMethod(), - operation); + Method method = operation.getMethodInfo().getMethod(); + Operation existing = operationByMethod.put(method, operation); if (existing != null) { throw new AssertionError(String.format( - "Found endpoint with duplicate operation method '%s'", - operation.getOperationMethod())); + "Found endpoint with duplicate operation method '%s'", method)); } }); return operationByMethod; @@ -276,6 +323,16 @@ public class AnnotationEndpointDiscovererTests { } + @SpecializedEndpoint(id = "specialized") + static class SpecializedTestEndpoint { + + @ReadOperation + public Object getAll() { + return null; + } + + } + static class TestEndpointSubclass extends TestEndpoint { @WriteOperation @@ -305,6 +362,12 @@ public class AnnotationEndpointDiscovererTests { } + @Import({ TestEndpoint.class, SpecializedTestEndpoint.class, + SpecializedExtension.class }) + static class TestEndpointsConfiguration { + + } + @Configuration static class ClashingEndpointConfiguration { @@ -317,67 +380,116 @@ public class AnnotationEndpointDiscovererTests { public TestEndpoint testEndpointOne() { return new TestEndpoint(); } + } - private static final class TestEndpointOperation extends Operation { + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Endpoint + @FilteredEndpoint(SpecializedEndpointFilter.class) + public @interface SpecializedEndpoint { - private final Method operationMethod; + @AliasFor(annotation = Endpoint.class) + String id(); - private TestEndpointOperation(OperationType type, - OperationInvoker operationInvoker, Method operationMethod) { - super(type, operationInvoker, true); - this.operationMethod = operationMethod; - } + } - private Method getOperationMethod() { - return this.operationMethod; + @EndpointExtension(endpoint = SpecializedTestEndpoint.class, filter = SpecializedEndpointFilter.class) + public static class SpecializedExtension { + + @ReadOperation + public Object getSpecial() { + return null; } } - private static class TestAnnotationEndpointDiscoverer - extends AnnotationEndpointDiscoverer { - - TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, - CachingConfigurationFactory cachingConfigurationFactory) { - super(applicationContext, endpointOperationFactory(), - TestEndpointOperation::getOperationMethod, - cachingConfigurationFactory); - } - - TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext) { - this(applicationContext, (id) -> null); - } + static class SpecializedEndpointFilter + implements EndpointFilter { @Override - public Collection> discoverEndpoints() { - return discoverEndpoints(null, null).stream() - .map(EndpointInfoDescriptor::getEndpointInfo) - .collect(Collectors.toList()); + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return discoverer instanceof SpecializedTestAnnotationEndpointDiscoverer; } - private static EndpointOperationFactory endpointOperationFactory() { - return new EndpointOperationFactory() { + } - @Override - public TestEndpointOperation createOperation(String endpointId, - AnnotationAttributes operationAttributes, Object target, - Method operationMethod, OperationType operationType, - long timeToLive) { - return new TestEndpointOperation(operationType, - createOperationInvoker(timeToLive), operationMethod); - } + public static class TestAnnotationEndpointDiscoverer + extends AnnotationEndpointDiscoverer { - private OperationInvoker createOperationInvoker(long timeToLive) { - OperationInvoker invoker = (arguments) -> null; - if (timeToLive > 0) { - return new CachingOperationInvoker(invoker, timeToLive); - } - else { - return invoker; - } - } - }; + TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext) { + this(applicationContext, (id) -> null, null); + } + + TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, + Function timeToLive) { + this(applicationContext, timeToLive, null); + } + + TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, + Function timeToLive, + Collection> filters) { + super(applicationContext, TestEndpointOperation::new, + TestEndpointOperation::getMethod, + new ConversionServiceParameterMapper(), + Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), + filters); + } + + } + + public static class SpecializedTestAnnotationEndpointDiscoverer extends + AnnotationEndpointDiscoverer { + + SpecializedTestAnnotationEndpointDiscoverer( + ApplicationContext applicationContext) { + this(applicationContext, (id) -> null, null); + } + + SpecializedTestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, + Collection> filters) { + this(applicationContext, (id) -> null, filters); + } + + SpecializedTestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, + Function timeToLive, + Collection> filters) { + super(applicationContext, SpecializedTestEndpointOperation::new, + SpecializedTestEndpointOperation::getMethod, + new ConversionServiceParameterMapper(), + Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), + filters); + } + + } + + public static class TestEndpointOperation extends Operation { + + private final OperationMethodInfo methodInfo; + + public TestEndpointOperation(String endpointId, OperationMethodInfo methodInfo, + Object target, OperationInvoker invoker) { + super(methodInfo.getOperationType(), invoker, true); + this.methodInfo = methodInfo; + } + + public Method getMethod() { + return this.methodInfo.getMethod(); + } + + public OperationMethodInfo getMethodInfo() { + return this.methodInfo; + } + + } + + public static class SpecializedTestEndpointOperation extends TestEndpointOperation { + + public SpecializedTestEndpointOperation(String endpointId, + OperationMethodInfo methodInfo, Object target, OperationInvoker invoker) { + super(endpointId, methodInfo, target, invoker); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisorTests.java new file mode 100644 index 00000000000..fa9a118763e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/cache/CachingOperationInvokerAdvisorTests.java @@ -0,0 +1,123 @@ +/* + * 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.cache; + +import java.lang.reflect.Method; +import java.util.function.Function; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.springframework.boot.actuate.endpoint.OperationInvoker; +import org.springframework.boot.actuate.endpoint.OperationType; +import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.ReflectionUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +/** + * Tests fopr {@link CachingOperationInvokerAdvisor}. + * + * @author Phillip Webb + */ +public class CachingOperationInvokerAdvisorTests { + + @Mock + private OperationInvoker invoker; + + @Mock + private Function timeToLive; + + private CachingOperationInvokerAdvisor advisor; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + this.advisor = new CachingOperationInvokerAdvisor(this.timeToLive); + } + + @Test + public void applyWhenOperationIsNotReadShouldNotAddAdvise() throws Exception { + OperationMethodInfo info = mockInfo(OperationType.WRITE, "get"); + OperationInvoker advised = this.advisor.apply("foo", info, this.invoker); + assertThat(advised).isSameAs(this.invoker); + } + + @Test + public void applyWhenHasParametersShouldNotAddAdvise() throws Exception { + OperationMethodInfo info = mockInfo(OperationType.READ, "getWithParameter", + String.class); + OperationInvoker advised = this.advisor.apply("foo", info, this.invoker); + assertThat(advised).isSameAs(this.invoker); + } + + @Test + public void applyWhenTimeToLiveReturnsNullShouldNotAddAdvise() throws Exception { + OperationMethodInfo info = mockInfo(OperationType.READ, "get"); + given(this.timeToLive.apply(any())).willReturn(null); + OperationInvoker advised = this.advisor.apply("foo", info, this.invoker); + assertThat(advised).isSameAs(this.invoker); + verify(this.timeToLive).apply("foo"); + } + + @Test + public void applyWhenTimeToLiveIsZeroShouldNotAddAdvise() throws Exception { + OperationMethodInfo info = mockInfo(OperationType.READ, "get"); + given(this.timeToLive.apply(any())).willReturn(0L); + OperationInvoker advised = this.advisor.apply("foo", info, this.invoker); + assertThat(advised).isSameAs(this.invoker); + verify(this.timeToLive).apply("foo"); + } + + @Test + public void applyShouldAddCacheAdvise() throws Exception { + OperationMethodInfo info = mockInfo(OperationType.READ, "get"); + given(this.timeToLive.apply(any())).willReturn(100L); + OperationInvoker advised = this.advisor.apply("foo", info, this.invoker); + assertThat(advised).isInstanceOf(CachingOperationInvoker.class); + assertThat(ReflectionTestUtils.getField(advised, "target")) + .isEqualTo(this.invoker); + assertThat(ReflectionTestUtils.getField(advised, "timeToLive")).isEqualTo(100L); + } + + private OperationMethodInfo mockInfo(OperationType operationType, String methodName, + Class... parameterTypes) { + Method method = ReflectionUtils.findMethod(TestOperations.class, methodName, + parameterTypes); + return new OperationMethodInfo(method, operationType, new AnnotationAttributes()); + } + + public static class TestOperations { + + public String get() { + return ""; + } + + public String getWithParameter(String foo) { + return ""; + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceOperationParameterMapperTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapperTests.java similarity index 96% rename from spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceOperationParameterMapperTests.java rename to spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapperTests.java index b44ff10755c..b9bdf81c4d6 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceOperationParameterMapperTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/convert/ConversionServiceParameterMapperTests.java @@ -22,7 +22,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.boot.actuate.endpoint.ParameterMappingException; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.format.support.DefaultFormattingConversionService; @@ -40,7 +40,7 @@ import static org.mockito.Mockito.verify; * * @author Phillip Webb */ -public class ConversionServiceOperationParameterMapperTests { +public class ConversionServiceParameterMapperTests { @Rule public ExpectedException thrown = ExpectedException.none(); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssemblerTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssemblerTests.java index 0abc746b46d..b36857ebf18 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssemblerTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanInfoAssemblerTests.java @@ -25,7 +25,6 @@ import javax.management.MBeanParameterInfo; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.OperationType; @@ -45,11 +44,11 @@ public class EndpointMBeanInfoAssemblerTests { @Test public void exposeSimpleReadOperation() { - JmxEndpointOperation operation = new JmxEndpointOperation(OperationType.READ, + JmxOperation operation = new JmxOperation(OperationType.READ, new DummyOperationInvoker(), "getAll", Object.class, "Test operation", Collections.emptyList()); - EndpointInfo endpoint = new EndpointInfo<>("test", - DefaultEnablement.ENABLED, Collections.singletonList(operation)); + EndpointInfo endpoint = new EndpointInfo<>("test", true, + Collections.singletonList(operation)); EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler .createEndpointMBeanInfo(endpoint); assertThat(endpointMBeanInfo).isNotNull(); @@ -74,12 +73,12 @@ public class EndpointMBeanInfoAssemblerTests { @Test public void exposeSimpleWriteOperation() { - JmxEndpointOperation operation = new JmxEndpointOperation(OperationType.WRITE, + JmxOperation operation = new JmxOperation(OperationType.WRITE, new DummyOperationInvoker(), "update", Object.class, "Update operation", Collections.singletonList(new JmxEndpointOperationParameterInfo("test", String.class, "Test argument"))); - EndpointInfo endpoint = new EndpointInfo<>("another", - DefaultEnablement.ENABLED, Collections.singletonList(operation)); + EndpointInfo endpoint = new EndpointInfo<>("another", true, + Collections.singletonList(operation)); EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler .createEndpointMBeanInfo(endpoint); assertThat(endpointMBeanInfo).isNotNull(); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java index adbccbe4632..4c961eca942 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java @@ -45,7 +45,6 @@ import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxAnnotationEndpointDiscoverer; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -341,10 +340,11 @@ public class EndpointMBeanTests { Consumer consumer) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( configuration)) { - consumer.accept(new JmxAnnotationEndpointDiscoverer(context, - new ConversionServiceParameterMapper( - DefaultConversionService.getSharedInstance()), - (id) -> new CachingConfiguration(0))); + ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper( + DefaultConversionService.getSharedInstance()); + JmxAnnotationEndpointDiscoverer discoverer = new JmxAnnotationEndpointDiscoverer( + context, parameterMapper, null, null); + consumer.accept(discoverer); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscovererTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscovererTests.java index c3d44f3ab6b..6e2be1de7f9 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscovererTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/annotation/JmxAnnotationEndpointDiscovererTests.java @@ -17,28 +17,29 @@ package org.springframework.boot.actuate.endpoint.jmx.annotation; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.EndpointInfo; -import org.springframework.boot.actuate.endpoint.ReflectiveOperationInvoker; import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -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.cache.CachingOperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; -import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperation; import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperationParameterInfo; +import org.springframework.boot.actuate.endpoint.jmx.JmxOperation; +import org.springframework.boot.actuate.endpoint.reflect.ReflectiveOperationInvoker; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -68,34 +69,33 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void standardEndpointIsDiscovered() { load(TestEndpoint.class, (discoverer) -> { - Map> endpoints = discover( - discoverer); + Map> endpoints = discover(discoverer); assertThat(endpoints).containsOnlyKeys("test"); - Map operationByName = mapOperations( + Map operationByName = mapOperations( endpoints.get("test").getOperations()); assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update", "deleteSomething"); - JmxEndpointOperation getAll = operationByName.get("getAll"); + JmxOperation getAll = operationByName.get("getAll"); assertThat(getAll.getDescription()) .isEqualTo("Invoke getAll for endpoint test"); assertThat(getAll.getOutputType()).isEqualTo(Object.class); assertThat(getAll.getParameters()).isEmpty(); assertThat(getAll.getInvoker()) .isInstanceOf(ReflectiveOperationInvoker.class); - JmxEndpointOperation getSomething = operationByName.get("getSomething"); + JmxOperation getSomething = operationByName.get("getSomething"); assertThat(getSomething.getDescription()) .isEqualTo("Invoke getSomething for endpoint test"); assertThat(getSomething.getOutputType()).isEqualTo(String.class); assertThat(getSomething.getParameters()).hasSize(1); hasDefaultParameter(getSomething, 0, String.class); - JmxEndpointOperation update = operationByName.get("update"); + JmxOperation update = operationByName.get("update"); assertThat(update.getDescription()) .isEqualTo("Invoke update for endpoint test"); assertThat(update.getOutputType()).isEqualTo(Void.TYPE); assertThat(update.getParameters()).hasSize(2); hasDefaultParameter(update, 0, String.class); hasDefaultParameter(update, 1, String.class); - JmxEndpointOperation deleteSomething = operationByName.get("deleteSomething"); + JmxOperation deleteSomething = operationByName.get("deleteSomething"); assertThat(deleteSomething.getDescription()) .isEqualTo("Invoke deleteSomething for endpoint test"); assertThat(deleteSomething.getOutputType()).isEqualTo(Void.TYPE); @@ -108,8 +108,7 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void onlyJmxEndpointsAreDiscovered() { load(MultipleEndpointsConfiguration.class, (discoverer) -> { - Map> endpoints = discover( - discoverer); + Map> endpoints = discover(discoverer); assertThat(endpoints).containsOnlyKeys("test", "jmx"); }); } @@ -129,8 +128,7 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void jmxEndpointOverridesStandardEndpoint() { load(OverriddenOperationJmxEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = discover( - discoverer); + Map> endpoints = discover(discoverer); assertThat(endpoints).containsOnlyKeys("test"); assertJmxTestEndpoint(endpoints.get("test")); }); @@ -139,14 +137,13 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void jmxEndpointAddsExtraOperation() { load(AdditionalOperationJmxEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = discover( - discoverer); + Map> endpoints = discover(discoverer); assertThat(endpoints).containsOnlyKeys("test"); - Map operationByName = mapOperations( + Map operationByName = mapOperations( endpoints.get("test").getOperations()); assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update", "deleteSomething", "getAnother"); - JmxEndpointOperation getAnother = operationByName.get("getAnother"); + JmxOperation getAnother = operationByName.get("getAnother"); assertThat(getAnother.getDescription()).isEqualTo("Get another thing"); assertThat(getAnother.getOutputType()).isEqualTo(Object.class); assertThat(getAnother.getParameters()).isEmpty(); @@ -155,15 +152,14 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsCachedWithMatchingId() { - load(TestEndpoint.class, (id) -> new CachingConfiguration(500), (discoverer) -> { - Map> endpoints = discover( - discoverer); + load(TestEndpoint.class, (id) -> 500L, (discoverer) -> { + Map> endpoints = discover(discoverer); assertThat(endpoints).containsOnlyKeys("test"); - Map operationByName = mapOperations( + Map operationByName = mapOperations( endpoints.get("test").getOperations()); assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update", "deleteSomething"); - JmxEndpointOperation getAll = operationByName.get("getAll"); + JmxOperation getAll = operationByName.get("getAll"); assertThat(getAll.getInvoker()).isInstanceOf(CachingOperationInvoker.class); assertThat(((CachingOperationInvoker) getAll.getInvoker()).getTimeToLive()) .isEqualTo(500); @@ -172,21 +168,21 @@ public class JmxAnnotationEndpointDiscovererTests { @Test public void extraReadOperationsAreCached() { - load(AdditionalOperationJmxEndpointConfiguration.class, - (id) -> new CachingConfiguration(500), (discoverer) -> { - Map> endpoints = discover( + load(AdditionalOperationJmxEndpointConfiguration.class, (id) -> 500L, + (discoverer) -> { + Map> endpoints = discover( discoverer); assertThat(endpoints).containsOnlyKeys("test"); - Map operationByName = mapOperations( + Map operationByName = mapOperations( endpoints.get("test").getOperations()); assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update", "deleteSomething", "getAnother"); - JmxEndpointOperation getAll = operationByName.get("getAll"); + JmxOperation getAll = operationByName.get("getAll"); assertThat(getAll.getInvoker()) .isInstanceOf(CachingOperationInvoker.class); assertThat(((CachingOperationInvoker) getAll.getInvoker()) .getTimeToLive()).isEqualTo(500); - JmxEndpointOperation getAnother = operationByName.get("getAnother"); + JmxOperation getAnother = operationByName.get("getAnother"); assertThat(getAnother.getInvoker()) .isInstanceOf(CachingOperationInvoker.class); assertThat(((CachingOperationInvoker) getAnother.getInvoker()) @@ -245,29 +241,29 @@ public class JmxAnnotationEndpointDiscovererTests { }); } - private void assertJmxTestEndpoint(EndpointInfo endpoint) { - Map operationByName = mapOperations( + private void assertJmxTestEndpoint(EndpointInfo endpoint) { + Map operationByName = mapOperations( endpoint.getOperations()); assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update", "deleteSomething"); - JmxEndpointOperation getAll = operationByName.get("getAll"); + JmxOperation getAll = operationByName.get("getAll"); assertThat(getAll.getDescription()).isEqualTo("Get all the things"); assertThat(getAll.getOutputType()).isEqualTo(Object.class); assertThat(getAll.getParameters()).isEmpty(); - JmxEndpointOperation getSomething = operationByName.get("getSomething"); + JmxOperation getSomething = operationByName.get("getSomething"); assertThat(getSomething.getDescription()) .isEqualTo("Get something based on a timeUnit"); assertThat(getSomething.getOutputType()).isEqualTo(String.class); assertThat(getSomething.getParameters()).hasSize(1); hasDocumentedParameter(getSomething, 0, "unitMs", Long.class, "Number of milliseconds"); - JmxEndpointOperation update = operationByName.get("update"); + JmxOperation update = operationByName.get("update"); assertThat(update.getDescription()).isEqualTo("Update something based on bar"); assertThat(update.getOutputType()).isEqualTo(Void.TYPE); assertThat(update.getParameters()).hasSize(2); hasDocumentedParameter(update, 0, "foo", String.class, "Foo identifier"); hasDocumentedParameter(update, 1, "bar", String.class, "Bar value"); - JmxEndpointOperation deleteSomething = operationByName.get("deleteSomething"); + JmxOperation deleteSomething = operationByName.get("deleteSomething"); assertThat(deleteSomething.getDescription()) .isEqualTo("Delete something based on a timeUnit"); assertThat(deleteSomething.getOutputType()).isEqualTo(Void.TYPE); @@ -276,8 +272,7 @@ public class JmxAnnotationEndpointDiscovererTests { "Number of milliseconds"); } - private void hasDefaultParameter(JmxEndpointOperation operation, int index, - Class type) { + private void hasDefaultParameter(JmxOperation operation, int index, Class type) { assertThat(index).isLessThan(operation.getParameters().size()); JmxEndpointOperationParameterInfo parameter = operation.getParameters() .get(index); @@ -285,8 +280,8 @@ public class JmxAnnotationEndpointDiscovererTests { assertThat(parameter.getDescription()).isNull(); } - private void hasDocumentedParameter(JmxEndpointOperation operation, int index, - String name, Class type, String description) { + private void hasDocumentedParameter(JmxOperation operation, int index, String name, + Class type, String description) { assertThat(index).isLessThan(operation.getParameters().size()); JmxEndpointOperationParameterInfo parameter = operation.getParameters() .get(index); @@ -295,17 +290,16 @@ public class JmxAnnotationEndpointDiscovererTests { assertThat(parameter.getDescription()).isEqualTo(description); } - private Map> discover( + private Map> discover( JmxAnnotationEndpointDiscoverer discoverer) { - Map> endpointsById = new HashMap<>(); + Map> endpointsById = new HashMap<>(); discoverer.discoverEndpoints() .forEach((endpoint) -> endpointsById.put(endpoint.getId(), endpoint)); return endpointsById; } - private Map mapOperations( - Collection operations) { - Map operationByName = new HashMap<>(); + private Map mapOperations(Collection operations) { + Map operationByName = new HashMap<>(); operations.forEach((operation) -> operationByName .put(operation.getOperationName(), operation)); return operationByName; @@ -316,15 +310,17 @@ public class JmxAnnotationEndpointDiscovererTests { load(configuration, (id) -> null, consumer); } - private void load(Class configuration, - CachingConfigurationFactory cachingConfigurationFactory, + private void load(Class configuration, Function timeToLive, Consumer consumer) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( configuration)) { - consumer.accept(new JmxAnnotationEndpointDiscoverer(context, - new ConversionServiceParameterMapper( - DefaultConversionService.getSharedInstance()), - cachingConfigurationFactory)); + ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper( + DefaultConversionService.getSharedInstance()); + JmxAnnotationEndpointDiscoverer discoverer = new JmxAnnotationEndpointDiscoverer( + context, parameterMapper, + Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), + null); + consumer.accept(discoverer); } } @@ -353,7 +349,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @Endpoint(id = "jmx", exposure = EndpointExposure.JMX) + @JmxEndpoint(id = "jmx") private static class TestJmxEndpoint { @ReadOperation @@ -363,7 +359,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @JmxEndpointExtension(endpoint = TestEndpoint.class) + @EndpointJmxExtension(endpoint = TestEndpoint.class) private static class TestJmxEndpointExtension { @ManagedOperation(description = "Get all the things") @@ -399,7 +395,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @JmxEndpointExtension(endpoint = TestEndpoint.class) + @EndpointJmxExtension(endpoint = TestEndpoint.class) private static class AdditionalOperationJmxEndpointExtension { @ManagedOperation(description = "Get another thing") @@ -425,7 +421,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @JmxEndpointExtension(endpoint = TestEndpoint.class) + @EndpointJmxExtension(endpoint = TestEndpoint.class) static class ClashingOperationsJmxEndpointExtension { @ReadOperation @@ -440,7 +436,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @Endpoint(id = "nonjmx", exposure = EndpointExposure.WEB) + @WebEndpoint(id = "nonjmx") private static class NonJmxEndpoint { @ReadOperation @@ -450,7 +446,7 @@ public class JmxAnnotationEndpointDiscovererTests { } - @JmxEndpointExtension(endpoint = NonJmxEndpoint.class) + @EndpointJmxExtension(endpoint = NonJmxEndpoint.class) private static class NonJmxJmxEndpointExtension { @ReadOperation 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 743195033fa..f2e6eb3c902 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 @@ -29,14 +29,13 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.boot.actuate.endpoint.ParameterMapper; import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; +import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader; import org.springframework.context.ApplicationContext; @@ -384,8 +383,8 @@ public abstract class AbstractWebEndpointIntegrationTests new CachingConfiguration(0), - endpointMediaTypes(), EndpointPathResolver.useEndpointId()); + parameterMapper, endpointMediaTypes(), + EndpointPathResolver.useEndpointId(), null, null); } @Bean diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java index a400ebf5e92..d3b5553d184 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java @@ -23,7 +23,6 @@ import java.util.Map; import org.assertj.core.api.Condition; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationType; @@ -60,8 +59,7 @@ public class EndpointLinksResolverTests { public void resolvedLinksContainsALinkForEachEndpointOperation() { Map links = this.linksResolver .resolveLinks( - Arrays.asList(new EndpointInfo<>("alpha", - DefaultEnablement.ENABLED, + Arrays.asList(new EndpointInfo<>("alpha", true, Arrays.asList(operationWithPath("/alpha", "alpha"), operationWithPath("/alpha/{name}", "alpha-name")))), @@ -75,8 +73,8 @@ public class EndpointLinksResolverTests { linkWithHref("https://api.example.com/application/alpha/{name}")); } - private WebEndpointOperation operationWithPath(String path, String id) { - return new WebEndpointOperation(OperationType.READ, null, false, + private WebOperation operationWithPath(String path, String id) { + return new WebOperation(OperationType.READ, null, false, new OperationRequestPredicate(path, WebEndpointHttpMethod.GET, Collections.emptyList(), Collections.emptyList()), id); 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 d455d3bcd23..def3cb6fdd6 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 @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -32,7 +33,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.boot.actuate.endpoint.EndpointExposure; import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.OperationInvoker; import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; @@ -40,13 +40,13 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; -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.cache.CachingOperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper; +import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpoint; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests.BaseConfiguration; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; -import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -89,7 +89,7 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void onlyWebEndpointsAreDiscovered() { load(MultipleEndpointsConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); }); @@ -98,10 +98,10 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void oneOperationIsDiscoveredWhenExtensionOverridesOperation() { load(OverriddenOperationWebEndpointExtensionConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); - EndpointInfo endpoint = endpoints.get("test"); + EndpointInfo endpoint = endpoints.get("test"); assertThat(requestPredicates(endpoint)).has( requestPredicates(path("test").httpMethod(WebEndpointHttpMethod.GET) .consumes().produces("application/json"))); @@ -111,10 +111,10 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void twoOperationsAreDiscoveredWhenExtensionAddsOperation() { load(AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); - EndpointInfo endpoint = endpoints.get("test"); + EndpointInfo endpoint = endpoints.get("test"); assertThat(requestPredicates(endpoint)).has(requestPredicates( path("test").httpMethod(WebEndpointHttpMethod.GET).consumes() .produces("application/json"), @@ -126,10 +126,10 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void predicateForWriteOperationThatReturnsVoidHasNoProducedMediaTypes() { load(VoidWriteOperationEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("voidwrite"); - EndpointInfo endpoint = endpoints.get("voidwrite"); + EndpointInfo endpoint = endpoints.get("voidwrite"); assertThat(requestPredicates(endpoint)).has(requestPredicates( path("voidwrite").httpMethod(WebEndpointHttpMethod.POST).produces() .consumes("application/json"))); @@ -189,30 +189,27 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void endpointMainReadOperationIsCachedWithMatchingId() { - load((id) -> new CachingConfiguration(500), (id) -> id, - TestEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( - discoverer.discoverEndpoints()); - assertThat(endpoints).containsOnlyKeys("test"); - EndpointInfo endpoint = endpoints.get("test"); - assertThat(endpoint.getOperations()).hasSize(1); - OperationInvoker operationInvoker = endpoint.getOperations() - .iterator().next().getInvoker(); - assertThat(operationInvoker) - .isInstanceOf(CachingOperationInvoker.class); - assertThat( - ((CachingOperationInvoker) operationInvoker).getTimeToLive()) - .isEqualTo(500); - }); + load((id) -> 500L, (id) -> id, TestEndpointConfiguration.class, (discoverer) -> { + Map> endpoints = mapEndpoints( + discoverer.discoverEndpoints()); + assertThat(endpoints).containsOnlyKeys("test"); + EndpointInfo endpoint = endpoints.get("test"); + assertThat(endpoint.getOperations()).hasSize(1); + OperationInvoker operationInvoker = endpoint.getOperations().iterator().next() + .getInvoker(); + assertThat(operationInvoker).isInstanceOf(CachingOperationInvoker.class); + assertThat(((CachingOperationInvoker) operationInvoker).getTimeToLive()) + .isEqualTo(500); + }); } @Test public void operationsThatReturnResourceProduceApplicationOctetStream() { load(ResourceEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("resource"); - EndpointInfo endpoint = endpoints.get("resource"); + EndpointInfo endpoint = endpoints.get("resource"); assertThat(requestPredicates(endpoint)).has(requestPredicates( path("resource").httpMethod(WebEndpointHttpMethod.GET).consumes() .produces("application/octet-stream"))); @@ -222,11 +219,10 @@ public class WebAnnotationEndpointDiscovererTests { @Test public void operationCanProduceCustomMediaTypes() { load(CustomMediaTypesEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("custommediatypes"); - EndpointInfo endpoint = endpoints - .get("custommediatypes"); + EndpointInfo endpoint = endpoints.get("custommediatypes"); assertThat(requestPredicates(endpoint)).has(requestPredicates( path("custommediatypes").httpMethod(WebEndpointHttpMethod.GET) .consumes().produces("text/plain"), @@ -241,10 +237,10 @@ public class WebAnnotationEndpointDiscovererTests { public void endpointPathCanBeCustomized() { load((id) -> null, (id) -> "custom/" + id, AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> { - Map> endpoints = mapEndpoints( + Map> endpoints = mapEndpoints( discoverer.discoverEndpoints()); assertThat(endpoints).containsOnlyKeys("test"); - EndpointInfo endpoint = endpoints.get("test"); + EndpointInfo endpoint = endpoints.get("test"); Condition> expected = requestPredicates( path("custom/test").httpMethod(WebEndpointHttpMethod.GET) .consumes().produces("application/json"), @@ -259,36 +255,38 @@ public class WebAnnotationEndpointDiscovererTests { this.load((id) -> null, (id) -> id, configuration, consumer); } - private void load(CachingConfigurationFactory cachingConfigurationFactory, + private void load(Function timeToLive, EndpointPathResolver endpointPathResolver, Class configuration, Consumer consumer) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( configuration); try { - consumer.accept(new WebAnnotationEndpointDiscoverer(context, - new ConversionServiceParameterMapper( - DefaultConversionService.getSharedInstance()), - cachingConfigurationFactory, - new EndpointMediaTypes(Collections.singletonList("application/json"), - Collections.singletonList("application/json")), - endpointPathResolver)); + ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper( + DefaultConversionService.getSharedInstance()); + EndpointMediaTypes mediaTypes = new EndpointMediaTypes( + Collections.singletonList("application/json"), + Collections.singletonList("application/json")); + WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( + context, parameterMapper, mediaTypes, endpointPathResolver, + Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), + null); + consumer.accept(discoverer); } finally { context.close(); } } - private Map> mapEndpoints( - Collection> endpoints) { - Map> endpointById = new HashMap<>(); + private Map> mapEndpoints( + Collection> endpoints) { + Map> endpointById = new HashMap<>(); endpoints.forEach((endpoint) -> endpointById.put(endpoint.getId(), endpoint)); return endpointById; } private List requestPredicates( - EndpointInfo endpoint) { - return endpoint.getOperations().stream() - .map(WebEndpointOperation::getRequestPredicate) + EndpointInfo endpoint) { + return endpoint.getOperations().stream().map(WebOperation::getRequestPredicate) .collect(Collectors.toList()); } @@ -316,7 +314,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = TestEndpoint.class) + @EndpointWebExtension(endpoint = TestEndpoint.class) static class TestWebEndpointExtension { @ReadOperation @@ -350,7 +348,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = TestEndpoint.class) + @EndpointWebExtension(endpoint = TestEndpoint.class) static class OverriddenOperationWebEndpointExtension { @ReadOperation @@ -360,7 +358,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = TestEndpoint.class) + @EndpointWebExtension(endpoint = TestEndpoint.class) static class AdditionalOperationWebEndpointExtension { @ReadOperation @@ -385,7 +383,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = TestEndpoint.class) + @EndpointWebExtension(endpoint = TestEndpoint.class) static class ClashingOperationsWebEndpointExtension { @ReadOperation @@ -400,7 +398,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = TestEndpoint.class) + @EndpointWebExtension(endpoint = TestEndpoint.class) static class ClashingSelectorsWebEndpointExtension { @ReadOperation @@ -415,7 +413,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @Endpoint(id = "nonweb", exposure = EndpointExposure.JMX) + @JmxEndpoint(id = "nonweb") static class NonWebEndpoint { @ReadOperation @@ -425,7 +423,7 @@ public class WebAnnotationEndpointDiscovererTests { } - @WebEndpointExtension(endpoint = NonWebEndpoint.class) + @EndpointWebExtension(endpoint = NonWebEndpoint.class) static class NonWebWebEndpointExtension { @ReadOperation @@ -440,7 +438,6 @@ public class WebAnnotationEndpointDiscovererTests { @WriteOperation public void write(String foo, String bar) { - } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java index 70dcace1ea6..44f8eedc9f6 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java @@ -27,9 +27,10 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.servlet.ServletContainer; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.ServletRegistrationBean; @@ -81,7 +82,7 @@ public class JerseyWebEndpointIntegrationTests extends @Bean public ResourceConfig resourceConfig(Environment environment, - WebAnnotationEndpointDiscoverer endpointDiscoverer, + EndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes) { ResourceConfig resourceConfig = new ResourceConfig(); Collection resources = new JerseyEndpointResourceFactory() diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java index af37cd8989b..79fa3b5fe75 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java @@ -20,9 +20,10 @@ import java.util.Arrays; import org.junit.Test; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; @@ -112,7 +113,7 @@ public class WebFluxEndpointIntegrationTests @Bean public WebFluxEndpointHandlerMapping webEndpointHandlerMapping( Environment environment, - WebAnnotationEndpointDiscoverer endpointDiscoverer, + EndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes) { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com")); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java index 39e904bce9d..b2adad5edd6 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java @@ -20,9 +20,10 @@ import java.util.Arrays; import org.junit.Test; +import org.springframework.boot.actuate.endpoint.EndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; -import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; @@ -105,7 +106,7 @@ public class MvcWebEndpointIntegrationTests extends @Bean public WebMvcEndpointHandlerMapping webEndpointHandlerMapping( Environment environment, - WebAnnotationEndpointDiscoverer webEndpointDiscoverer, + EndpointDiscoverer webEndpointDiscoverer, EndpointMediaTypes endpointMediaTypes) { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com")); 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 4bf8c8ebde0..327f8ee19dd 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,8 +99,7 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner { mediaTypes); WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceParameterMapper(), - (id) -> null, endpointMediaTypes, - EndpointPathResolver.useEndpointId()); + endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null); 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/WebEndpointRunners.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointRunners.java index 64e109f635c..e1e84ccaaeb 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointRunners.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointRunners.java @@ -42,7 +42,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * {@link org.springframework.core.env.Environment} are reset at the end of every test. * This means that {@link TestPropertyValues} can be used in a test without affecting the * {@code Environment} of other tests in the same class. The runner always sets the flag - * `endpoints.default.web.enabled` to true so that web endpoints are enabled. + * {@code management.endpoints.web.expose} to {@code *} so that web endpoints are enabled. * * @author Andy Wilkinson * @author Phillip Webb 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 42683280d0a..e2e19b04c8a 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,8 +105,7 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner { mediaTypes); WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceParameterMapper(), - (id) -> null, endpointMediaTypes, - EndpointPathResolver.useEndpointId()); + endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null); 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 422828e1500..a0baec81972 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,8 +88,7 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner { mediaTypes); WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( this.applicationContext, new ConversionServiceParameterMapper(), - (id) -> null, endpointMediaTypes, - EndpointPathResolver.useEndpointId()); + endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null); return new WebMvcEndpointHandlerMapping(new EndpointMapping("/application"), discoverer.discoverEndpoints(), endpointMediaTypes, new CorsConfiguration()); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebIntegrationTests.java index d70d51a4232..3f3f84187d1 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointWebIntegrationTests.java @@ -147,9 +147,9 @@ public class EnvironmentEndpointWebIntegrationTests { } @Bean - public EnvironmentWebEndpointExtension webEndpointExtension( + public EnvironmentEndpointWebExtension environmentEndpointWebExtension( EnvironmentEndpoint endpoint) { - return new EnvironmentWebEndpointExtension(endpoint); + return new EnvironmentEndpointWebExtension(endpoint); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java index 61791ee8028..40e1dc8affa 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java @@ -29,7 +29,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.web.reactive.server.WebTestClient; /** - * Integration tests for {@link HealthEndpoint} and {@link HealthWebEndpointExtension} + * Integration tests for {@link HealthEndpoint} and {@link HealthEndpointWebExtension} * exposed by Jersey, Spring MVC, and WebFlux. * * @author Andy Wilkinson @@ -71,9 +71,9 @@ public class HealthEndpointWebIntegrationTests { } @Bean - public HealthWebEndpointExtension healthWebEndpointExtension( + public HealthEndpointWebExtension healthWebEndpointExtension( HealthEndpoint delegate) { - return new HealthWebEndpointExtension(delegate, new HealthStatusHttpMapper()); + return new HealthEndpointWebExtension(delegate, new HealthStatusHttpMapper()); } @Bean diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/StatusEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/StatusEndpointWebIntegrationTests.java index bf105f264f6..12ccf5bf675 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/StatusEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/StatusEndpointWebIntegrationTests.java @@ -29,7 +29,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.web.reactive.server.WebTestClient; /** - * Integration tests for {@link StatusEndpoint} and {@link StatusWebEndpointExtension} + * Integration tests for {@link StatusEndpoint} and {@link StatusEndpointWebExtension} * exposed by Jersey, Spring MVC, and WebFlux. * * @author Stephane Nicoll @@ -68,9 +68,9 @@ public class StatusEndpointWebIntegrationTests { } @Bean - public StatusWebEndpointExtension statusWebEndpointExtension( + public StatusEndpointWebExtension statusWebEndpointExtension( StatusEndpoint delegate) { - return new StatusWebEndpointExtension(delegate, new HealthStatusHttpMapper()); + return new StatusEndpointWebExtension(delegate, new HealthStatusHttpMapper()); } @Bean 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 1ce56a6e13b..bfe33e2d13f 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 @@ -1071,158 +1071,6 @@ content into your application; rather pick only the properties that you need. # ACTUATOR PROPERTIES # ---------------------------------------- - # AUDIT EVENTS ENDPOINT ({sc-spring-boot-actuator}/audit/AuditEventsEndpoint.{sc-ext}[AuditEventsEndpoint]) - endpoints.auditevents.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. - 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. - - # 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. - - # CONDITIONS REPORT ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/condition/ConditionsReportEndpoint.{sc-ext}[ConditionsReportEndpoint]) - endpoints.conditions.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. - endpoints.conditions.enabled= # Enable the conditions endpoint. - endpoints.conditions.jmx.enabled= # Expose the conditions endpoint as a JMX MBean. - endpoints.conditions.web.enabled= # Expose the conditions endpoint as a Web endpoint. - endpoints.conditions.web.path=conditions # Path of the conditions 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. - endpoints.configprops.enabled= # Enable the configprops endpoint. - 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. - endpoints.default.jmx.enabled=true # Enable all endpoints as JMX MBeans by default. - endpoints.default.web.enabled=false # Enable all endpoints as Web endpoints by default. - - # ENVIRONMENT ENDPOINT ({sc-spring-boot-actuator}/env/EnvironmentEndpoint.{sc-ext}[EnvironmentEndpoint]) - endpoints.env.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. - endpoints.env.enabled= # Enable the env endpoint. - 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. - - # SCHEDULED TASKS ENDPOINT ({sc-spring-boot-actuator}/scheduling/ScheduledTasksEndpoint.{sc-ext}[ScheduledTasksEndpoint]) - endpoints.scheduledtasks.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. - endpoints.scheduledtasks.enabled= # Enable the scheduled tasks endpoint. - endpoints.scheduledtasks.jmx.enabled= # Expose the scheduled tasks endpoint as a JMX MBean. - endpoints.scheduledtasks.web.enabled= # Expose the scheduled tasks endpoint as a Web endpoint. - endpoints.scheduledtasks.web.path=sessions # Path of the scheduled tasks 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. management.server.address= # Network address that the management endpoints should bind to. Requires a custom management.server.port. @@ -1248,22 +1096,119 @@ content into your application; rather pick only the properties that you need. management.cloudfoundry.enabled=true # Enable extended Cloud Foundry actuator endpoints. management.cloudfoundry.skip-ssl-validation=false # Skip SSL verification for Cloud Foundry actuator endpoint security calls. - # ENDPOINTS CORS CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/web/servlet/CorsEndpointProperties.{sc-ext}[CorsEndpointProperties]) - management.endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported. - management.endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers. - management.endpoints.cors.allowed-methods= # Comma-separated list of methods to allow. '*' allows all methods. When not set, defaults to GET. - management.endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled. - management.endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response. - management.endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients. - - # ENDPOINTS WEB CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/web/WebEndpointProperties.{sc-ext}[WebEndpointProperties]) - management.endpoints.web.base-path=/application # Base path for Web endpoints. Relative to server.context-path or management.server.context-path if management.server.port is configured. - # ENDPOINTS JMX CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/jmx/JmxEndpointExporterProperties.{sc-ext}[JmxEndpointExporterProperties]) + management.endpoints.jmx.enabled= # Whether JMX endpoints are enabled + management.endpoints.jmx.expose= The IDs of endpoints to expose or '*' for all (default is 'info', 'status') + management.endpoints.jmx.exclude= The IDs of endpoints to exclude management.endpoints.jmx.domain=org.springframework.boot # Endpoints JMX domain name. Fallback to 'spring.jmx.default-domain' if set. management.endpoints.jmx.static-names=false # Additional static properties to append to all ObjectNames of MBeans representing Endpoints. management.endpoints.jmx.unique-names=false # Ensure that ObjectNames are modified in case of conflict. + # ENDPOINTS WEB CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/web/WebEndpointProperties.{sc-ext}[WebEndpointProperties]) + management.endpoints.web.enabled= # Whether web endpoints are enabled + management.endpoints.web.expose= The IDs of endpoints to expose or '*' for all (default is '*') + management.endpoints.web.exclude= The IDs of endpoints to exclude + management.endpoints.web.base-path=/application # Base path for Web endpoints. Relative to server.context-path or management.server.context-path if management.server.port is configured. + management.endpoints.web.path=mapping= Map of endpoint IDs to the path that should expose them + + # ENDPOINTS CORS CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/web/servlet/CorsEndpointProperties.{sc-ext}[CorsEndpointProperties]) + management.endpoints.web.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported. + management.endpoints.web.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers. + management.endpoints.web.cors.allowed-methods= # Comma-separated list of methods to allow. '*' allows all methods. When not set, defaults to GET. + management.endpoints.web.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled. + management.endpoints.web.cors.exposed-headers= # Comma-separated list of headers to include in a response. + management.endpoints.web.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients. + + # AUDIT EVENTS ENDPOINT ({sc-spring-boot-actuator}/audit/AuditEventsEndpoint.{sc-ext}[AuditEventsEndpoint]) + management.endpoint.auditevents.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.auditevents.enabled= # Enable the auditevents endpoint. + + # BEANS ENDPOINT ({sc-spring-boot-actuator}/beans/BeansEndpoint.{sc-ext}[BeansEndpoint]) + management.endpoint.beans.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.beans.enabled= # Enable the beans endpoint. + + # CONDITIONS REPORT ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/condition/ConditionsReportEndpoint.{sc-ext}[ConditionsReportEndpoint]) + management.endpoint.conditions.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.conditions.enabled= # Enable the conditions endpoint. + + # CONFIGURATION PROPERTIES REPORT ENDPOINT ({sc-spring-boot-actuator}/context/properties/ConfigurationPropertiesReportEndpoint.{sc-ext}[ConfigurationPropertiesReportEndpoint]) + management.endpoint.configprops.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.configprops.enabled= # Enable the configprops endpoint. + management.endpoint.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. + + # ENVIRONMENT ENDPOINT ({sc-spring-boot-actuator}/env/EnvironmentEndpoint.{sc-ext}[EnvironmentEndpoint]) + management.endpoint.env.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.env.enabled= # Enable the env endpoint. + management.endpoint.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. + + # FLYWAY ENDPOINT ({sc-spring-boot-actuator}/flyway/FlywayEndpoint.{sc-ext}[FlywayEndpoint]) + management.endpoint.flyway.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.flyway.enabled= # Enable 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. + + # HEAP DUMP ENDPOINT ({sc-spring-boot-actuator}/management/HeapDumpWebEndpoint.{sc-ext}[HeapDumpWebEndpoint]) + management.endpoint.heapdump.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.heapdump.enabled= # Enable the heapdump endpoint. + + # INFO ENDPOINT ({sc-spring-boot-actuator}/info/InfoEndpoint.{sc-ext}[InfoEndpoint]) + management.endpoint.info.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.info.enabled=true # Enable the info endpoint. + + # LIQUIBASE ENDPOINT ({sc-spring-boot-actuator}/liquibase/LiquibaseEndpoint.{sc-ext}[LiquibaseEndpoint]) + management.endpoint.liquibase.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.liquibase.enabled= # Enable the liquibase endpoint. + + # LOG FILE ENDPOINT ({sc-spring-boot-actuator}/logging/LogFileWebEndpoint.{sc-ext}[LogFileWebEndpoint]) + management.endpoint.logfile.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.logfile.enabled= # Enable the logfile endpoint. + management.endpoint.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. + + # LOGGERS ENDPOINT ({sc-spring-boot-actuator}/logging/LoggersEndpoint.{sc-ext}[LoggersEndpoint]) + management.endpoint.loggers.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.loggers.enabled= # Enable the loggers endpoint. + + # REQUEST MAPPING ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/web/servlet/RequestMappingEndpoint.{sc-ext}[RequestMappingEndpoint]) + management.endpoint.mappings.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.mappings.enabled= # Enable the mappings endpoint. + + # METRICS ENDPOINT ({sc-spring-boot-actuator}/metrics/MetricsEndpoint.{sc-ext}[MetricsEndpoint]) + management.endpoint.metrics.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.metrics.enabled= # Enable the metrics endpoint. + + # PROMETHEUS ENDPOINT ({sc-spring-boot-actuator}/metrics/export/prometheus/PrometheusScrapeEndpoint.{sc-ext}[PrometheusScrapeEndpoint]) + management.endpoint.prometheus.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.prometheus.enabled= # Enable the metrics endpoint. + + # SCHEDULED TASKS ENDPOINT ({sc-spring-boot-actuator}/scheduling/ScheduledTasksEndpoint.{sc-ext}[ScheduledTasksEndpoint]) + endpoints.scheduledtasks.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + endpoints.scheduledtasks.enabled= # Enable the scheduled tasks endpoint. + endpoints.scheduledtasks.jmx.enabled= # Expose the scheduled tasks endpoint as a JMX MBean. + endpoints.scheduledtasks.web.enabled= # Expose the scheduled tasks endpoint as a Web endpoint. + endpoints.scheduledtasks.web.path=sessions # Path of the scheduled tasks endpoint. + + # SESSIONS ENDPOINT ({sc-spring-boot-actuator}/session/SessionsEndpoint.{sc-ext}[SessionsEndpoint]) + management.endpoint.sessions.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.sessions.enabled= # Enable the sessions endpoint. + + # SHUTDOWN ENDPOINT ({sc-spring-boot-actuator}/context/ShutdownEndpoint.{sc-ext}[ShutdownEndpoint]) + management.endpoint.shutdown.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.shutdown.enabled=false # Enable the shutdown endpoint. + + # STATUS ENDPOINT ({sc-spring-boot-actuator}/health/StatusEndpoint.{sc-ext}[StatusEndpoint]) + management.endpoint.status.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.status.enabled=true # Enable the status endpoint. + + # THREAD DUMP ENDPOINT ({sc-spring-boot-actuator}/management/ThreadDumpEndpoint.{sc-ext}[ThreadDumpEndpoint]) + management.endpoint.threaddump.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.threaddump.enabled= # Enable the threaddump endpoint. + + # TRACE ENDPOINT ({sc-spring-boot-actuator}/trace/TraceEndpoint.{sc-ext}[TraceEndpoint]) + management.endpoint.trace.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + management.endpoint.trace.enabled= # Enable the trace endpoint. + # HEALTH INDICATORS management.health.db.enabled=true # Enable database health check. management.health.cassandra.enabled=true # Enable cassandra health check. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 717df0c7846..d0c5904b46b 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -7,10 +7,6 @@ Spring Boot includes a number of additional features to help you monitor and man application when you push it to production. You can choose to manage and monitor your application by using HTTP endpoints or with JMX. Auditing, health, and metrics gathering can also be automatically applied to your application. - -Actuator HTTP endpoints are only available with a Spring MVC-based application. In -particular, it does not work with Jersey <> -- @@ -146,89 +142,114 @@ content. |=== -[[production-ready-endpoints-security]] -=== Securing Endpoints -By default, all HTTP endpoints are secured such that only users that have an `ACTUATOR` -role may access them. Security is enforced by using the standard -`HttpServletRequest.isUserInRole` method. -TIP: If you want to use something other than `ACTUATOR` as the role, set the -`management.security.roles` property to the value you want to use. -If you deploy applications behind a firewall, you may prefer that all your actuator -endpoints can be accessed without requiring authentication. You can do so by changing the -`management.security.enabled` property, as follows: +[[production-ready-endpoints-exposing-endpoints]] +=== Exposing Endpoints +Since Endpoints may contain sensitive information, careful consideration should be given +about when to expose them. Out of the box, Spring Boot will expose all enabled endpoints +over JMX, but only the `health` and `info` endpoints over HTTP. + +To change the endpoints that are exposed you can use the `expose` and `exclude` property +for the technology. For example, to only expose the `health` over JMX you would use: .application.properties [source,properties,indent=0] ---- - management.security.enabled=false + management.endpoints.jmx.expose=health ---- -CAUTION: By default, actuator endpoints are exposed on the same port that serves regular -HTTP traffic. Take care not to accidentally expose sensitive information if you change -the `management.security.enabled` property. - -If you deploy applications publicly, you may want to add '`Spring Security`' to handle -user authentication. When '`Spring Security`' is added, by default, '`basic`' -authentication is used. The username is`user` and the password is a random generated -password (which is printed on the console when the application starts). - -TIP: Generated passwords are logged as the application starts. To find the password in -the console, search for '`Using default security password`'. - -You can use Spring properties to change the username and password and to change the -security role(s) required to access the endpoints. For example, you might set the -following properties in your `application.properties`: +The `*` character can be used to indicate all endpoints. For example, to expose everything +over HTTP except the `env` endpoint you would use: +.application.properties [source,properties,indent=0] ---- - security.user.name=admin - security.user.password=secret - management.security.roles=SUPERUSER + management.endpoints.web.expose=* + management.endpoints.web.exclude=env ---- -If your application has custom security configuration and you want all your actuator -endpoints to be accessible without authentication, you need to explicitly configure that -in your security configuration. Also, you need to change the -`management.security.enabled` property to `false`. +NOTE: If your application is exposed publicly we strongly recommend that you also +<>. -If your custom security configuration secures your actuator endpoints, you also need to -ensure that the authenticated user has the roles specified under -`management.security.roles`. +TIP: If you want to implement your own strategy for when endpoints are exposed you can +register an `EndpointFilter` bean. -TIP: If you do not have a use case for exposing basic health information to -unauthenticated users and you have secured the actuator endpoints with custom security, -you can set `management.security.enabled` to `false`. This tells Spring Boot to skip the -additional role check. + + +[[production-ready-endpoints-security]] +=== Securing HTTP Endpoints +You should take care to secure HTTP endpoints in the same way that you would any other +sensitive URL. Spring Boot will not apply any security on your behalf, however, it does +provide some convenient `ReqestMatchers` that can be used in combination with Spring +Security. + +A typical Spring Security configuration could look something like this: + +[source,java,indent=0] +---- + @Configuration + public class ActuatorSecurity extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests() + .anyRequest().hasRole("ENDPOINT_ADMIN") + .and() + .httpBasic(); + } + + } +---- + +The above uses `EndpointRequest.toAnyEndpoint()` to match a request to any endpoint, then +ensure that thet all have the `ENDPOINT_ADMIN` role. Several other matcher methods are +also available on `EndpointRequest` (see the API documentation for details). + +If you deploy applications behind a firewall, you may prefer that all your actuator +endpoints can be accessed without requiring authentication. You can do so by changing the +`management.endpoints.web.expose` property, as follows: + +.application.properties +[source,properties,indent=0] +---- + management.endpoints.web.expose=* +---- [[production-ready-customizing-endpoints]] === Customizing Endpoints Endpoints can be customized by using Spring properties. You can change whether an -endpoint is `enabled`. +endpoint is `enabled` and the amount of time it will cache reponses. -For example, the following `application.properties` enables the `shutdown` endpoint: +For example, the following `application.properties` changes the time-to-live of the +`beans` endpoint and also enables `shutdown`: [source,properties,indent=0] ---- - endpoints.shutdown.enabled=true + management.endpoint.beans.cache.time-to-live=10 + management.endpoint.shutdown.enabled=true ---- -NOTE: The prefix ‟`endpoints` + `.` + `id`” is used to uniquely identify the endpoint -that is being configured. +NOTE: The prefix `management.endpoint.` is used to uniquely identify the +endpoint that is being configured. -By default, all endpoints except for `shutdown` are enabled. If you prefer to specifically -"`opt-in`" endpoint enablement, you can use the `endpoints.default.enabled` property. For -example, the following settings disables _all_ endpoints except for `info`: +By default, all endpoints except for `shutdown` are enabled. If you prefer to +specifically "`opt-in`" endpoint enablement, you can use the +`management.endpoints.enabled-by-default` property. For example, the following settings +disable _all_ endpoints except for `info`: [source,properties,indent=0] ---- - endpoints.default.enabled=false - endpoints.info.enabled=true + management.endpoints.enabled-by-default=flase + management.endpoint.info.enabled=true ---- +NOTE: Disabled endpoints are removed entirely from the `ApplicationContext`. If you only +want to change the technologies over which an endpoint is exposed you can use the `expose` +and `exclude` properties (see <>). + [[production-ready-endpoint-hypermedia]] @@ -244,6 +265,25 @@ disabled to prevent the possibility of a clash with other mappings. +[[production-ready-endpoint-custom-mapping]] +=== Actuator Web Endpoint Paths +By default, endpoints are exposed over HTTP under the `/application` path using ID of the +endpoint. For example, the `beans` endpoint is exposed under `/application/beans`. If you +want to map endpoints to a different path you can use the +`management.endpoints.web.path-mapping` property. You can also use +`management.endpoints.web.base-path` if you want change the base path. + +Here's an example that remaps `/application/health` to `/healthcheck`: + +.application.properties +[source,properties,indent=0] +---- + management.endpoints.web.base-path=/ + management.endpoints.path-mapping.health=healthcheck +---- + + + [[production-ready-endpoint-cors]] === CORS Support http://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing] @@ -252,13 +292,13 @@ flexible way what kind of cross domain requests are authorized. If you use Sprin Spring WebFlux, Actuator's web endpoints can be configured to support such scenarios. CORS support is disabled by default and is only enabled once the -`management.endpoints.cors.allowed-origins` property has been set. The following +`management.endpoints.web.cors.allowed-origins` property has been set. The following configuration permits `GET` and `POST` calls from the `example.com` domain: [source,properties,indent=0] ---- - management.endpoints.cors.allowed-origins=http://example.com - management.endpoints.cors.allowed-methods=GET,POST + management.endpoints.web.cors.allowed-origins=http://example.com + management.endpoints.web.cors.allowed-methods=GET,POST ---- TIP: See {sc-spring-boot-actuator-autoconfigure}/endpoint/web/servlet/CorsEndpointProperties.{sc-ext}[CorsEndpointProperties] for a complete list of options. @@ -268,14 +308,22 @@ TIP: See {sc-spring-boot-actuator-autoconfigure}/endpoint/web/servlet/CorsEndpoi [[production-ready-customizing-endpoints-programmatically]] === Adding Custom Endpoints If you add a `@Bean` annotated with `@Endpoint`, any methods annotated with -`@ReadOperation` or `@WriteOperation` are automatically exposed over JMX and, in a web -application, over HTTP as well. +`@ReadOperation`, `@WriteOperation` or `@DeleteOperaion` are automatically exposed over +JMX and, in a web application, over HTTP as well. -TIP: If you do this as a library feature, consider adding a configuration class annotated -with `@ManagementContextConfiguration` to `/META-INF/spring.factories` under the key, -`org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration`. If you -do so and if your users ask for a separate management port or address, the endpoint moves -to a child context with all the other web endpoints. +You can also write technology specific endpoints by using `@JmxEndpoint` or +`@WebEndpoint`. These endpoints are filtered to their respective technologies. For +example, `@WebEndpoint` will be exposed only over HTTP and not over JMX. + +Finally, it's possible to write technology specific extensions using +`@EndpointWebExtension` and `@EndpointJmxExtension`. These annotations allow you to +provide technology specific operations to augment an existing endpoint. + +TIP: If you add endpoints as a library feature, consider adding a configuration class +annotated with `@ManagementContextConfiguration` to `/META-INF/spring.factories` under the +key, `org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration`. If +you do so and if your users ask for a separate management port or address, the endpoint +moves to a child context with all the other web endpoints. 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 04e18760b75..1d33b434c3c 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 @@ -23,10 +23,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; @@ -42,6 +42,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; @@ -157,9 +158,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor } TypeElement endpointType = elementUtils.getTypeElement(endpointAnnotation()); if (endpointType != null) { // Is @Endpoint available - for (Element element : roundEnv.getElementsAnnotatedWith(endpointType)) { - processEndpoint(element); - } + getElementsAnnotatedOrMetaAnnotatedWith(roundEnv, endpointType) + .forEach(this::processEndpoint); } if (roundEnv.processingOver()) { try { @@ -172,6 +172,41 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor return false; } + private Map> getElementsAnnotatedOrMetaAnnotatedWith( + RoundEnvironment roundEnv, TypeElement annotation) { + DeclaredType annotationType = (DeclaredType) annotation.asType(); + Map> result = new LinkedHashMap<>(); + for (Element element : roundEnv.getRootElements()) { + LinkedList stack = new LinkedList<>(); + stack.push(element); + collectElementsAnnotatedOrMetaAnnotatedWith(annotationType, stack); + stack.removeFirst(); + if (!stack.isEmpty()) { + result.put(element, Collections.unmodifiableList(stack)); + } + } + return result; + } + + private boolean collectElementsAnnotatedOrMetaAnnotatedWith( + DeclaredType annotationType, LinkedList stack) { + Element element = stack.peekLast(); + for (AnnotationMirror annotation : this.processingEnv.getElementUtils() + .getAllAnnotationMirrors(element)) { + Element annotationElement = annotation.getAnnotationType().asElement(); + if (!stack.contains(annotationElement)) { + stack.addLast(annotationElement); + if (annotationElement.equals(annotationType.asElement())) { + return true; + } + if (!collectElementsAnnotatedOrMetaAnnotatedWith(annotationType, stack)) { + stack.removeLast(); + } + } + } + return false; + } + private void processElement(Element element) { try { AnnotationMirror annotation = getAnnotation(element, @@ -360,9 +395,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor } } - private void processEndpoint(Element element) { + private void processEndpoint(Element element, List annotations) { try { - AnnotationMirror annotation = getAnnotation(element, endpointAnnotation()); + String annotationName = this.typeUtils.getQualifiedName(annotations.get(0)); + AnnotationMirror annotation = getAnnotation(element, annotationName); if (element instanceof TypeElement) { processEndpoint(annotation, (TypeElement) element); } @@ -379,51 +415,17 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor if (endpointId == null || "".equals(endpointId)) { return; // Can't process that endpoint } - Boolean enabledByDefault = determineEnabledByDefault( - elementValues.get("defaultEnablement")); + Boolean enabledByDefault = (Boolean) elementValues.get("enableByDefault"); String type = this.typeUtils.getQualifiedName(element); this.metadataCollector .add(ItemMetadata.newGroup(endpointKey(endpointId), type, type, null)); this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId), "enabled", Boolean.class.getName(), type, null, - String.format("Enable the %s endpoint.", endpointId), enabledByDefault, - null)); + String.format("Enable the %s endpoint.", endpointId), + (enabledByDefault == null ? true : enabledByDefault), null)); this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId), "cache.time-to-live", Long.class.getName(), type, null, "Maximum time in milliseconds that a response can be cached.", 0, null)); - EndpointExposure endpointTypes = EndpointExposure - .parse(elementValues.get("exposure")); - if (endpointTypes.hasJmx()) { - this.metadataCollector.add(ItemMetadata.newProperty( - endpointKey(endpointId + ".jmx"), "enabled", Boolean.class.getName(), - type, null, - String.format("Expose the %s endpoint as a JMX MBean.", endpointId), - enabledByDefault, null)); - } - if (endpointTypes.hasWeb()) { - this.metadataCollector.add(ItemMetadata.newProperty( - endpointKey(endpointId + ".web"), "enabled", Boolean.class.getName(), - 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)); - } - } - - private Boolean determineEnabledByDefault(Object defaultEnablement) { - if (defaultEnablement != null) { - String value = String.valueOf(defaultEnablement); - if ("ENABLED".equals(value)) { - return true; - } - if ("DISABLED".equals(value)) { - return false; - } - } - return null; } private String endpointKey(String suffix) { @@ -550,46 +552,4 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor this.processingEnv.getMessager().printMessage(kind, msg); } - private static class EndpointExposure { - - private static final List ALL = Arrays.asList("JMX", "WEB"); - - private final List types; - - EndpointExposure(List types) { - this.types = types; - } - - static EndpointExposure parse(Object exposureAttribute) { - List values = asAnnotationValues(exposureAttribute); - if (values.isEmpty()) { - return new EndpointExposure(ALL); - } - return new EndpointExposure( - values.stream().map(EndpointExposure::getValueAttribute) - .collect(Collectors.toList())); - } - - @SuppressWarnings("unchecked") - private static List asAnnotationValues(Object typesAttribute) { - if (!(typesAttribute instanceof List)) { - return Collections.emptyList(); - } - return (List) typesAttribute; - } - - private static String getValueAttribute(AnnotationValue value) { - return ((VariableElement) value.getValue()).getSimpleName().toString(); - } - - public boolean hasJmx() { - return this.types.contains("JMX"); - } - - public boolean hasWeb() { - return this.types.contains("WEB"); - } - - } - } 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 579f7ef16ee..ea7200b2bc6 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 @@ -39,11 +39,9 @@ import org.springframework.boot.configurationprocessor.metadata.TestJsonConverte import org.springframework.boot.configurationsample.endpoint.CustomPropertiesEndpoint; import org.springframework.boot.configurationsample.endpoint.DisabledEndpoint; import org.springframework.boot.configurationsample.endpoint.EnabledEndpoint; -import org.springframework.boot.configurationsample.endpoint.OnlyJmxEndpoint; -import org.springframework.boot.configurationsample.endpoint.OnlyWebEndpoint; import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint; +import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint; import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint; -import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalJmxEndpoint; import org.springframework.boot.configurationsample.incremental.BarProperties; import org.springframework.boot.configurationsample.incremental.FooProperties; import org.springframework.boot.configurationsample.incremental.RenamedBarProperties; @@ -536,12 +534,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { ConfigurationMetadata metadata = compile(SimpleEndpoint.class); assertThat(metadata).has( Metadata.withGroup("endpoints.simple").fromSource(SimpleEndpoint.class)); - 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(enabledFlag("simple", true)); assertThat(metadata).has(cacheTtl("simple")); - assertThat(metadata.getItems()).hasSize(6); + assertThat(metadata.getItems()).hasSize(3); } @Test @@ -550,11 +545,8 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("endpoints.disabled") .fromSource(DisabledEndpoint.class)); 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(6); + assertThat(metadata.getItems()).hasSize(3); } @Test @@ -563,11 +555,8 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("endpoints.enabled") .fromSource(EnabledEndpoint.class)); 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(6); + assertThat(metadata.getItems()).hasSize(3); } @Test @@ -577,35 +566,19 @@ public class ConfigurationMetadataAnnotationProcessorTests { .fromSource(CustomPropertiesEndpoint.class)); assertThat(metadata).has(Metadata.withProperty("endpoints.customprops.name") .ofType(String.class).withDefaultValue("test")); - 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(enabledFlag("customprops", true)); assertThat(metadata).has(cacheTtl("customprops")); - assertThat(metadata.getItems()).hasSize(7); - } - - @Test - public void jmxOnlyEndpoint() throws IOException { - ConfigurationMetadata metadata = compile(OnlyJmxEndpoint.class); - assertThat(metadata).has( - Metadata.withGroup("endpoints.jmx").fromSource(OnlyJmxEndpoint.class)); - assertThat(metadata).has(enabledFlag("jmx", null)); - assertThat(metadata).has(jmxEnabledFlag("jmx", null)); - assertThat(metadata).has(cacheTtl("jmx")); assertThat(metadata.getItems()).hasSize(4); } @Test - public void webOnlyEndpoint() throws IOException { - ConfigurationMetadata metadata = compile(OnlyWebEndpoint.class); - assertThat(metadata).has( - 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(5); + public void specificEndpoint() throws IOException { + ConfigurationMetadata metadata = compile(SpecificEndpoint.class); + assertThat(metadata).has(Metadata.withGroup("endpoints.specific") + .fromSource(SpecificEndpoint.class)); + assertThat(metadata).has(enabledFlag("specific", true)); + assertThat(metadata).has(cacheTtl("specific")); + assertThat(metadata.getItems()).hasSize(3); } @Test @@ -615,74 +588,37 @@ public class ConfigurationMetadataAnnotationProcessorTests { ConfigurationMetadata metadata = project.fullBuild(); assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") .fromSource(IncrementalEndpoint.class)); - 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(enabledFlag("incremental", true)); assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(6); + assertThat(metadata.getItems()).hasSize(3); project.replaceText(IncrementalEndpoint.class, "id = \"incremental\"", - "id = \"incremental\", defaultEnablement = org.springframework.boot." - + "configurationsample.DefaultEnablement.DISABLED"); + "id = \"incremental\", enableByDefault = false"); metadata = project.incrementalBuild(IncrementalEndpoint.class); assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") .fromSource(IncrementalEndpoint.class)); 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(6); + assertThat(metadata.getItems()).hasSize(3); } @Test - public void incrementalEndpointBuildDisableJmxEndpoint() throws Exception { + public void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception { TestProject project = new TestProject(this.temporaryFolder, - IncrementalEndpoint.class); + SpecificEndpoint.class); ConfigurationMetadata metadata = project.fullBuild(); - assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") - .fromSource(IncrementalEndpoint.class)); - 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(6); - project.replaceText(IncrementalEndpoint.class, "id = \"incremental\"", - "id = \"incremental\", exposure = org.springframework.boot." - + "configurationsample.EndpointExposure.WEB"); - metadata = project.incrementalBuild(IncrementalEndpoint.class); - assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") - .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(5); - } - - @Test - public void incrementalEndpointBuildEnableJmxEndpoint() throws Exception { - TestProject project = new TestProject(this.temporaryFolder, - IncrementalJmxEndpoint.class); - ConfigurationMetadata metadata = project.fullBuild(); - assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") - .fromSource(IncrementalJmxEndpoint.class)); - assertThat(metadata).has(enabledFlag("incremental", null)); - assertThat(metadata).has(jmxEnabledFlag("incremental", null)); - assertThat(metadata).has(cacheTtl("incremental")); - assertThat(metadata.getItems()).hasSize(4); - project.replaceText(IncrementalJmxEndpoint.class, - ", exposure = EndpointExposure.JMX", ""); - metadata = project.incrementalBuild(IncrementalJmxEndpoint.class); - assertThat(metadata).has(Metadata.withGroup("endpoints.incremental") - .fromSource(IncrementalJmxEndpoint.class)); - 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(6); + assertThat(metadata).has(Metadata.withGroup("endpoints.specific") + .fromSource(SpecificEndpoint.class)); + assertThat(metadata).has(enabledFlag("specific", true)); + assertThat(metadata).has(cacheTtl("specific")); + assertThat(metadata.getItems()).hasSize(3); + project.replaceText(SpecificEndpoint.class, "enableByDefault = true", + "enableByDefault = false"); + metadata = project.incrementalBuild(SpecificEndpoint.class); + assertThat(metadata).has(Metadata.withGroup("endpoints.specific") + .fromSource(SpecificEndpoint.class)); + assertThat(metadata).has(enabledFlag("specific", false)); + assertThat(metadata).has(cacheTtl("specific")); + assertThat(metadata.getItems()).hasSize(3); } private Metadata.MetadataItemCondition enabledFlag(String endpointId, @@ -692,26 +628,6 @@ public class ConfigurationMetadataAnnotationProcessorTests { .withDescription(String.format("Enable the %s endpoint.", endpointId)); } - private Metadata.MetadataItemCondition jmxEnabledFlag(String endpointId, - Boolean defaultValue) { - return Metadata.withEnabledFlag("endpoints." + endpointId + ".jmx.enabled") - .withDefaultValue(defaultValue).withDescription(String - .format("Expose the %s endpoint as a JMX MBean.", endpointId)); - } - - private Metadata.MetadataItemCondition webEnabledFlag(String endpointId, - Boolean defaultValue) { - return Metadata.withEnabledFlag("endpoints." + endpointId + ".web.enabled") - .withDefaultValue(defaultValue).withDescription(String - .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( diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Endpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Endpoint.java index 056e8424879..4773320f4b7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Endpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/Endpoint.java @@ -33,10 +33,8 @@ import java.lang.annotation.Target; @Documented public @interface Endpoint { - String id(); + String id() default ""; - DefaultEnablement defaultEnablement() default DefaultEnablement.NEUTRAL; - - EndpointExposure[] exposure() default {}; + boolean enableByDefault() default true; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/DefaultEnablement.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/MetaEndpoint.java similarity index 64% rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/DefaultEnablement.java rename to spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/MetaEndpoint.java index 00ef7b1fd99..b571c668708 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/DefaultEnablement.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/MetaEndpoint.java @@ -16,8 +16,20 @@ package org.springframework.boot.configurationsample; -public enum DefaultEnablement { +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; - ENABLED, DISABLED, NEUTRAL +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Endpoint +public @interface MetaEndpoint { + + String id(); + + boolean enableByDefault() default true; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/DisabledEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/DisabledEndpoint.java index 83932f9f3c1..7f3ff911855 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/DisabledEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/DisabledEndpoint.java @@ -16,7 +16,6 @@ package org.springframework.boot.configurationsample.endpoint; -import org.springframework.boot.configurationsample.DefaultEnablement; import org.springframework.boot.configurationsample.Endpoint; /** @@ -24,7 +23,7 @@ import org.springframework.boot.configurationsample.Endpoint; * * @author Stephane Nicoll */ -@Endpoint(id = "disabled", defaultEnablement = DefaultEnablement.DISABLED) +@Endpoint(id = "disabled", enableByDefault = false) public class DisabledEndpoint { } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java index f279ed44f1c..d14e2f33138 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java @@ -16,7 +16,6 @@ package org.springframework.boot.configurationsample.endpoint; -import org.springframework.boot.configurationsample.DefaultEnablement; import org.springframework.boot.configurationsample.Endpoint; /** @@ -24,7 +23,7 @@ import org.springframework.boot.configurationsample.Endpoint; * * @author Stephane Nicoll */ -@Endpoint(id = "enabled", defaultEnablement = DefaultEnablement.ENABLED) +@Endpoint(id = "enabled") public class EnabledEndpoint { } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyWebEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyWebEndpoint.java deleted file mode 100644 index fe204f28a2f..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyWebEndpoint.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.configurationsample.endpoint; - -import org.springframework.boot.configurationsample.Endpoint; -import org.springframework.boot.configurationsample.EndpointExposure; - -/** - * An endpoints that only exposes a web endpoint. - * - * @author Stephane Nicoll - */ -@Endpoint(id = "web", exposure = EndpointExposure.WEB) -public class OnlyWebEndpoint { - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyJmxEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java similarity index 73% rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyJmxEndpoint.java rename to spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java index a44b4d05304..bb319fd62ac 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/OnlyJmxEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java @@ -16,15 +16,15 @@ package org.springframework.boot.configurationsample.endpoint; -import org.springframework.boot.configurationsample.Endpoint; -import org.springframework.boot.configurationsample.EndpointExposure; +import org.springframework.boot.configurationsample.MetaEndpoint; /** - * An endpoint that only exposes a JMX MBean. + * An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in + * Boot. * * @author Stephane Nicoll */ -@Endpoint(id = "jmx", exposure = EndpointExposure.JMX) -public class OnlyJmxEndpoint { +@MetaEndpoint(id = "specific", enableByDefault = true) +public class SpecificEndpoint { } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalJmxEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java similarity index 72% rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalJmxEndpoint.java rename to spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java index 61de43af379..02bf6338ae3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalJmxEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java @@ -16,15 +16,15 @@ package org.springframework.boot.configurationsample.endpoint.incremental; -import org.springframework.boot.configurationsample.Endpoint; -import org.springframework.boot.configurationsample.EndpointExposure; +import org.springframework.boot.configurationsample.MetaEndpoint; /** - * An endpoint that only exposes a JMX MBean. + * An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in + * Boot. * * @author Stephane Nicoll */ -@Endpoint(id = "incremental", exposure = EndpointExposure.JMX) -public class IncrementalJmxEndpoint { +@MetaEndpoint(id = "incremental") +public class IncrementalSpecificEndpoint { } diff --git a/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/main/resources/application.properties index 3d5a4c86a18..82d76b81033 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/main/resources/application.properties @@ -1 +1 @@ -endpoints.default.web.enabled=true \ No newline at end of file +management.endpoints.web.expose=* diff --git a/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/test/resources/application-cors.properties b/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/test/resources/application-cors.properties index 9c035018939..94bc394189d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/test/resources/application-cors.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-custom-security/src/test/resources/application-cors.properties @@ -1,2 +1,2 @@ -management.endpoints.cors.allowed-origins=http://localhost:8080 -management.endpoints.cors.allowed-methods=GET \ No newline at end of file +management.endpoints.web.cors.allowed-origins=http://localhost:8080 +management.endpoints.web.cors.allowed-methods=GET diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties index 30809ef62eb..5aca421a492 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties @@ -1,2 +1,2 @@ -endpoints.shutdown.enabled=true -endpoints.default.web.enabled=true \ No newline at end of file +management.endpoint.shutdown.enabled=true +management.endpoints.web.expose=* diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/resources/application.properties index 6359a789d8f..15049def9bd 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/resources/application.properties @@ -1,3 +1,3 @@ -health.diskspace.enabled=false -endpoints.default.web.enabled=true -management.jolokia.enabled=true \ No newline at end of file +management.health.diskspace.enabled=false +management.endpoints.web.expose=* +management.jolokia.enabled=true diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties index e184ef94a9e..8f5a661c8eb 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties @@ -4,8 +4,9 @@ service.name=Phil # logging.level.org.springframework.security=DEBUG management.server.address=127.0.0.1 -endpoints.default.web.enabled=true -endpoints.shutdown.enabled=true +management.endpoints.web.expose=* +management.endpoint.shutdown.enabled=true + server.tomcat.basedir=target/tomcat server.tomcat.accesslog.enabled=true server.tomcat.accesslog.pattern=%h %t "%r" %s %b diff --git a/spring-boot-samples/spring-boot-sample-web-method-security/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-web-method-security/src/main/resources/application.properties index 93d1879c7e6..3d580fcd11d 100644 --- a/spring-boot-samples/spring-boot-sample-web-method-security/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-web-method-security/src/main/resources/application.properties @@ -1,3 +1,3 @@ spring.thymeleaf.cache: false logging.level.org.springframework.security: INFO -endpoints.default.web.enabled=true \ No newline at end of file +management.endpoints.web.expose=*