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 4b36b16cbc..756de19036 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 e3f3b01629..d7bcf4a941 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 14c753f46d..ffa8ef73fd 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 49b469ed10..c048d7872b 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 a4d90783d8..b50e1759f4 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 59bf78203c..8eb4684ba3 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 603f5447f4..0000000000 --- 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 62dfe4e264..41e554d40a 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 840aae0d80..0000000000 --- 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 432b9f1aba..0000000000 --- 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 0000000000..e3700044d7 --- /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 7b546794fe..0000000000 --- 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 0000000000..a5139e0cec --- /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 8ed4359f6c..52664b9d58 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 bb1166215e..f58be42cfe 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); - } - } - - 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; + + metadata.getDeclaringClassName() + "." + metadata.getMethodName(), + ex); } - // 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 03d8411c51..daa794a2d9 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 78c64f9cee..0bbea34ef1 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 397eaefc70..4f479451aa 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 c44792068f..60ab87bf4f 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 99c13a4f59..1965297f2c 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 Stream> getEndpoints() { + return this.endpointDiscoverer.discoverEndpoints().stream(); } - private String getPath(EndpointInfo endpointInfo) { + 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 66ff39d9d5..03ea2bb785 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 0000000000..1fd830d89f --- /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 fb97a4a341..129423a672 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 fab0195f34..7ef32c6707 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 d8034449b9..d09729664b 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 ec1c5412f7..b00a574d7a 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 984cc265b0..35d3a3b80b 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 c460d01349..a378d46b23 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 0d0e0282c0..5993489539 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 3a49056136..8d6a7d705f 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 37b19e9a4d..ba520a499e 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 0ebb85b0bf..095235a85c 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 28d4828238..06a21e76b1 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 - }, - { - "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 + "description": "Enable or disable all endpoints by default." }, { - "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 930d73f44d..98ec8c726f 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 b78d91a030..309e77c325 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 620e3b5c2f..3fa11fb8c4 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 bc05eef7f7..dc4332e91a 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 a34cb91340..9775745cfb 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 9d77d3e16c..1dd27bb16d 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 4947400382..04faa3bf5e 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 95a3eb29c9..a95ceb9fc8 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 71a7916970..665207a1cf 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 c7e3701171..f57788cb1f 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 8485f86326..0000000000 --- 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 fcc5018739..8e033bd102 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 0000000000..fc04aeb727 --- /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 8c3ccea6f8..aac943b227 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") - .run((context) -> assertThat(context).doesNotHaveBean("foo")); - } - - @Test - public void enabledOverrideViaSpecificProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.foo.enabled=true") - .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") + 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 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")); + 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 enabledOverrideViaGeneralAnyProperty() { - this.contextRunner.withUserConfiguration(FooConfig.class) - .withPropertyValues("endpoints.default.enabled=false", - "endpoints.default.web.enabled=false", - "endpoints.default.jmx.enabled=true") + public void outcomeWhenNoPropertiesAndAnnotationIsEnabledByDefaultShouldMatch() + throws Exception { + this.contextRunner + .withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class) .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")); - } - - @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); - } - - @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.web.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); - } - - @Test - public void disabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.default.jmx.enabled=true") - .run((context) -> assertThat(context).doesNotHaveBean("bar")); - } - - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); - } - - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.web.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); - } - - @Test - public void enabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() { - this.contextRunner.withUserConfiguration(BarConfig.class) - .withPropertyValues("endpoints.bar.jmx.enabled=true") - .run((context) -> assertThat(context).hasBean("bar")); - } - - @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")); + public void outcomeWhenNoPropertiesAndExtensionAnnotationIsEnabledByDefaultShouldMatch() + throws Exception { + this.contextRunner + .withUserConfiguration( + FooEndpointAndExtensionEnabledByDefaultTrueConfiguration.class) + .run((context) -> assertThat(context).hasBean("foo").hasBean("fooExt")); } @Test - public void enabledOnlyWebByDefault() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.default.web.enabled=true") - .run((context) -> assertThat(context).hasBean("onlyweb")); + public void outcomeWhenNoPropertiesAndExtensionAnnotationIsNotEnabledByDefaultShouldNotMatch() + throws Exception { + this.contextRunner + .withUserConfiguration( + FooEndpointAndExtensionEnabledByDefaultFalseConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean("foo") + .doesNotHaveBean("fooExt")); } - @Test - public void disabledOnlyWebViaEndpointProperty() { - this.contextRunner.withUserConfiguration(OnlyWebConfig.class) - .withPropertyValues("endpoints.onlyweb.enabled=false") - .run((context) -> assertThat(context).doesNotHaveBean("onlyweb")); - } + @Endpoint(id = "foo", enableByDefault = true) + static class FooEndpointEnabledByDefaultTrue { - @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")); - } + @Endpoint(id = "foo", enableByDefault = false) + static class FooEndpointEnabledByDefaultFalse { - @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")); - } + @EndpointExtension(endpoint = FooEndpointEnabledByDefaultTrue.class, filter = TestFilter.class) + static class FooEndpointExtensionEnabledByDefaultTrue { - @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()); - } + @EndpointExtension(endpoint = FooEndpointEnabledByDefaultFalse.class, filter = TestFilter.class) + static class FooEndpointExtensionEnabledByDefaultFalse { - @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)); - } + static class TestFilter implements EndpointFilter { - @Test - public void jmxExtensionWithEnabledByDefaultEndpoint() { - this.contextRunner.withUserConfiguration(FooJmxExtensionConfig.class) - .run((context) -> assertThat(context) - .hasSingleBean(FooJmxEndpointExtension.class)); - } + @Override + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return true; + } - @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 { + static class FooEndpointEnabledByDefaultTrueConfiguration { @Bean @ConditionalOnEnabledEndpoint - public FooEndpoint foo() { - return new FooEndpoint(); + public FooEndpointEnabledByDefaultTrue foo() { + return new FooEndpointEnabledByDefaultTrue(); } } - @Endpoint(id = "foo") - static class FooEndpoint { - - } - @Configuration - static class BarConfig { + static class FooEndpointEnabledByDefaultFalseConfiguration { @Bean @ConditionalOnEnabledEndpoint - public BarEndpoint bar() { - return new BarEndpoint(); + public FooEndpointEnabledByDefaultFalse foo() { + return new FooEndpointEnabledByDefaultFalse(); } } - @Endpoint(id = "bar", exposure = { EndpointExposure.WEB, - EndpointExposure.JMX }, defaultEnablement = DefaultEnablement.DISABLED) - static class BarEndpoint { - - } - @Configuration - static class OnlyWebConfig { + static class FooEndpointAndExtensionEnabledByDefaultTrueConfiguration { - @Bean(name = "onlyweb") + @Bean @ConditionalOnEnabledEndpoint - public OnlyWebEndpoint onlyWeb() { - return new OnlyWebEndpoint(); + public FooEndpointEnabledByDefaultTrue foo() { + return new FooEndpointEnabledByDefaultTrue(); } - } - - @Endpoint(id = "onlyweb", exposure = EndpointExposure.WEB) - static class OnlyWebEndpoint { - - } - - @Configuration - static class NonEndpointBeanConfig { - @Bean @ConditionalOnEnabledEndpoint - public String foo() { - return "endpoint type cannot be detected"; + 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 86b34f36c1..e401c1db47 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 6a691a2f6a..881af3bfa0 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 3e2c10fbaa..45a58aec42 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 dcfb3a7377..4488a8a6a1 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 2f5921b530..9e4a175212 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 0d92ffb7c1..c7a7f48bd7 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 da09d4a086..74e6eee624 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 2d4e918d43..3e97ec6950 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 1f285025af..a8f700061c 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 809f0bf235..ef3fa568ca 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 9a732a0a13..6e8d9afd7a 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 a33d9ff43e..fd7710dc9e 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 ac4da1a7c2..5eb7bbb9a5 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 dd3e6cb33f..bd4e150a62 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 077723972f..8d85b05f4c 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 66cbd4d1b0..e0cbcefb77 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 5d98f702e0..aa06270d71 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 4c05330a08..1232be5a72 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 f4c6420286..10ec134c96 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 0ff4b03328..d0414e9f0e 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 31e1d84dae..369ed28029 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 1183afce93..af55ebebfa 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 73baf06014..8939c2081e 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 92016058f9..e959678421 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 8b6b364812..dbf34d71b7 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 3cde1d36df..f20e9c80d0 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 d6db27d368..ee529b9434 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 c5ab87fe00..d89ee7b5ef 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 70beac4740..0000000000 --- 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 d8ca91b696..a7fb1d8f19 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 caf34ca71a..90336068bf 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 1561215068..aac6a05b41 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 cfc90d0502..c51666e6cf 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 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 EndpointInfo getEndpointInfo(Map, EndpointInfo> endpoints, - Class beanType, Class endpointClass) { - EndpointInfo endpoint = endpoints.get(endpointClass); + 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); - } - return new EndpointInfoDescriptor<>(info, operations); + private boolean isEndpointExposed(Class endpointType, + EndpointInfo endpointInfo) { + if (isEndpointFiltered(endpointInfo)) { + return false; + } + 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); - }); - return result; + private boolean isExtensionExposed(Class extensionType, + EndpointInfo endpointInfo) { + AnnotationAttributes annotationAttributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(extensionType, EndpointExtension.class); + Class filterClass = annotationAttributes.getClass("filter"); + return isFilterMatch(filterClass, endpointInfo); } - private boolean isExposedOver(AnnotationAttributes attributes, - EndpointExposure exposure) { - if (exposure == null) { - return true; + @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); } - EndpointExposure[] supported = (EndpointExposure[]) attributes.get("exposure"); - return ObjectUtils.isEmpty(supported) - || ObjectUtils.containsElement(supported, exposure); + return false; } - 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; + private boolean isFilterMatch(EndpointFilter filter, + EndpointInfo endpointInfo) { + try { + return filter.match(endpointInfo, this); } - operation = createWriteOperationIfPossible(endpointId, beanName, method); - if (operation != null) { - return operation; + 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; + } } - 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 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 T createDeleteOperationIfPossible(String endpointId, String beanName, - Method method) { - return createOperationIfPossible(endpointId, beanName, method, - DeleteOperation.class, OperationType.DELETE); + /** + * Allows subclasses to verify that the descriptors are correctly configured. + * @param exposedEndpoints the discovered endpoints to verify before exposing + */ + protected void verify(Collection exposedEndpoints) { } - 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)); - } + /** + * A discovered endpoint (which may not be valid and might not ultimately be exposed). + */ + protected final class DiscoveredEndpoint { + + private final EndpointInfo info; - 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(); + 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); } - return 0; - } - /** - * An {@code EndpointOperationFactory} creates an {@link Operation} for an operation - * on an endpoint. - * - * @param the {@link Operation} type - */ - @FunctionalInterface - protected interface EndpointOperationFactory { + 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; + } - /** - * Describes a tech-specific extension of an endpoint. - * @param the type of the operation - */ - private static final class EndpointExtensionInfo { + /** + * 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; + } - private final Class extensionType; + /** + * 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 final Collection operations; + 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 EndpointExtensionInfo(Class extensionType, Collection operations) { - this.extensionType = extensionType; - this.operations = 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 Class getExtensionType() { - return this.extensionType; + private Map flatten(Map> operations) { + Map flattened = new LinkedHashMap<>(); + operations.forEach((operationKey, value) -> flattened + .put(operationKey.getKey(), getLastValue(value))); + return Collections.unmodifiableMap(flattened); } - private Collection getOperations() { - return this.operations; + 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 an {@link EndpointInfo endpoint} and whether or not it is valid. - * - * @param the type of the operation - * @param the type of the operation key + * A discovered extension. */ - protected static class EndpointInfoDescriptor { + protected final class DiscoveredExtension { - private final EndpointInfo endpointInfo; + private final Class extensionType; - private final Map, List> operations; + private final Collection operations; - protected EndpointInfoDescriptor(EndpointInfo endpointInfo, - Map, List> operations) { - this.endpointInfo = endpointInfo; + private DiscoveredExtension(Class extensionType, Collection operations) { + this.extensionType = extensionType; this.operations = operations; } - public EndpointInfo getEndpointInfo() { - return this.endpointInfo; + public Class getExtensionType() { + return this.extensionType; } - public Map, List> findDuplicateOperations() { - Map, List> duplicateOperations = new HashMap<>(); - this.operations.forEach((k, list) -> { - if (list.size() > 1) { - duplicateOperations.put(k, list); - } - }); - return duplicateOperations; + public Collection getOperations() { + return this.operations; + } + + @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 9c60dad786..6faca1d2a3 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 2b36b0a411..66fcdffcc1 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(); - - /** - * Defines the {@link EndpointExposure technologies} over which the endpoint should be - * exposed. By default, all technologies are supported. - * @return the supported endpoint exposure technologies - */ - EndpointExposure[] exposure() default {}; + String id() default ""; /** - * Defines the {@link DefaultEnablement} of the endpoint. By default, the endpoint's - * enablement defaults to the "default" settings. - * @return the default enablement + * If the endpoint should be enabled or disabled by default. + * @return {@code true} if the endpoint is enabled by default */ - 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 0000000000..c78d9506e0 --- /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 0000000000..4f816148be --- /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 0000000000..46db9d0922 --- /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 0000000000..ab317a3ce5 --- /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 d4688f2ef2..c1fb2027cd 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 0fe850965d..c29c323de6 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 0021b2f7bd..0000000000 --- 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 0000000000..91d72c0950 --- /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 437225af91..3b44790b83 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 9119c0795e..ab0fea59df 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 feed7ec1e9..b189c8fca5 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 d0379420d7..ea9f54ceca 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 e4f884e9e2..250e12cd48 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 446127c62e..aabf44c992 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 8ff93938d4..652e27ed2a 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 abeea74946..466c0c27b9 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 0000000000..def482bdcd --- /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 0000000000..d5519aa793 --- /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 0000000000..81e625914a --- /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 03610f7d71..ecf691cce6 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 0000000000..1a17c08968 --- /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 c664eb6f61..3a92637d87 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 5dfcd667a8..be0fea7808 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 f3dc19e104..0cc45e12f0 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 a49af1a7d3..3f8d14a9bf 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 efa13dcfa0..d3294d767b 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 1a82c18932..f4536f7a99 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 395c1d1f28..e3dde0783d 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 6834bd9eb1..ba66c76251 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 c16f07de39..a5257e5b26 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 d09202261f..c8e89e86ff 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 773140a77b..55627354c6 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 0000000000..2c19a331e7 --- /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 0000000000..071027c98f --- /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 0000000000..3d4d77b253 --- /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 73c7e519be..20b46656a6 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 3289a25c30..51f379a796 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 809d6efe23..e33c25a7f4 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 745cd3357b..c4e32069ea 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 1d8bdca6fa..46b6c61055 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 e57707a705..9d9f26cb11 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 dea8d8f3ab..5c4e35bd2a 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 788691ec87..2d54eb11d4 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 ed76c49286..09577b243b 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 e28715e432..b7245761ac 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 f7d3789e82..5b84192248 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 7a1aab8c4f..cacbacc15b 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 8357212efe..d0e00c66cc 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 dfd0c69ecb..d1ab51cf73 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 1d914e648e..15d52f256f 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 dd292d24a1..58dc634b63 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 e6906867ae..5efaf4ee2d 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; + } + + @EndpointExtension(endpoint = SpecializedTestEndpoint.class, filter = SpecializedEndpointFilter.class) + public static class SpecializedExtension { + + @ReadOperation + public Object getSpecial() { + return null; } - private Method getOperationMethod() { - return this.operationMethod; + } + + static class SpecializedEndpointFilter + implements EndpointFilter { + + @Override + public boolean match(EndpointInfo info, + EndpointDiscoverer discoverer) { + return discoverer instanceof SpecializedTestAnnotationEndpointDiscoverer; } } - private static class TestAnnotationEndpointDiscoverer - extends AnnotationEndpointDiscoverer { + public static class TestAnnotationEndpointDiscoverer + extends AnnotationEndpointDiscoverer { + + TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext) { + this(applicationContext, (id) -> null, null); + } TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, - CachingConfigurationFactory cachingConfigurationFactory) { - super(applicationContext, endpointOperationFactory(), - TestEndpointOperation::getOperationMethod, - cachingConfigurationFactory); + Function timeToLive) { + this(applicationContext, timeToLive, null); } - TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext) { - this(applicationContext, (id) -> null); + TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext, + Function timeToLive, + Collection> filters) { + super(applicationContext, TestEndpointOperation::new, + TestEndpointOperation::getMethod, + new ConversionServiceParameterMapper(), + Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), + filters); } - @Override - public Collection> discoverEndpoints() { - return discoverEndpoints(null, null).stream() - .map(EndpointInfoDescriptor::getEndpointInfo) - .collect(Collectors.toList()); + } + + public static class SpecializedTestAnnotationEndpointDiscoverer extends + AnnotationEndpointDiscoverer { + + SpecializedTestAnnotationEndpointDiscoverer( + ApplicationContext applicationContext) { + this(applicationContext, (id) -> null, null); } - 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); - } - - private OperationInvoker createOperationInvoker(long timeToLive) { - OperationInvoker invoker = (arguments) -> null; - if (timeToLive > 0) { - return new CachingOperationInvoker(invoker, timeToLive); - } - else { - return invoker; - } - } - }; + 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 0000000000..fa9a118763 --- /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 b44ff10755..b9bdf81c4d 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 0abc746b46..b36857ebf1 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 adbccbe463..4c961eca94 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 c3d44f3ab6..6e2be1de7f 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 743195033f..f2e6eb3c90 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 a400ebf5e9..d3b5553d18 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 d455d3bcd2..def3cb6fdd 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 70dcace1ea..44f8eedc9f 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 af37cd8989..79fa3b5fe7 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 39e904bce9..b2adad5edd 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 4bf8c8ebde..327f8ee19d 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 64e109f635..e1e84ccaae 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 42683280d0..e2e19b04c8 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 422828e150..a0baec8197 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 d70d51a423..3f3f84187d 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 61791ee802..40e1dc8aff 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 bf105f264f..12ccf5bf67 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 1ce56a6e13..bfe33e2d13 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,115 +1071,116 @@ content into your application; rather pick only the properties that you need. # ACTUATOR PROPERTIES # ---------------------------------------- + # 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. + management.server.context-path= # Management endpoint context-path. For instance `/actuator`. Requires a custom management.server.port + management.server.port= # Management endpoint HTTP port. Uses the same port as the application by default. Configure a different port to use management-specific SSL. + management.server.ssl.ciphers= # Supported SSL ciphers. Requires a custom management.port. + management.server.ssl.client-auth= # Whether client authentication is wanted ("want") or needed ("need"). Requires a trust store. Requires a custom management.server.port. + management.server.ssl.enabled= # Enable SSL support. Requires a custom management.server.port. + management.server.ssl.enabled-protocols= # Enabled SSL protocols. Requires a custom management.server.port. + management.server.ssl.key-alias= # Alias that identifies the key in the key store. Requires a custom management.server.port. + management.server.ssl.key-password= # Password used to access the key in the key store. Requires a custom management.server.port. + management.server.ssl.key-store= # Path to the key store that holds the SSL certificate (typically a jks file). Requires a custom management.server.port. + management.server.ssl.key-store-password= # Password used to access the key store. Requires a custom management.server.port. + management.server.ssl.key-store-provider= # Provider for the key store. Requires a custom management.server.port. + management.server.ssl.key-store-type= # Type of the key store. Requires a custom management.server.port. + management.server.ssl.protocol=TLS # SSL protocol to use. Requires a custom management.server.port. + management.server.ssl.trust-store= # Trust store that holds SSL certificates. Requires a custom management.server.port. + management.server.ssl.trust-store-password= # Password used to access the trust store. Requires a custom management.server.port. + management.server.ssl.trust-store-provider= # Provider for the trust store. Requires a custom management.server.port. + management.server.ssl.trust-store-type= # Type of the trust store. Requires a custom management.server.port. + + # CLOUDFOUNDRY + 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 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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. - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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. @@ -1189,80 +1190,24 @@ content into your application; rather pick only the properties that you need. 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. + 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]) - 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. - management.server.context-path= # Management endpoint context-path. For instance `/actuator`. Requires a custom management.server.port - management.server.port= # Management endpoint HTTP port. Uses the same port as the application by default. Configure a different port to use management-specific SSL. - management.server.ssl.ciphers= # Supported SSL ciphers. Requires a custom management.port. - management.server.ssl.client-auth= # Whether client authentication is wanted ("want") or needed ("need"). Requires a trust store. Requires a custom management.server.port. - management.server.ssl.enabled= # Enable SSL support. Requires a custom management.server.port. - management.server.ssl.enabled-protocols= # Enabled SSL protocols. Requires a custom management.server.port. - management.server.ssl.key-alias= # Alias that identifies the key in the key store. Requires a custom management.server.port. - management.server.ssl.key-password= # Password used to access the key in the key store. Requires a custom management.server.port. - management.server.ssl.key-store= # Path to the key store that holds the SSL certificate (typically a jks file). Requires a custom management.server.port. - management.server.ssl.key-store-password= # Password used to access the key store. Requires a custom management.server.port. - management.server.ssl.key-store-provider= # Provider for the key store. Requires a custom management.server.port. - management.server.ssl.key-store-type= # Type of the key store. Requires a custom management.server.port. - management.server.ssl.protocol=TLS # SSL protocol to use. Requires a custom management.server.port. - management.server.ssl.trust-store= # Trust store that holds SSL certificates. Requires a custom management.server.port. - management.server.ssl.trust-store-password= # Password used to access the trust store. Requires a custom management.server.port. - management.server.ssl.trust-store-provider= # Provider for the trust store. Requires a custom management.server.port. - management.server.ssl.trust-store-type= # Type of the trust store. Requires a custom management.server.port. - - # CLOUDFOUNDRY - 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.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. + 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. 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 717df0c784..d0c5904b46 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. +The `*` character can be used to indicate all endpoints. For example, to expose everything +over HTTP except the `env` endpoint you would use: -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). +.application.properties +[source,properties,indent=0] +---- + management.endpoints.web.expose=* + management.endpoints.web.exclude=env +---- -TIP: Generated passwords are logged as the application starts. To find the password in -the console, search for '`Using default security password`'. +NOTE: If your application is exposed publicly we strongly recommend that you also +<>. -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`: +TIP: If you want to implement your own strategy for when endpoints are exposed you can +register an `EndpointFilter` bean. -[source,properties,indent=0] + + +[[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] ---- - security.user.name=admin - security.user.password=secret - management.security.roles=SUPERUSER + @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(); + } + + } ---- -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`. +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 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`. +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: -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. +.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. - -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. +`@ReadOperation`, `@WriteOperation` or `@DeleteOperaion` are automatically exposed over +JMX and, in a web application, over HTTP as well. + +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 04e18760b7..1d33b434c3 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 579f7ef16e..ea7200b2bc 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); - } - - @Test - public void incrementalEndpointBuildDisableJmxEndpoint() throws Exception { - TestProject project = new TestProject(this.temporaryFolder, - IncrementalEndpoint.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); + assertThat(metadata.getItems()).hasSize(3); } @Test - public void incrementalEndpointBuildEnableJmxEndpoint() throws Exception { + public void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception { TestProject project = new TestProject(this.temporaryFolder, - IncrementalJmxEndpoint.class); + SpecificEndpoint.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 056e842487..4773320f4b 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 00ef7b1fd9..b571c66870 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 83932f9f3c..7f3ff91185 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 f279ed44f1..d14e2f3313 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 fe204f28a2..0000000000 --- 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 a44b4d0530..bb319fd62a 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 61de43af37..02bf6338ae 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 3d5a4c86a1..82d76b8103 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 9c03501893..94bc394189 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 30809ef62e..5aca421a49 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 6359a789d8..15049def9b 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 e184ef94a9..8f5a661c8e 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 93d1879c7e..3d580fcd11 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=*