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-10176pull/10948/merge
parent
d24709c696
commit
fd5c43cdc9
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporterProperties.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.java
45
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointExporterProperties.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.java
13
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolver.java
13
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolver.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolver.java
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||||
|
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
||||||
|
import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor;
|
||||||
|
import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link EnableAutoConfiguration Auto-configuration} for web {@link Endpoint} support.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnWebApplication
|
||||||
|
@AutoConfigureAfter(EndpointAutoConfiguration.class)
|
||||||
|
@EnableConfigurationProperties(WebEndpointProperties.class)
|
||||||
|
@ConditionalOnProperty(name = "management.endpoints.web.enabled", matchIfMissing = true)
|
||||||
|
public class WebEndpointAutoConfiguration {
|
||||||
|
|
||||||
|
private static final List<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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
24
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactoryTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunctionTests.java
24
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultCachingConfigurationFactoryTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointIdTimeToLivePropertyFunctionTests.java
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||||
|
import org.springframework.boot.actuate.endpoint.Operation;
|
||||||
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ExposeExcludePropertyEndpointFilter}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class ExposeExcludePropertyEndpointFilterTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
private MockEnvironment environment = new MockEnvironment();
|
||||||
|
|
||||||
|
private EndpointFilter<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> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolverTests.java
19
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/DefaultEndpointPathResolverTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/DefaultEndpointPathResolverTests.java
11
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationTests.java
11
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationTests.java
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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