Separate endpoint concerns
Update endpoint code to provide cleaner separation of concerns. Specifically, the top level endpoint package is no longer aware of the fact that JMX and HTTP are ultimately used to expose endpoints. Caching concerns have also been abstracted behind a general purpose `OperationMethodInvokerAdvisor` interface. Configuration properties have been refined to further enforce separation. The `management.endpoint.<name>` prefix provides configuration for a single endpoint (including enable and cache time-to-live). These properties are now technology agnostic (they don't include `web` or `jmx` sub properties). The `management.endpoints.<technology>` prefix provide exposure specific configuration. For example, `management.endpoints.web.path-mapping` allow endpoint URLs to be changed. Endpoint enabled/disabled logic has been simplified so that endpoints can't be disabled per exposure technology. Instead a filter based approach is used to allow refinement of what endpoints are exposed over a given technology. Fixes gh-10176
This commit is contained in:
parent
d24709c696
commit
fd5c43cdc9
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<EndpointInfo<WebEndpointOperation>> webEndpoints,
|
||||
Collection<EndpointInfo<WebOperation>> webEndpoints,
|
||||
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
|
||||
ReactiveCloudFoundrySecurityInterceptor securityInterceptor) {
|
||||
super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<EndpointInfo<WebEndpointOperation>> webEndpoints,
|
||||
Collection<EndpointInfo<WebOperation>> 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),
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String> 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<WebEndpointOperation> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, Long> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <T> the endpoint operation type
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class EndpointProvider<T extends Operation> {
|
||||
|
||||
private final EndpointDiscoverer<T> 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<T> discoverer,
|
||||
EndpointExposure exposure) {
|
||||
this.discoverer = discoverer;
|
||||
this.endpointEnablementProvider = new EndpointEnablementProvider(environment);
|
||||
this.exposure = exposure;
|
||||
}
|
||||
|
||||
public Collection<EndpointInfo<T>> 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <T> The operation type
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class ExposeExcludePropertyEndpointFilter<T extends Operation>
|
||||
implements EndpointFilter<T> {
|
||||
|
||||
private final Class<? extends EndpointDiscoverer<T>> discovererType;
|
||||
|
||||
private final Set<String> expose;
|
||||
|
||||
private final Set<String> exclude;
|
||||
|
||||
private final Set<String> exposeDefaults;
|
||||
|
||||
public ExposeExcludePropertyEndpointFilter(
|
||||
Class<? extends EndpointDiscoverer<T>> 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<? extends EndpointDiscoverer<T>> discovererType,
|
||||
Collection<String> expose, Collection<String> 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<String> bind(Binder binder, String name) {
|
||||
return asSet(binder.bind(name, Bindable.listOf(String.class))
|
||||
.orElseGet(ArrayList::new));
|
||||
}
|
||||
|
||||
private Set<String> asSet(Collection<String> items) {
|
||||
if (items == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return items.stream().map(String::toLowerCase)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(EndpointInfo<T> info, EndpointDiscoverer<T> discoverer) {
|
||||
if (this.discovererType.isInstance(discoverer)) {
|
||||
return isExposed(info) && !isExcluded(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isExposed(EndpointInfo<T> 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<T> info) {
|
||||
if (this.exclude.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.exclude.contains("*") || contains(this.exclude, info);
|
||||
}
|
||||
|
||||
private boolean contains(Set<String> items, EndpointInfo<T> info) {
|
||||
return items.contains(info.getId().toLowerCase());
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* When an endpoint uses {@link DefaultEnablement#DISABLED}, it will only be enabled if
|
||||
* {@code endpoint.<name>.enabled}, {@code endpoint.<name>.jmx.enabled} or
|
||||
* {@code endpoint.<name>.web.enabled} is {@code true}.
|
||||
* <p>
|
||||
* When an endpoint uses {@link DefaultEnablement#ENABLED}, it will be enabled unless
|
||||
* {@code endpoint.<name>.enabled}, {@code endpoint.<name>.jmx.enabled} or
|
||||
* {@code endpoint.<name>.web.enabled} is {@code false}.
|
||||
* <p>
|
||||
* 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.<name>.enabled}, {@code endpoint.<name>.jmx.enabled} or
|
||||
* {@code endpoint.<name>.web.enabled} has not been set to {@code false}.
|
||||
* <p>
|
||||
* If any properties are set, they are evaluated from most to least specific, e.g.
|
||||
* considering a web endpoint with id {@code foo}:
|
||||
* <ol>
|
||||
* <li>endpoints.foo.web.enabled</li>
|
||||
* <li>endpoints.foo.enabled</li>
|
||||
* <li>endpoints.default.web.enabled</li>
|
||||
* <li>endpoints.default.enabled</li>
|
||||
* </ol>
|
||||
* For instance if {@code endpoints.default.enabled} is {@code false} but
|
||||
* {@code endpoints.<name>.enabled} is {@code true}, the condition will match.
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyAutoConfiguration {
|
||||
*
|
||||
* @ConditionalOnEnabledEndpoint
|
||||
* @Bean
|
||||
* public MyEndpoint myEndpoint() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @Endpoint(id = "my", defaultEnablement = DefaultEnablement.DISABLED)
|
||||
* static class MyEndpoint { ... }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -16,45 +16,62 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointEnablement;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointEnablementProvider;
|
||||
import org.springframework.boot.actuate.endpoint.DefaultEnablement;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointExposure;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A condition that checks if an endpoint is enabled.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnEnabledEndpoint
|
||||
*/
|
||||
class OnEnabledEndpointCondition extends SpringBootCondition {
|
||||
|
||||
private static final String ENABLED_BY_DEFAULT_KEY = "management.endpoints.enabled-by-default";
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
EndpointAttributes attributes = getEndpointAttributes(context, metadata);
|
||||
EndpointEnablement endpointEnablement = attributes
|
||||
.getEnablement(new EndpointEnablementProvider(context.getEnvironment()));
|
||||
return new ConditionOutcome(endpointEnablement.isEnabled(),
|
||||
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
|
||||
.because(endpointEnablement.getReason()));
|
||||
AnnotationAttributes attributes = getEndpointAttributes(context, metadata);
|
||||
String id = attributes.getString("id");
|
||||
String key = "management.endpoint." + id + ".enabled";
|
||||
Boolean userDefinedEnabled = context.getEnvironment().getProperty(key,
|
||||
Boolean.class);
|
||||
if (userDefinedEnabled != null) {
|
||||
return new ConditionOutcome(userDefinedEnabled,
|
||||
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
|
||||
.because("found property " + key + " with value "
|
||||
+ userDefinedEnabled));
|
||||
}
|
||||
Boolean userDefinedDefault = context.getEnvironment()
|
||||
.getProperty(ENABLED_BY_DEFAULT_KEY, Boolean.class);
|
||||
if (userDefinedDefault != null) {
|
||||
return new ConditionOutcome(userDefinedDefault,
|
||||
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
|
||||
.because("no property " + key
|
||||
+ " found so using user defined default from "
|
||||
+ ENABLED_BY_DEFAULT_KEY));
|
||||
}
|
||||
boolean endpointDefault = attributes.getBoolean("enableByDefault");
|
||||
return new ConditionOutcome(endpointDefault,
|
||||
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class).because(
|
||||
"no property " + key + " found so using endpoint default"));
|
||||
}
|
||||
|
||||
private EndpointAttributes getEndpointAttributes(ConditionContext context,
|
||||
private AnnotationAttributes getEndpointAttributes(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Assert.state(
|
||||
metadata instanceof MethodMetadata
|
||||
|
@ -63,77 +80,35 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
|
|||
return getEndpointAttributes(context, (MethodMetadata) metadata);
|
||||
}
|
||||
|
||||
private EndpointAttributes getEndpointAttributes(ConditionContext context,
|
||||
MethodMetadata methodMetadata) {
|
||||
private AnnotationAttributes getEndpointAttributes(ConditionContext context,
|
||||
MethodMetadata metadata) {
|
||||
// We should be safe to load at this point since we are in the REGISTER_BEAN phase
|
||||
try {
|
||||
// We should be safe to load at this point since we are in the
|
||||
// REGISTER_BEAN phase
|
||||
Class<?> returnType = ClassUtils.forName(methodMetadata.getReturnTypeName(),
|
||||
Class<?> returnType = ClassUtils.forName(metadata.getReturnTypeName(),
|
||||
context.getClassLoader());
|
||||
return extractEndpointAttributes(returnType);
|
||||
return getEndpointAttributes(returnType);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to extract endpoint id for "
|
||||
+ methodMetadata.getDeclaringClassName() + "."
|
||||
+ methodMetadata.getMethodName(), ex);
|
||||
+ metadata.getDeclaringClassName() + "." + metadata.getMethodName(),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected EndpointAttributes extractEndpointAttributes(Class<?> type) {
|
||||
EndpointAttributes attributes = extractEndpointAttributesFromEndpoint(type);
|
||||
if (attributes != null) {
|
||||
return attributes;
|
||||
}
|
||||
JmxEndpointExtension jmxExtension = AnnotationUtils.findAnnotation(type,
|
||||
JmxEndpointExtension.class);
|
||||
if (jmxExtension != null) {
|
||||
return extractEndpointAttributes(jmxExtension.endpoint());
|
||||
}
|
||||
WebEndpointExtension webExtension = AnnotationUtils.findAnnotation(type,
|
||||
WebEndpointExtension.class);
|
||||
if (webExtension != null) {
|
||||
return extractEndpointAttributes(webExtension.endpoint());
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods that return"
|
||||
+ " @Endpoint, @JmxEndpointExtension, or @WebEndpointExtension");
|
||||
}
|
||||
|
||||
private EndpointAttributes extractEndpointAttributesFromEndpoint(
|
||||
Class<?> endpointClass) {
|
||||
Endpoint endpoint = AnnotationUtils.findAnnotation(endpointClass, Endpoint.class);
|
||||
if (endpoint == null) {
|
||||
return null;
|
||||
}
|
||||
// If both types are set, all exposure technologies are exposed
|
||||
EndpointExposure[] exposures = endpoint.exposure();
|
||||
return new EndpointAttributes(endpoint.id(), endpoint.defaultEnablement(),
|
||||
(exposures.length == 1 ? exposures[0] : null));
|
||||
}
|
||||
|
||||
private static class EndpointAttributes {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final DefaultEnablement defaultEnablement;
|
||||
|
||||
private final EndpointExposure exposure;
|
||||
|
||||
EndpointAttributes(String id, DefaultEnablement defaultEnablement,
|
||||
EndpointExposure exposure) {
|
||||
if (!StringUtils.hasText(id)) {
|
||||
throw new IllegalStateException("Endpoint id could not be determined");
|
||||
protected AnnotationAttributes getEndpointAttributes(Class<?> type) {
|
||||
AnnotationAttributes attributes = AnnotatedElementUtils
|
||||
.findMergedAnnotationAttributes(type, Endpoint.class, true, true);
|
||||
if (attributes == null) {
|
||||
attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(type,
|
||||
EndpointExtension.class, false, true);
|
||||
if (attributes != null) {
|
||||
return getEndpointAttributes(attributes.getClass("endpoint"));
|
||||
}
|
||||
this.id = id;
|
||||
this.defaultEnablement = defaultEnablement;
|
||||
this.exposure = exposure;
|
||||
}
|
||||
|
||||
public EndpointEnablement getEnablement(EndpointEnablementProvider provider) {
|
||||
return provider.getEndpointEnablement(this.id, this.defaultEnablement,
|
||||
this.exposure);
|
||||
}
|
||||
|
||||
Assert.state(attributes != null,
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods that "
|
||||
+ "return an @Endpoint or and @EndpointExtension");
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<EndpointFilter<JmxOperation>> 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> objectMapper) {
|
||||
EndpointProvider<JmxEndpointOperation> 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<JmxOperation> jmxIncludeExcludePropertyEndpointFilter() {
|
||||
return new ExposeExcludePropertyEndpointFilter<>(
|
||||
JmxAnnotationEndpointDiscoverer.class, this.properties.getExpose(),
|
||||
this.properties.getExclude(), "*");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<JmxEndpointOperation> endpointProvider;
|
||||
private final JmxAnnotationEndpointDiscoverer endpointDiscoverer;
|
||||
|
||||
private final EndpointMBeanRegistrar endpointMBeanRegistrar;
|
||||
|
||||
|
@ -52,9 +53,9 @@ class JmxEndpointExporter implements InitializingBean, DisposableBean {
|
|||
|
||||
private Collection<ObjectName> registeredObjectNames;
|
||||
|
||||
JmxEndpointExporter(EndpointProvider<JmxEndpointOperation> 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<ObjectName> registerEndpointMBeans() {
|
||||
Collection<EndpointInfo<JmxOperation>> 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<ObjectName> registerEndpointMBeans() {
|
||||
List<ObjectName> objectNames = new ArrayList<>();
|
||||
Collection<EndpointMBean> mBeans = this.mBeanFactory
|
||||
.createMBeans(this.endpointProvider.getEndpoints());
|
||||
for (EndpointMBean mBean : mBeans) {
|
||||
objectNames.add(this.endpointMBeanRegistrar.registerEndpointMBean(mBean));
|
||||
}
|
||||
return objectNames;
|
||||
}
|
||||
|
||||
private void unregisterEndpointMBeans(Collection<ObjectName> objectNames) {
|
||||
objectNames.forEach(this.endpointMBeanRegistrar::unregisterEndpointMbean);
|
||||
|
||||
|
|
|
@ -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<String> expose = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* The IDs of endpoints that should be excluded.
|
||||
*/
|
||||
private Set<String> 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<String> getExpose() {
|
||||
return this.expose;
|
||||
}
|
||||
|
||||
public void setExpose(Set<String> expose) {
|
||||
this.expose = expose;
|
||||
}
|
||||
|
||||
public Set<String> getExclude() {
|
||||
return this.exclude;
|
||||
}
|
||||
|
||||
public void setExclude(Set<String> exclude) {
|
||||
this.exclude = exclude;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return this.domain;
|
||||
}
|
|
@ -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<EndpointInfo<WebEndpointOperation>> endpoints;
|
||||
|
||||
private final String basePath;
|
||||
|
||||
public DefaultEndpointPathProvider(EndpointProvider<WebEndpointOperation> provider,
|
||||
private final EndpointDiscoverer<WebOperation> endpointDiscoverer;
|
||||
|
||||
public DefaultEndpointPathProvider(
|
||||
EndpointDiscoverer<WebOperation> endpointDiscoverer,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
this.endpoints = provider.getEndpoints();
|
||||
this.endpointDiscoverer = endpointDiscoverer;
|
||||
this.basePath = webEndpointProperties.getBasePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPaths() {
|
||||
return this.endpoints.stream().map(this::getPath).collect(Collectors.toList());
|
||||
return getEndpoints().map(this::getPath).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath(String id) {
|
||||
Assert.notNull(id, "ID must not be null");
|
||||
return this.endpoints.stream().filter((info) -> id.equals(info.getId()))
|
||||
.findFirst().map(this::getPath).orElse(null);
|
||||
return getEndpoints().filter((info) -> id.equals(info.getId())).findFirst()
|
||||
.map(this::getPath).orElse(null);
|
||||
}
|
||||
|
||||
private String getPath(EndpointInfo<WebEndpointOperation> endpointInfo) {
|
||||
private Stream<EndpointInfo<WebOperation>> getEndpoints() {
|
||||
return this.endpointDiscoverer.discoverEndpoints().stream();
|
||||
}
|
||||
|
||||
private String getPath(EndpointInfo<WebOperation> endpointInfo) {
|
||||
return this.basePath + "/" + endpointInfo.getId();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, String> pathMapping;
|
||||
|
||||
DefaultEndpointPathResolver(Environment environment) {
|
||||
this.environment = environment;
|
||||
DefaultEndpointPathResolver(Map<String, String> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String> 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<OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<EndpointFilter<WebOperation>> 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<WebOperation> endpointDiscoverer,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new DefaultEndpointPathProvider(endpointDiscoverer, webEndpointProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExposeExcludePropertyEndpointFilter<WebOperation> webIncludeExcludePropertyEndpointFilter() {
|
||||
return new ExposeExcludePropertyEndpointFilter<>(
|
||||
WebAnnotationEndpointDiscoverer.class, this.properties.getExpose(),
|
||||
this.properties.getExclude(), "info", "status");
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String> expose = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* The IDs of endpoints that should be excluded.
|
||||
*/
|
||||
private Set<String> exclude = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Mapping between endpoint IDs and the path that should expose them.
|
||||
*/
|
||||
private Map<String, String> 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<String> getExpose() {
|
||||
return this.expose;
|
||||
}
|
||||
|
||||
public void setExpose(Set<String> expose) {
|
||||
this.expose = expose;
|
||||
}
|
||||
|
||||
public Set<String> getExclude() {
|
||||
return this.exclude;
|
||||
}
|
||||
|
||||
public void setExclude(Set<String> exclude) {
|
||||
this.exclude = exclude;
|
||||
}
|
||||
|
||||
public Map<String, String> getPathMapping() {
|
||||
return this.pathMapping;
|
||||
}
|
||||
|
||||
public void setPathMapping(Map<String, String> pathMapping) {
|
||||
this.pathMapping = pathMapping;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<WebEndpointOperation> 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<WebEndpointOperation> provider,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, webEndpointProperties);
|
||||
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<WebEndpointOperation> provider,
|
||||
WebAnnotationEndpointDiscoverer endpointDiscoverer,
|
||||
EndpointMediaTypes endpointMediaTypes,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new WebFluxEndpointHandlerMapping(
|
||||
new EndpointMapping(webEndpointProperties.getBasePath()),
|
||||
provider.getEndpoints(), endpointMediaTypes);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EndpointPathProvider endpointPathProvider(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, webEndpointProperties);
|
||||
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<WebEndpointOperation> 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<WebEndpointOperation> provider,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, webEndpointProperties);
|
||||
}
|
||||
|
||||
private CorsConfiguration getCorsConfiguration(CorsEndpointProperties properties) {
|
||||
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
|
||||
return null;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{"properties": [
|
||||
{
|
||||
"name": "endpoints.configprops.keys-to-sanitize",
|
||||
"name": "management.endpoint.configprops.keys-to-sanitize",
|
||||
"defaultValue": [
|
||||
"password",
|
||||
"secret",
|
||||
|
@ -11,25 +11,12 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "endpoints.default.enabled",
|
||||
"name": "management.endpoints.enabled-by-default",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable all endpoints by default.",
|
||||
"defaultValue": true
|
||||
"description": "Enable or disable all endpoints by default."
|
||||
},
|
||||
{
|
||||
"name": "endpoints.default.jmx.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable all endpoints as JMX MBeans by default.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "endpoints.default.web.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable all endpoints as Web endpoints by default.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "endpoints.env.keys-to-sanitize",
|
||||
"name": "management.endpoint.env.keys-to-sanitize",
|
||||
"defaultValue": [
|
||||
"password",
|
||||
"secret",
|
||||
|
@ -40,7 +27,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "endpoints.trace.filter.enabled",
|
||||
"name": "management.endpoint.trace.filter.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable the trace servlet filter.",
|
||||
"defaultValue": true,
|
||||
|
@ -321,7 +308,7 @@
|
|||
"type": "java.lang.Boolean",
|
||||
"description": "Set whether credentials are supported. When not set, credentials are not supported.",
|
||||
"deprecation": {
|
||||
"replacement": "management.endpoints.cors.allow-credentials",
|
||||
"replacement": "management.endpoints.web.cors.allow-credentials",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
|
@ -330,7 +317,7 @@
|
|||
"type": "java.util.List<java.lang.String>",
|
||||
"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<java.lang.String>",
|
||||
"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<java.lang.String>",
|
||||
"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<java.lang.String>",
|
||||
"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": "*"
|
||||
|
|
|
@ -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,\
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<WebOperation> 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
|
||||
|
|
|
@ -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<EndpointInfo<WebEndpointOperation>> endpoints = (List<EndpointInfo<WebEndpointOperation>>) handlerMapping
|
||||
List<EndpointInfo<WebOperation>> endpoints = (List<EndpointInfo<WebOperation>>) handlerMapping
|
||||
.getEndpoints();
|
||||
assertThat(endpoints.size()).isEqualTo(1);
|
||||
assertThat(endpoints.get(0).getId()).isEqualTo("test");
|
||||
List<String> 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<EndpointInfo<WebEndpointOperation>> endpoints = (List<EndpointInfo<WebEndpointOperation>>) handlerMapping
|
||||
List<EndpointInfo<WebOperation>> endpoints = (List<EndpointInfo<WebOperation>>) 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<WebOperation> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<EndpointInfo<WebEndpointOperation>> endpoints = (List<EndpointInfo<WebEndpointOperation>>) handlerMapping
|
||||
List<EndpointInfo<WebOperation>> endpoints = (List<EndpointInfo<WebOperation>>) 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<EndpointInfo<WebEndpointOperation>> endpoints = (List<EndpointInfo<WebEndpointOperation>>) handlerMapping
|
||||
List<EndpointInfo<WebOperation>> endpoints = (List<EndpointInfo<WebOperation>>) 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<WebOperation> endpoint = endpoints.stream()
|
||||
.filter((candidate) -> "test".equals(candidate.getId())).findFirst()
|
||||
.get();
|
||||
Collection<WebOperation> operations = endpoint.getOperations();
|
||||
assertThat(operations).hasSize(1);
|
||||
assertThat(operations.iterator().next().getRequestPredicate().getPath())
|
||||
.isEqualTo("test");
|
||||
}
|
||||
|
||||
private CloudFoundryWebEndpointServletHandlerMapping getHandlerMapping() {
|
||||
|
|
|
@ -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<WebOperation> 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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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("******", "******"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, Long> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Operation> 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<Operation>(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<Operation>(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<Operation> info = new EndpointInfo<>(id, true,
|
||||
Collections.emptyList());
|
||||
return this.filter.match(info, this.discoverer);
|
||||
}
|
||||
|
||||
private abstract static class TestEndpointDiscoverer
|
||||
implements EndpointDiscoverer<Operation> {
|
||||
|
||||
}
|
||||
|
||||
private abstract static class DifferentTestEndpointDiscoverer
|
||||
implements EndpointDiscoverer<Operation> {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -18,11 +18,12 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.DefaultEnablement;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointExposure;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -40,324 +41,158 @@ public class ConditionalOnEnabledEndpointTests {
|
|||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void enabledByDefault() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
public void outcomeWhenEndpointEnabledPropertyIsTrueShouldMatch() throws Exception {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=true")
|
||||
.withUserConfiguration(
|
||||
FooEndpointEnabledByDefaultFalseConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledViaSpecificProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false")
|
||||
public void outcomeWhenEndpointEnabledPropertyIsFalseShouldNotMatch()
|
||||
throws Exception {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=false")
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledViaGeneralProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false")
|
||||
public void outcomeWhenNoEndpointPropertyAndUserDefinedDefaultIsTrueShouldMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.endpoints.enabled-by-default=true")
|
||||
.withUserConfiguration(
|
||||
FooEndpointEnabledByDefaultFalseConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeWhenNoEndpointPropertyAndUserDefinedDefaultIsFalseShouldNotMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.endpoints.enabled-by-default=false")
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaSpecificProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.foo.enabled=true")
|
||||
public void outcomeWhenNoPropertiesAndAnnotationIsEnabledByDefaultShouldMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrueConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaSpecificWebProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false",
|
||||
"endpoints.foo.web.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaSpecificJmxProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false",
|
||||
"endpoints.foo.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaSpecificAnyProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false",
|
||||
"endpoints.foo.web.enabled=false",
|
||||
"endpoints.foo.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaGeneralWebProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.default.web.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaGeneralJmxProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.default.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideViaGeneralAnyProperty() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.default.web.enabled=false",
|
||||
"endpoints.default.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledEvenWithEnabledGeneralProperties() {
|
||||
this.contextRunner.withUserConfiguration(FooConfig.class).withPropertyValues(
|
||||
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
|
||||
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false")
|
||||
public void outcomeWhenNoPropertiesAndAnnotationIsNotEnabledByDefaultShouldNotMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(
|
||||
FooEndpointEnabledByDefaultFalseConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledByDefaultWithAnnotationFlag() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||
public void outcomeWhenNoPropertiesAndExtensionAnnotationIsEnabledByDefaultShouldMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(
|
||||
FooEndpointAndExtensionEnabledByDefaultTrueConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("foo").hasBean("fooExt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=true")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||
public void outcomeWhenNoPropertiesAndExtensionAnnotationIsNotEnabledByDefaultShouldNotMatch()
|
||||
throws Exception {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(
|
||||
FooEndpointAndExtensionEnabledByDefaultFalseConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("foo")
|
||||
.doesNotHaveBean("fooExt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.default.web.enabled=true")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||
@Endpoint(id = "foo", enableByDefault = true)
|
||||
static class FooEndpointEnabledByDefaultTrue {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.default.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||
@Endpoint(id = "foo", enableByDefault = false)
|
||||
static class FooEndpointEnabledByDefaultFalse {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideWithAndAnnotationFlagAndSpecificProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.bar.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("bar"));
|
||||
@EndpointExtension(endpoint = FooEndpointEnabledByDefaultTrue.class, filter = TestFilter.class)
|
||||
static class FooEndpointExtensionEnabledByDefaultTrue {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.bar.web.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("bar"));
|
||||
@EndpointExtension(endpoint = FooEndpointEnabledByDefaultFalse.class, filter = TestFilter.class)
|
||||
static class FooEndpointExtensionEnabledByDefaultFalse {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.bar.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("bar"));
|
||||
}
|
||||
static class TestFilter implements EndpointFilter<Operation> {
|
||||
|
||||
@Test
|
||||
public void enabledOverrideWithAndAnnotationFlagAndAnyProperty() {
|
||||
this.contextRunner.withUserConfiguration(BarConfig.class)
|
||||
.withPropertyValues("endpoints.bar.web.enabled=false",
|
||||
"endpoints.bar.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabledOnlyWebByDefault() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.default.web.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledOnlyWebViaEndpointProperty() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.onlyweb.enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledOnlyWebViaSpecificTechProperty() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.onlyweb.web.enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableOverridesOnlyWebViaGeneralJmxPropertyHasNoEffect() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.default.jmx.enabled=true")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableOverridesOnlyWebViaSpecificJmxPropertyHasNoEffect() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.onlyweb.jmx.enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableOverridesOnlyWebViaSpecificWebProperty() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class)
|
||||
.withPropertyValues("endpoints.default.enabled=false",
|
||||
"endpoints.onlyweb.web.enabled=true")
|
||||
.run((context) -> assertThat(context).hasBean("onlyweb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledOnlyWebEvenWithEnabledGeneralProperties() {
|
||||
this.contextRunner.withUserConfiguration(OnlyWebConfig.class).withPropertyValues(
|
||||
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
|
||||
"endpoints.onlyweb.enabled=true", "endpoints.onlyweb.web.enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextFailIfEndpointTypeIsNotDetected() {
|
||||
this.contextRunner.withUserConfiguration(NonEndpointBeanConfig.class)
|
||||
.run((context) -> assertThat(context).hasFailed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webExtensionWithEnabledByDefaultEndpoint() {
|
||||
this.contextRunner.withUserConfiguration(FooWebExtensionConfig.class)
|
||||
.run((context) -> assertThat(context)
|
||||
.hasSingleBean(FooWebEndpointExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webExtensionWithEnabledByDefaultEndpointCanBeDisabled() {
|
||||
this.contextRunner.withUserConfiguration(FooWebExtensionConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false")
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(FooWebEndpointExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxExtensionWithEnabledByDefaultEndpoint() {
|
||||
this.contextRunner.withUserConfiguration(FooJmxExtensionConfig.class)
|
||||
.run((context) -> assertThat(context)
|
||||
.hasSingleBean(FooJmxEndpointExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxExtensionWithEnabledByDefaultEndpointCanBeDisabled() {
|
||||
this.contextRunner.withUserConfiguration(FooJmxExtensionConfig.class)
|
||||
.withPropertyValues("endpoints.foo.enabled=false")
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(FooJmxEndpointExtension.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FooConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public FooEndpoint foo() {
|
||||
return new FooEndpoint();
|
||||
@Override
|
||||
public boolean match(EndpointInfo<Operation> info,
|
||||
EndpointDiscoverer<Operation> discoverer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "foo")
|
||||
static class FooEndpoint {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BarConfig {
|
||||
static class FooEndpointEnabledByDefaultTrueConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public BarEndpoint bar() {
|
||||
return new BarEndpoint();
|
||||
public FooEndpointEnabledByDefaultTrue foo() {
|
||||
return new FooEndpointEnabledByDefaultTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "bar", exposure = { EndpointExposure.WEB,
|
||||
EndpointExposure.JMX }, defaultEnablement = DefaultEnablement.DISABLED)
|
||||
static class BarEndpoint {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OnlyWebConfig {
|
||||
static class FooEndpointEnabledByDefaultFalseConfiguration {
|
||||
|
||||
@Bean(name = "onlyweb")
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public OnlyWebEndpoint onlyWeb() {
|
||||
return new OnlyWebEndpoint();
|
||||
public FooEndpointEnabledByDefaultFalse foo() {
|
||||
return new FooEndpointEnabledByDefaultFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "onlyweb", exposure = EndpointExposure.WEB)
|
||||
static class OnlyWebEndpoint {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class NonEndpointBeanConfig {
|
||||
static class FooEndpointAndExtensionEnabledByDefaultTrueConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public String foo() {
|
||||
return "endpoint type cannot be detected";
|
||||
public FooEndpointEnabledByDefaultTrue foo() {
|
||||
return new FooEndpointEnabledByDefaultTrue();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public FooEndpointExtensionEnabledByDefaultTrue fooExt() {
|
||||
return new FooEndpointExtensionEnabledByDefaultTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JmxEndpointExtension(endpoint = FooEndpoint.class)
|
||||
static class FooJmxEndpointExtension {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FooJmxExtensionConfig {
|
||||
static class FooEndpointAndExtensionEnabledByDefaultFalseConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
FooJmxEndpointExtension fooJmxEndpointExtension() {
|
||||
return new FooJmxEndpointExtension();
|
||||
public FooEndpointEnabledByDefaultFalse foo() {
|
||||
return new FooEndpointEnabledByDefaultFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WebEndpointExtension(endpoint = FooEndpoint.class)
|
||||
static class FooWebEndpointExtension {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FooWebExtensionConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
FooWebEndpointExtension fooJmxEndpointExtension() {
|
||||
return new FooWebEndpointExtension();
|
||||
public FooEndpointExtensionEnabledByDefaultFalse fooExt() {
|
||||
return new FooEndpointExtensionEnabledByDefaultFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<WebEndpointOperation> endpointProvider;
|
||||
private EndpointDiscoverer<WebOperation> discoverer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
@ -81,16 +80,13 @@ public class DefaultEndpointPathProviderTests {
|
|||
}
|
||||
|
||||
private DefaultEndpointPathProvider createProvider(String contextPath) {
|
||||
Collection<EndpointInfo<WebEndpointOperation>> 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<EndpointInfo<WebOperation>> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String, Integer> 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<String, Integer> statusMappings = mapper.getStatusMapping();
|
||||
|
|
|
@ -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<String, Integer> 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<String, Integer> statusMappings = mapper.getStatusMapping();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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<String, String> NO_CONTEXT_MESSAGE = Collections
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <T> the type of the endpoint's operations
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public enum DefaultEnablement {
|
||||
@FunctionalInterface
|
||||
public interface EndpointFilter<T extends Operation> {
|
||||
|
||||
/**
|
||||
* 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<T> info, EndpointDiscoverer<T> discoverer);
|
||||
|
||||
}
|
|
@ -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<T extends Operation> {
|
|||
|
||||
private final String id;
|
||||
|
||||
private final DefaultEnablement defaultEnablement;
|
||||
private final boolean enableByDefault;
|
||||
|
||||
private final Collection<T> operations;
|
||||
|
||||
|
@ -37,14 +40,15 @@ public class EndpointInfo<T extends Operation> {
|
|||
* 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<T> operations) {
|
||||
public EndpointInfo(String id, boolean enableByDefault, Collection<T> 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<T extends Operation> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <T> the type of the operation
|
||||
* @param <K> the type of the operation key
|
||||
* @param <T> the type of the operation
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class AnnotationEndpointDiscoverer<T extends Operation, K>
|
||||
public abstract class AnnotationEndpointDiscoverer<K, T extends Operation>
|
||||
implements EndpointDiscoverer<T> {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final EndpointOperationFactory<T> operationFactory;
|
||||
|
||||
private final Function<T, K> operationKeyFactory;
|
||||
|
||||
private final CachingConfigurationFactory cachingConfigurationFactory;
|
||||
private final OperationsFactory<T> operationsFactory;
|
||||
|
||||
private final List<EndpointFilter<T>> 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<T> operationFactory,
|
||||
Function<T, K> operationKeyFactory,
|
||||
CachingConfigurationFactory cachingConfigurationFactory) {
|
||||
OperationFactory<T> operationFactory, Function<T, K> operationKeyFactory,
|
||||
ParameterMapper parameterMapper,
|
||||
Collection<? extends OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<? extends EndpointFilter<T>> 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<EndpointInfo<T>> discoverEndpoints() {
|
||||
Class<T> operationType = getOperationType();
|
||||
Map<Class<?>, DiscoveredEndpoint> endpoints = getEndpoints(operationType);
|
||||
Map<Class<?>, DiscoveredExtension> extensions = getExtensions(operationType,
|
||||
endpoints);
|
||||
Collection<DiscoveredEndpoint> 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 <T>}".
|
||||
* @return the operation type
|
||||
*/
|
||||
protected Collection<EndpointInfoDescriptor<T, K>> discoverEndpoints(
|
||||
Class<? extends Annotation> extensionType, EndpointExposure exposure) {
|
||||
Map<Class<?>, EndpointInfo<T>> endpoints = discoverEndpoints(exposure);
|
||||
Map<Class<?>, EndpointExtensionInfo<T>> extensions = discoverExtensions(endpoints,
|
||||
extensionType, exposure);
|
||||
Collection<EndpointInfoDescriptor<T, K>> result = new ArrayList<>();
|
||||
endpoints.forEach((endpointClass, endpointInfo) -> {
|
||||
EndpointExtensionInfo<T> extension = extensions.remove(endpointClass);
|
||||
result.add(createDescriptor(endpointClass, endpointInfo, extension));
|
||||
});
|
||||
return result;
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class<T> getOperationType() {
|
||||
return (Class<T>) ResolvableType
|
||||
.forClass(AnnotationEndpointDiscoverer.class, getClass())
|
||||
.resolveGeneric(1);
|
||||
}
|
||||
|
||||
private Map<Class<?>, EndpointInfo<T>> discoverEndpoints(EndpointExposure exposure) {
|
||||
private Map<Class<?>, DiscoveredEndpoint> getEndpoints(Class<T> operationType) {
|
||||
Map<Class<?>, DiscoveredEndpoint> endpoints = new LinkedHashMap<>();
|
||||
Map<String, DiscoveredEndpoint> endpointsById = new LinkedHashMap<>();
|
||||
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
|
||||
this.applicationContext, Endpoint.class);
|
||||
Map<Class<?>, EndpointInfo<T>> endpoints = new LinkedHashMap<>();
|
||||
Map<String, EndpointInfo<T>> 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<T> info = createEndpointInfo(beanName, beanType, attributes);
|
||||
EndpointInfo<T> 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<T> createEndpointInfo(String beanName, Class<?> beanType,
|
||||
AnnotationAttributes attributes) {
|
||||
String id = attributes.getString("id");
|
||||
DefaultEnablement defaultEnablement = (DefaultEnablement) attributes
|
||||
.get("defaultEnablement");
|
||||
Map<Method, T> operations = discoverOperations(id, beanName, beanType);
|
||||
return new EndpointInfo<>(id, defaultEnablement, operations.values());
|
||||
private void addEndpoint(Map<Class<?>, DiscoveredEndpoint> endpoints,
|
||||
Map<String, DiscoveredEndpoint> 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<Class<?>, EndpointExtensionInfo<T>> discoverExtensions(
|
||||
Map<Class<?>, EndpointInfo<T>> endpoints,
|
||||
Class<? extends Annotation> 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<T> operations = this.operationsFactory
|
||||
.createOperations(id, target, endpointType).values();
|
||||
EndpointInfo<T> endpointInfo = new EndpointInfo<>(id, enabledByDefault,
|
||||
operations);
|
||||
boolean exposed = isEndpointExposed(endpointType, endpointInfo);
|
||||
return new DiscoveredEndpoint(endpointType, endpointInfo, exposed);
|
||||
}
|
||||
|
||||
private Map<Class<?>, DiscoveredExtension> getExtensions(Class<T> operationType,
|
||||
Map<Class<?>, DiscoveredEndpoint> endpoints) {
|
||||
Map<Class<?>, DiscoveredExtension> extensions = new LinkedHashMap<>();
|
||||
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
|
||||
this.applicationContext, extensionType);
|
||||
Map<Class<?>, EndpointExtensionInfo<T>> 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<Class<?>, DiscoveredEndpoint> endpoints,
|
||||
Map<Class<?>, 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<T> info = getEndpointInfo(endpoints, beanType, endpointType);
|
||||
Map<Method, T> operations = discoverOperations(info.getId(), beanName,
|
||||
beanType);
|
||||
EndpointExtensionInfo<T> extension = new EndpointExtensionInfo<>(beanType,
|
||||
Object target = this.applicationContext.getBean(beanName);
|
||||
Map<Method, T> operations = this.operationsFactory
|
||||
.createOperations(endpoint.getInfo().getId(), target, extensionType);
|
||||
DiscoveredExtension extension = new DiscoveredExtension(extensionType,
|
||||
operations.values());
|
||||
EndpointExtensionInfo<T> 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<T extends Operation, K>
|
|||
+ extension.getExtensionType().getName() + " and "
|
||||
+ previous.getExtensionType().getName());
|
||||
}
|
||||
return extensions;
|
||||
|
||||
}
|
||||
|
||||
private EndpointInfo<T> getEndpointInfo(Map<Class<?>, EndpointInfo<T>> endpoints,
|
||||
Class<?> beanType, Class<?> endpointClass) {
|
||||
EndpointInfo<T> endpoint = endpoints.get(endpointClass);
|
||||
private Class<?> getEndpointType(Class<?> extensionType) {
|
||||
AnnotationAttributes attributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(extensionType, EndpointExtension.class);
|
||||
Class<?> endpointType = attributes.getClass("endpoint");
|
||||
Assert.state(!endpointType.equals(Void.class), () -> "Extension "
|
||||
+ endpointType.getName() + " does not specify an endpoint");
|
||||
return endpointType;
|
||||
}
|
||||
|
||||
private DiscoveredEndpoint getExtendingEndpoint(
|
||||
Map<Class<?>, 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<? extends Annotation> extensionType,
|
||||
Class<?> beanType) {
|
||||
AnnotationAttributes attributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(beanType, extensionType);
|
||||
return (Class<?>) attributes.get("endpoint");
|
||||
}
|
||||
|
||||
private EndpointInfoDescriptor<T, K> createDescriptor(Class<?> type,
|
||||
EndpointInfo<T> info, EndpointExtensionInfo<T> extension) {
|
||||
Map<OperationKey<K>, List<T>> operations = indexOperations(info.getId(), type,
|
||||
info.getOperations());
|
||||
if (extension != null) {
|
||||
operations.putAll(indexOperations(info.getId(), extension.getExtensionType(),
|
||||
extension.getOperations()));
|
||||
return new EndpointInfoDescriptor<>(mergeEndpoint(info, extension),
|
||||
operations);
|
||||
private boolean isEndpointExposed(Class<?> endpointType,
|
||||
EndpointInfo<T> endpointInfo) {
|
||||
if (isEndpointFiltered(endpointInfo)) {
|
||||
return false;
|
||||
}
|
||||
return new EndpointInfoDescriptor<>(info, operations);
|
||||
AnnotationAttributes annotationAttributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(endpointType, FilteredEndpoint.class);
|
||||
if (annotationAttributes == null) {
|
||||
return true;
|
||||
}
|
||||
Class<?> filterClass = annotationAttributes.getClass("value");
|
||||
return isFilterMatch(filterClass, endpointInfo);
|
||||
}
|
||||
|
||||
private EndpointInfo<T> mergeEndpoint(EndpointInfo<T> endpoint,
|
||||
EndpointExtensionInfo<T> extension) {
|
||||
Map<K, T> operations = new HashMap<>();
|
||||
Consumer<T> 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<T> endpointInfo) {
|
||||
for (EndpointFilter<T> filter : this.filters) {
|
||||
if (!isFilterMatch(filter, endpointInfo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Map<OperationKey<K>, List<T>> indexOperations(String endpointId,
|
||||
Class<?> target, Collection<T> operations) {
|
||||
LinkedMultiValueMap<OperationKey<K>, T> result = new LinkedMultiValueMap<>();
|
||||
operations.forEach((operation) -> {
|
||||
K key = this.operationKeyFactory.apply(operation);
|
||||
result.add(new OperationKey<>(endpointId, target, key), operation);
|
||||
private boolean isExtensionExposed(Class<?> extensionType,
|
||||
EndpointInfo<T> endpointInfo) {
|
||||
AnnotationAttributes annotationAttributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(extensionType, EndpointExtension.class);
|
||||
Class<?> filterClass = annotationAttributes.getClass("filter");
|
||||
return isFilterMatch(filterClass, endpointInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isFilterMatch(Class<?> filterClass, EndpointInfo<T> endpointInfo) {
|
||||
Class<?> generic = ResolvableType.forClass(EndpointFilter.class, filterClass)
|
||||
.resolveGeneric(0);
|
||||
if (generic == null || generic.isAssignableFrom(getOperationType())) {
|
||||
EndpointFilter<T> filter = (EndpointFilter<T>) BeanUtils
|
||||
.instantiateClass(filterClass);
|
||||
return isFilterMatch(filter, endpointInfo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isFilterMatch(EndpointFilter<T> filter,
|
||||
EndpointInfo<T> endpointInfo) {
|
||||
try {
|
||||
return filter.match(endpointInfo, this);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
String msg = ex.getMessage();
|
||||
if (msg == null || msg.startsWith(endpointInfo.getClass().getName())) {
|
||||
// Possibly a lambda-defined listener which we could not resolve the
|
||||
// generic event type for
|
||||
Log logger = LogFactory.getLog(getClass());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Non-matching info type for lister: " + filter, ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Collection<DiscoveredEndpoint> mergeExposed(
|
||||
Map<Class<?>, DiscoveredEndpoint> endpoints,
|
||||
Map<Class<?>, DiscoveredExtension> extensions) {
|
||||
List<DiscoveredEndpoint> result = new ArrayList<>();
|
||||
endpoints.forEach((endpointClass, endpoint) -> {
|
||||
if (endpoint.isExposed()) {
|
||||
DiscoveredExtension extension = extensions.remove(endpointClass);
|
||||
result.add(endpoint.merge(extension));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isExposedOver(AnnotationAttributes attributes,
|
||||
EndpointExposure exposure) {
|
||||
if (exposure == null) {
|
||||
return true;
|
||||
}
|
||||
EndpointExposure[] supported = (EndpointExposure[]) attributes.get("exposure");
|
||||
return ObjectUtils.isEmpty(supported)
|
||||
|| ObjectUtils.containsElement(supported, exposure);
|
||||
}
|
||||
|
||||
private Map<Method, T> discoverOperations(String id, String name, Class<?> type) {
|
||||
return MethodIntrospector.selectMethods(type,
|
||||
(MethodIntrospector.MetadataLookup<T>) (
|
||||
method) -> createOperationIfPossible(id, name, method));
|
||||
}
|
||||
|
||||
private T createOperationIfPossible(String endpointId, String beanName,
|
||||
Method method) {
|
||||
T operation = createReadOperationIfPossible(endpointId, beanName, method);
|
||||
if (operation != null) {
|
||||
return operation;
|
||||
}
|
||||
operation = createWriteOperationIfPossible(endpointId, beanName, method);
|
||||
if (operation != null) {
|
||||
return operation;
|
||||
}
|
||||
return createDeleteOperationIfPossible(endpointId, beanName, method);
|
||||
}
|
||||
|
||||
private T createReadOperationIfPossible(String endpointId, String beanName,
|
||||
Method method) {
|
||||
return createOperationIfPossible(endpointId, beanName, method,
|
||||
ReadOperation.class, OperationType.READ);
|
||||
}
|
||||
|
||||
private T createWriteOperationIfPossible(String endpointId, String beanName,
|
||||
Method method) {
|
||||
return createOperationIfPossible(endpointId, beanName, method,
|
||||
WriteOperation.class, OperationType.WRITE);
|
||||
}
|
||||
|
||||
private T createDeleteOperationIfPossible(String endpointId, String beanName,
|
||||
Method method) {
|
||||
return createOperationIfPossible(endpointId, beanName, method,
|
||||
DeleteOperation.class, OperationType.DELETE);
|
||||
}
|
||||
|
||||
private T createOperationIfPossible(String endpointId, String beanName, Method method,
|
||||
Class<? extends Annotation> operationAnnotation,
|
||||
OperationType operationType) {
|
||||
AnnotationAttributes operationAttributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(method, operationAnnotation);
|
||||
if (operationAttributes == null) {
|
||||
return null;
|
||||
}
|
||||
CachingConfiguration cachingConfiguration = this.cachingConfigurationFactory
|
||||
.getCachingConfiguration(endpointId);
|
||||
return this.operationFactory.createOperation(endpointId, operationAttributes,
|
||||
this.applicationContext.getBean(beanName), method, operationType,
|
||||
determineTimeToLive(cachingConfiguration, operationType, method));
|
||||
}
|
||||
|
||||
private long determineTimeToLive(CachingConfiguration cachingConfiguration,
|
||||
OperationType operationType, Method method) {
|
||||
if (cachingConfiguration != null && cachingConfiguration.getTimeToLive() > 0
|
||||
&& operationType == OperationType.READ
|
||||
&& method.getParameters().length == 0) {
|
||||
return cachingConfiguration.getTimeToLive();
|
||||
}
|
||||
return 0;
|
||||
/**
|
||||
* Allows subclasses to verify that the descriptors are correctly configured.
|
||||
* @param exposedEndpoints the discovered endpoints to verify before exposing
|
||||
*/
|
||||
protected void verify(Collection<DiscoveredEndpoint> exposedEndpoints) {
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@code EndpointOperationFactory} creates an {@link Operation} for an operation
|
||||
* on an endpoint.
|
||||
*
|
||||
* @param <T> the {@link Operation} type
|
||||
* A discovered endpoint (which may not be valid and might not ultimately be exposed).
|
||||
*/
|
||||
@FunctionalInterface
|
||||
protected interface EndpointOperationFactory<T extends Operation> {
|
||||
protected final class DiscoveredEndpoint {
|
||||
|
||||
private final EndpointInfo<T> info;
|
||||
|
||||
private final boolean exposed;
|
||||
|
||||
private final Map<OperationKey, List<T>> operations;
|
||||
|
||||
private DiscoveredEndpoint(Class<?> type, EndpointInfo<T> info, boolean exposed) {
|
||||
Assert.notNull(info, "Info must not be null");
|
||||
this.info = info;
|
||||
this.exposed = exposed;
|
||||
this.operations = indexEndpointOperations(type, info);
|
||||
}
|
||||
|
||||
private Map<OperationKey, List<T>> indexEndpointOperations(Class<?> endpointType,
|
||||
EndpointInfo<T> info) {
|
||||
return Collections.unmodifiableMap(
|
||||
indexOperations(info.getId(), endpointType, info.getOperations()));
|
||||
}
|
||||
|
||||
private DiscoveredEndpoint(EndpointInfo<T> info, boolean exposed,
|
||||
Map<OperationKey, List<T>> 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<T> getInfo() {
|
||||
return this.info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the endpoint is exposed.
|
||||
* @return if the is exposed
|
||||
*/
|
||||
private boolean isExposed() {
|
||||
return this.exposed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all operation that were discovered. These might be different to the ones
|
||||
* that are in {@link #getInfo()}.
|
||||
* @return the endpoint operations
|
||||
*/
|
||||
public Map<OperationKey, List<T>> getOperations() {
|
||||
return this.operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find any duplicate operations.
|
||||
* @return any duplicate operations
|
||||
*/
|
||||
public Map<OperationKey, List<T>> findDuplicateOperations() {
|
||||
return this.operations.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue().size() > 1)
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (u, v) -> v,
|
||||
LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private DiscoveredEndpoint merge(DiscoveredExtension extension) {
|
||||
if (extension == null) {
|
||||
return this;
|
||||
}
|
||||
Map<OperationKey, List<T>> operations = mergeOperations(extension);
|
||||
EndpointInfo<T> info = new EndpointInfo<>(this.info.getId(),
|
||||
this.info.isEnableByDefault(), flatten(operations).values());
|
||||
return new DiscoveredEndpoint(info, this.exposed, operations);
|
||||
}
|
||||
|
||||
private Map<OperationKey, List<T>> mergeOperations(
|
||||
DiscoveredExtension extension) {
|
||||
MultiValueMap<OperationKey, T> operations = new LinkedMultiValueMap<>(
|
||||
this.operations);
|
||||
operations.addAll(indexOperations(getInfo().getId(),
|
||||
extension.getExtensionType(), extension.getOperations()));
|
||||
return Collections.unmodifiableMap(operations);
|
||||
}
|
||||
|
||||
private Map<K, T> flatten(Map<OperationKey, List<T>> operations) {
|
||||
Map<K, T> flattened = new LinkedHashMap<>();
|
||||
operations.forEach((operationKey, value) -> flattened
|
||||
.put(operationKey.getKey(), getLastValue(value)));
|
||||
return Collections.unmodifiableMap(flattened);
|
||||
}
|
||||
|
||||
private T getLastValue(List<T> value) {
|
||||
return value.get(value.size() - 1);
|
||||
}
|
||||
|
||||
private MultiValueMap<OperationKey, T> indexOperations(String endpointId,
|
||||
Class<?> target, Collection<T> operations) {
|
||||
LinkedMultiValueMap<OperationKey, T> result = new LinkedMultiValueMap<>();
|
||||
operations.forEach((operation) -> {
|
||||
K key = getOperationKey(operation);
|
||||
result.add(new OperationKey(endpointId, target, key), operation);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private K getOperationKey(T operation) {
|
||||
return AnnotationEndpointDiscoverer.this.operationKeyFactory.apply(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getInfo().toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a tech-specific extension of an endpoint.
|
||||
* @param <T> the type of the operation
|
||||
* A discovered extension.
|
||||
*/
|
||||
private static final class EndpointExtensionInfo<T extends Operation> {
|
||||
protected final class DiscoveredExtension {
|
||||
|
||||
private final Class<?> extensionType;
|
||||
|
||||
private final Collection<T> operations;
|
||||
|
||||
private EndpointExtensionInfo(Class<?> extensionType, Collection<T> operations) {
|
||||
private DiscoveredExtension(Class<?> extensionType, Collection<T> operations) {
|
||||
this.extensionType = extensionType;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
private Class<?> getExtensionType() {
|
||||
public Class<?> getExtensionType() {
|
||||
return this.extensionType;
|
||||
}
|
||||
|
||||
private Collection<T> getOperations() {
|
||||
public Collection<T> getOperations() {
|
||||
return this.operations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an {@link EndpointInfo endpoint} and whether or not it is valid.
|
||||
*
|
||||
* @param <T> the type of the operation
|
||||
* @param <K> the type of the operation key
|
||||
*/
|
||||
protected static class EndpointInfoDescriptor<T extends Operation, K> {
|
||||
|
||||
private final EndpointInfo<T> endpointInfo;
|
||||
|
||||
private final Map<OperationKey<K>, List<T>> operations;
|
||||
|
||||
protected EndpointInfoDescriptor(EndpointInfo<T> endpointInfo,
|
||||
Map<OperationKey<K>, List<T>> operations) {
|
||||
this.endpointInfo = endpointInfo;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public EndpointInfo<T> getEndpointInfo() {
|
||||
return this.endpointInfo;
|
||||
}
|
||||
|
||||
public Map<OperationKey<K>, List<T>> findDuplicateOperations() {
|
||||
Map<OperationKey<K>, List<T>> duplicateOperations = new HashMap<>();
|
||||
this.operations.forEach((k, list) -> {
|
||||
if (list.size() > 1) {
|
||||
duplicateOperations.put(k, list);
|
||||
}
|
||||
});
|
||||
return duplicateOperations;
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.extensionType.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the key of an operation in the context of an operation's implementation.
|
||||
*
|
||||
* @param <K> the type of the key
|
||||
*/
|
||||
protected static final class OperationKey<K> {
|
||||
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<T extends Operation, K>
|
|||
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<T extends Operation, K>
|
|||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ public @interface DeleteOperation {
|
|||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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.).
|
||||
* <p>
|
||||
* {@code @Endpoint} represents the lowest common denominator for endpoints and
|
||||
* intentionally limits the sorts of operation methods that may be defined in order to
|
||||
* support the broadest possible range of exposure technologies. If you need deeper
|
||||
* support for a specific technology you can either write an endpoint that is
|
||||
* {@link FilteredEndpoint filtered} to a certain technology, or provide
|
||||
* {@link EndpointExtension extension} for the broader endpoint.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see EndpointExtension
|
||||
* @see FilteredEndpoint
|
||||
* @see AnnotationEndpointDiscoverer
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
|
@ -41,20 +55,12 @@ public @interface Endpoint {
|
|||
* The id of the endpoint.
|
||||
* @return the id
|
||||
*/
|
||||
String id();
|
||||
String id() default "";
|
||||
|
||||
/**
|
||||
* Defines the {@link EndpointExposure technologies} over which the endpoint should be
|
||||
* exposed. By default, all technologies are supported.
|
||||
* @return the supported endpoint exposure technologies
|
||||
* If the endpoint should be enabled or disabled by default.
|
||||
* @return {@code true} if the endpoint is enabled by default
|
||||
*/
|
||||
EndpointExposure[] exposure() default {};
|
||||
|
||||
/**
|
||||
* Defines the {@link DefaultEnablement} of the endpoint. By default, the endpoint's
|
||||
* enablement defaults to the "default" settings.
|
||||
* @return the default enablement
|
||||
*/
|
||||
DefaultEnablement defaultEnablement() default DefaultEnablement.NEUTRAL;
|
||||
boolean enableByDefault() default true;
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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: <pre class="code">
|
||||
* @EndpointExtension(filter = WebEndpointFilter.class)
|
||||
* public @interface EndpointWebExtension {
|
||||
*
|
||||
* @AliasFor(annotation = EndpointExtension.class, attribute = "endpoint")
|
||||
* Class<?> endpoint();
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @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<? extends EndpointFilter<?>> filter();
|
||||
|
||||
/**
|
||||
* The class of the endpoint to extend.
|
||||
* @return the class endpoint to extend
|
||||
*/
|
||||
Class<?> endpoint() default Void.class;
|
||||
|
||||
}
|
|
@ -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:<pre class="code">
|
||||
* @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;
|
||||
*
|
||||
* } </pre>
|
||||
* @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<? extends EndpointFilter<?>> value();
|
||||
|
||||
}
|
|
@ -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 <T> the {@link Operation} type
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface OperationFactory<T extends Operation> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
|
@ -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 <T> The operation type
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OperationsFactory<T extends Operation> {
|
||||
|
||||
private static final Map<OperationType, Class<? extends Annotation>> OPERATION_TYPES;
|
||||
|
||||
static {
|
||||
Map<OperationType, Class<? extends Annotation>> 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<T> operationFactory;
|
||||
|
||||
private final ParameterMapper parameterMapper;
|
||||
|
||||
private final Collection<OperationMethodInvokerAdvisor> invokerAdvisors;
|
||||
|
||||
OperationsFactory(OperationFactory<T> operationFactory,
|
||||
ParameterMapper parameterMapper,
|
||||
Collection<? extends OperationMethodInvokerAdvisor> invokerAdvisors) {
|
||||
this.operationFactory = operationFactory;
|
||||
this.parameterMapper = parameterMapper;
|
||||
this.invokerAdvisors = (invokerAdvisors == null ? Collections.emptyList()
|
||||
: new ArrayList<>(invokerAdvisors));
|
||||
}
|
||||
|
||||
public Map<Method, T> createOperations(String id, Object target, Class<?> type) {
|
||||
return MethodIntrospector.selectMethods(type,
|
||||
(MetadataLookup<T>) (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<? extends Annotation> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -35,7 +35,6 @@ public @interface ReadOperation {
|
|||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
|
|
@ -35,7 +35,6 @@ public @interface WriteOperation {
|
|||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, Long> endpointIdTimeToLive;
|
||||
|
||||
public CachingOperationInvokerAdvisor(Function<String, Long> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<String, Object> arguments = getArguments(params,
|
||||
|
|
|
@ -36,10 +36,10 @@ public final class EndpointMBeanInfo {
|
|||
|
||||
private final MBeanInfo mBeanInfo;
|
||||
|
||||
private final Map<String, JmxEndpointOperation> operations;
|
||||
private final Map<String, JmxOperation> operations;
|
||||
|
||||
public EndpointMBeanInfo(String endpointId, MBeanInfo mBeanInfo,
|
||||
Map<String, JmxEndpointOperation> operations) {
|
||||
Map<String, JmxOperation> operations) {
|
||||
this.endpointId = endpointId;
|
||||
this.mBeanInfo = mBeanInfo;
|
||||
this.operations = operations;
|
||||
|
@ -53,7 +53,7 @@ public final class EndpointMBeanInfo {
|
|||
return this.mBeanInfo;
|
||||
}
|
||||
|
||||
public Map<String, JmxEndpointOperation> getOperations() {
|
||||
public Map<String, JmxOperation> getOperations() {
|
||||
return this.operations;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,12 +54,12 @@ class EndpointMBeanInfoAssembler {
|
|||
* @return the mbean info for the endpoint
|
||||
*/
|
||||
EndpointMBeanInfo createEndpointMBeanInfo(
|
||||
EndpointInfo<JmxEndpointOperation> endpointInfo) {
|
||||
EndpointInfo<JmxOperation> endpointInfo) {
|
||||
Map<String, OperationInfos> operationsMapping = getOperationInfo(endpointInfo);
|
||||
ModelMBeanOperationInfo[] operationsMBeanInfo = operationsMapping.values()
|
||||
.stream().map((t) -> t.mBeanOperationInfo).collect(Collectors.toList())
|
||||
.toArray(new ModelMBeanOperationInfo[] {});
|
||||
Map<String, JmxEndpointOperation> operationsInfo = new LinkedHashMap<>();
|
||||
Map<String, JmxOperation> 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<String, OperationInfos> getOperationInfo(
|
||||
EndpointInfo<JmxEndpointOperation> endpointInfo) {
|
||||
EndpointInfo<JmxOperation> endpointInfo) {
|
||||
Map<String, OperationInfos> 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;
|
||||
}
|
||||
|
|
|
@ -50,11 +50,11 @@ public class JmxEndpointMBeanFactory {
|
|||
* @return the MBeans
|
||||
*/
|
||||
public Collection<EndpointMBean> createMBeans(
|
||||
Collection<EndpointInfo<JmxEndpointOperation>> endpoints) {
|
||||
Collection<EndpointInfo<JmxOperation>> endpoints) {
|
||||
return endpoints.stream().map(this::createMBean).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private EndpointMBean createMBean(EndpointInfo<JmxEndpointOperation> endpointInfo) {
|
||||
private EndpointMBean createMBean(EndpointInfo<JmxOperation> endpointInfo) {
|
||||
EndpointMBeanInfo endpointMBeanInfo = this.assembler
|
||||
.createEndpointMBeanInfo(endpointInfo);
|
||||
return new EndpointMBean(this.resultMapper::mapResponse, endpointMBeanInfo);
|
||||
|
|
|
@ -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<JmxEndpointOperationParameterInfo> 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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<JmxEndpointOperation, String> {
|
||||
extends AnnotationEndpointDiscoverer<String, JmxOperation> {
|
||||
|
||||
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<? extends OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<? extends EndpointFilter<JmxOperation>> filters) {
|
||||
super(applicationContext, new JmxEndpointOperationFactory(),
|
||||
JmxOperation::getOperationName, parameterMapper, invokerAdvisors,
|
||||
filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EndpointInfo<JmxEndpointOperation>> discoverEndpoints() {
|
||||
Collection<EndpointInfoDescriptor<JmxEndpointOperation, String>> endpointDescriptors = discoverEndpoints(
|
||||
JmxEndpointExtension.class, EndpointExposure.JMX);
|
||||
verifyThatOperationsHaveDistinctName(endpointDescriptors);
|
||||
return endpointDescriptors.stream().map(EndpointInfoDescriptor::getEndpointInfo)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void verifyThatOperationsHaveDistinctName(
|
||||
Collection<EndpointInfoDescriptor<JmxEndpointOperation, String>> endpointDescriptors) {
|
||||
List<List<JmxEndpointOperation>> clashes = new ArrayList<>();
|
||||
endpointDescriptors.forEach((descriptor) -> clashes
|
||||
.addAll(descriptor.findDuplicateOperations().values()));
|
||||
protected void verify(Collection<DiscoveredEndpoint> exposedEndpoints) {
|
||||
List<List<JmxOperation>> 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<JmxEndpointOperation> {
|
||||
|
||||
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<JmxEndpointOperationParameterInfo> parameters = getParameters(invoker);
|
||||
return new JmxEndpointOperation(type,
|
||||
CachingOperationInvoker.apply(invoker, timeToLive), operationName,
|
||||
outputType, description, parameters);
|
||||
}
|
||||
|
||||
private String getDescription(Method method, Supplier<String> fallback) {
|
||||
ManagedOperation managedOperation = jmxAttributeSource
|
||||
.getManagedOperation(method);
|
||||
if (managedOperation != null
|
||||
&& StringUtils.hasText(managedOperation.getDescription())) {
|
||||
return managedOperation.getDescription();
|
||||
}
|
||||
return fallback.get();
|
||||
}
|
||||
|
||||
private List<JmxEndpointOperationParameterInfo> 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<JmxEndpointOperationParameterInfo> mergeParameters(
|
||||
Parameter[] methodParameters,
|
||||
ManagedOperationParameter[] operationParameters) {
|
||||
List<JmxEndpointOperationParameterInfo> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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<JmxOperation> {
|
||||
|
||||
@Override
|
||||
public boolean match(EndpointInfo<JmxOperation> info,
|
||||
EndpointDiscoverer<JmxOperation> discoverer) {
|
||||
return (discoverer instanceof JmxAnnotationEndpointDiscoverer);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue