mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Fix ConditionalOnAvailableEndpoint dashed matching
Update `ConditionalOnAvailableEndpoint` so that it now uses the same matching code as the endpoint filter. This allows the condition to match endpoint IDs that contain a dash. In order to share logic, the `ExposeExcludePropertyEndpointFilter` class has been deprecated and its logic moved to a new `expose` package under `IncludExcludeEndpointFilter`. This filter is used by both the `OnAvailableEndpointCondition` and the auto-configuration classes. Fixes gh-21044
This commit is contained in:
parent
439d9beecb
commit
df26e24605
@ -16,21 +16,12 @@
|
||||
|
||||
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.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
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 include} and
|
||||
@ -39,108 +30,20 @@ import org.springframework.util.Assert;
|
||||
* @param <E> the endpoint type
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.7 in favor of {@link IncludExcludeEndpointFilter}
|
||||
*/
|
||||
public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>> implements EndpointFilter<E> {
|
||||
|
||||
private final Class<E> endpointType;
|
||||
|
||||
private final EndpointPatterns include;
|
||||
|
||||
private final EndpointPatterns exclude;
|
||||
|
||||
private final EndpointPatterns exposeDefaults;
|
||||
@Deprecated
|
||||
public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>>
|
||||
extends IncludExcludeEndpointFilter<E> {
|
||||
|
||||
public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, Environment environment, String prefix,
|
||||
String... exposeDefaults) {
|
||||
Assert.notNull(endpointType, "EndpointType 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.endpointType = endpointType;
|
||||
this.include = new EndpointPatterns(bind(binder, prefix + ".include"));
|
||||
this.exclude = new EndpointPatterns(bind(binder, prefix + ".exclude"));
|
||||
this.exposeDefaults = new EndpointPatterns(exposeDefaults);
|
||||
super(endpointType, environment, prefix, exposeDefaults);
|
||||
}
|
||||
|
||||
public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, Collection<String> include,
|
||||
Collection<String> exclude, String... exposeDefaults) {
|
||||
Assert.notNull(endpointType, "EndpointType Type must not be null");
|
||||
this.endpointType = endpointType;
|
||||
this.include = new EndpointPatterns(include);
|
||||
this.exclude = new EndpointPatterns(exclude);
|
||||
this.exposeDefaults = new EndpointPatterns(exposeDefaults);
|
||||
}
|
||||
|
||||
private List<String> bind(Binder binder, String name) {
|
||||
return binder.bind(name, Bindable.listOf(String.class)).orElseGet(ArrayList::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(E endpoint) {
|
||||
if (this.endpointType.isInstance(endpoint)) {
|
||||
return isExposed(endpoint) && !isExcluded(endpoint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isExposed(ExposableEndpoint<?> endpoint) {
|
||||
if (this.include.isEmpty()) {
|
||||
return this.exposeDefaults.matchesAll() || this.exposeDefaults.matches(endpoint);
|
||||
}
|
||||
return this.include.matchesAll() || this.include.matches(endpoint);
|
||||
}
|
||||
|
||||
private boolean isExcluded(ExposableEndpoint<?> endpoint) {
|
||||
if (this.exclude.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.exclude.matchesAll() || this.exclude.matches(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of endpoint patterns used to match IDs.
|
||||
*/
|
||||
private static class EndpointPatterns {
|
||||
|
||||
private final boolean empty;
|
||||
|
||||
private final boolean matchesAll;
|
||||
|
||||
private final Set<EndpointId> endpointIds;
|
||||
|
||||
EndpointPatterns(String[] patterns) {
|
||||
this((patterns != null) ? Arrays.asList(patterns) : (Collection<String>) null);
|
||||
}
|
||||
|
||||
EndpointPatterns(Collection<String> patterns) {
|
||||
patterns = (patterns != null) ? patterns : Collections.emptySet();
|
||||
boolean matchesAll = false;
|
||||
Set<EndpointId> endpointIds = new LinkedHashSet<>();
|
||||
for (String pattern : patterns) {
|
||||
if ("*".equals(pattern)) {
|
||||
matchesAll = true;
|
||||
}
|
||||
else {
|
||||
endpointIds.add(EndpointId.fromPropertyValue(pattern));
|
||||
}
|
||||
}
|
||||
this.empty = patterns.isEmpty();
|
||||
this.matchesAll = matchesAll;
|
||||
this.endpointIds = endpointIds;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
|
||||
boolean matchesAll() {
|
||||
return this.matchesAll;
|
||||
}
|
||||
|
||||
boolean matches(ExposableEndpoint<?> endpoint) {
|
||||
return this.endpointIds.contains(endpoint.getEndpointId());
|
||||
}
|
||||
|
||||
super(endpointType, include, exclude, exposeDefaults);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@ -16,20 +16,18 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter.DefaultIncludes;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
@ -39,13 +37,14 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnAvailableEndpoint
|
||||
*/
|
||||
class OnAvailableEndpointCondition extends AbstractEndpointCondition {
|
||||
|
||||
private static final String JMX_ENABLED_KEY = "spring.jmx.enabled";
|
||||
|
||||
private static final ConcurrentReferenceHashMap<Environment, Set<ExposureInformation>> endpointExposureCache = new ConcurrentReferenceHashMap<>();
|
||||
private static final Map<Environment, Set<Exposure>> exposuresCache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
@ -60,79 +59,51 @@ class OnAvailableEndpointCondition extends AbstractEndpointCondition {
|
||||
return new ConditionOutcome(true, message.andCondition(ConditionalOnAvailableEndpoint.class)
|
||||
.because("application is running on Cloud Foundry"));
|
||||
}
|
||||
AnnotationAttributes attributes = getEndpointAttributes(ConditionalOnAvailableEndpoint.class, context,
|
||||
metadata);
|
||||
EndpointId id = EndpointId.of(environment, attributes.getString("id"));
|
||||
Set<ExposureInformation> exposureInformations = getExposureInformation(environment);
|
||||
for (ExposureInformation exposureInformation : exposureInformations) {
|
||||
if (exposureInformation.isExposed(id)) {
|
||||
EndpointId id = EndpointId.of(environment,
|
||||
getEndpointAttributes(ConditionalOnAvailableEndpoint.class, context, metadata).getString("id"));
|
||||
Set<Exposure> exposures = getExposures(environment);
|
||||
for (Exposure exposure : exposures) {
|
||||
if (exposure.isExposed(id)) {
|
||||
return new ConditionOutcome(true,
|
||||
message.andCondition(ConditionalOnAvailableEndpoint.class)
|
||||
.because("marked as exposed by a 'management.endpoints."
|
||||
+ exposureInformation.getPrefix() + ".exposure' property"));
|
||||
.because("marked as exposed by a 'management.endpoints." + exposure.getPrefix()
|
||||
+ ".exposure' property"));
|
||||
}
|
||||
}
|
||||
return new ConditionOutcome(false, message.andCondition(ConditionalOnAvailableEndpoint.class)
|
||||
.because("no 'management.endpoints' property marked it as exposed"));
|
||||
}
|
||||
|
||||
private Set<ExposureInformation> getExposureInformation(Environment environment) {
|
||||
Set<ExposureInformation> exposureInformations = endpointExposureCache.get(environment);
|
||||
if (exposureInformations == null) {
|
||||
exposureInformations = new HashSet<>(2);
|
||||
Binder binder = Binder.get(environment);
|
||||
private Set<Exposure> getExposures(Environment environment) {
|
||||
Set<Exposure> exposures = exposuresCache.get(environment);
|
||||
if (exposures == null) {
|
||||
exposures = new HashSet<>(2);
|
||||
if (environment.getProperty(JMX_ENABLED_KEY, Boolean.class, false)) {
|
||||
exposureInformations.add(new ExposureInformation(binder, "jmx", "*"));
|
||||
exposures.add(new Exposure(environment, "jmx", DefaultIncludes.JMX));
|
||||
}
|
||||
exposureInformations.add(new ExposureInformation(binder, "web", "info", "health"));
|
||||
endpointExposureCache.put(environment, exposureInformations);
|
||||
exposures.add(new Exposure(environment, "web", DefaultIncludes.WEB));
|
||||
exposuresCache.put(environment, exposures);
|
||||
}
|
||||
return exposureInformations;
|
||||
return exposures;
|
||||
}
|
||||
|
||||
static class ExposureInformation {
|
||||
static class Exposure extends IncludExcludeEndpointFilter<ExposableEndpoint<?>> {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
private final Set<String> include;
|
||||
|
||||
private final Set<String> exclude;
|
||||
|
||||
private final Set<String> exposeDefaults;
|
||||
|
||||
ExposureInformation(Binder binder, String prefix, String... exposeDefaults) {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
Exposure(Environment environment, String prefix, DefaultIncludes defaultIncludes) {
|
||||
super((Class) ExposableEndpoint.class, environment, "management.endpoints." + prefix + ".exposure",
|
||||
defaultIncludes);
|
||||
this.prefix = prefix;
|
||||
this.include = bind(binder, "management.endpoints." + prefix + ".exposure.include");
|
||||
this.exclude = bind(binder, "management.endpoints." + prefix + ".exposure.exclude");
|
||||
this.exposeDefaults = new HashSet<>(Arrays.asList(exposeDefaults));
|
||||
}
|
||||
|
||||
private Set<String> bind(Binder binder, String name) {
|
||||
List<String> values = binder.bind(name, Bindable.listOf(String.class)).orElse(Collections.emptyList());
|
||||
Set<String> result = new HashSet<>(values.size());
|
||||
for (String value : values) {
|
||||
result.add("*".equals(value) ? "*" : EndpointId.fromPropertyValue(value).toLowerCaseString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
boolean isExposed(EndpointId endpointId) {
|
||||
String id = endpointId.toLowerCaseString();
|
||||
if (!this.exclude.isEmpty()) {
|
||||
if (this.exclude.contains("*") || this.exclude.contains(id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.include.isEmpty()) {
|
||||
if (this.exposeDefaults.contains("*") || this.exposeDefaults.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.include.contains("*") || this.include.contains(id);
|
||||
boolean isExposed(EndpointId id) {
|
||||
return super.match(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.expose;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
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 include} and
|
||||
* {@code exclude} patterns.
|
||||
*
|
||||
* @param <E> the endpoint type
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.7
|
||||
*/
|
||||
public class IncludExcludeEndpointFilter<E extends ExposableEndpoint<?>> implements EndpointFilter<E> {
|
||||
|
||||
private final Class<E> endpointType;
|
||||
|
||||
private final EndpointPatterns include;
|
||||
|
||||
private final EndpointPatterns defaultIncludes;
|
||||
|
||||
private final EndpointPatterns exclude;
|
||||
|
||||
/**
|
||||
* Create a new {@link IncludExcludeEndpointFilter} with include/exclude rules bound
|
||||
* from the {@link Environment}.
|
||||
* @param endpointType the endpoint type that should be considered (other types always
|
||||
* match)
|
||||
* @param environment the environment containing the properties
|
||||
* @param prefix the property prefix to bind
|
||||
* @param defaultIncludes the default {@code includes} to use when none are specified.
|
||||
*/
|
||||
public IncludExcludeEndpointFilter(Class<E> endpointType, Environment environment, String prefix,
|
||||
String... defaultIncludes) {
|
||||
this(endpointType, environment, prefix, new EndpointPatterns(defaultIncludes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link IncludExcludeEndpointFilter} with include/exclude rules bound
|
||||
* from the {@link Environment}.
|
||||
* @param endpointType the endpoint type that should be considered (other types always
|
||||
* match)
|
||||
* @param environment the environment containing the properties
|
||||
* @param prefix the property prefix to bind
|
||||
* @param defaultIncludes the default {@code includes} to use when none are specified.
|
||||
*/
|
||||
public IncludExcludeEndpointFilter(Class<E> endpointType, Environment environment, String prefix,
|
||||
DefaultIncludes defaultIncludes) {
|
||||
this(endpointType, environment, prefix, DefaultIncludes.patterns(defaultIncludes));
|
||||
}
|
||||
|
||||
private IncludExcludeEndpointFilter(Class<E> endpointType, Environment environment, String prefix,
|
||||
EndpointPatterns defaultIncludes) {
|
||||
Assert.notNull(endpointType, "EndpointType must not be null");
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
Assert.hasText(prefix, "Prefix must not be empty");
|
||||
Assert.notNull(defaultIncludes, "DefaultIncludes must not be null");
|
||||
Binder binder = Binder.get(environment);
|
||||
this.endpointType = endpointType;
|
||||
this.include = new EndpointPatterns(bind(binder, prefix + ".include"));
|
||||
this.defaultIncludes = defaultIncludes;
|
||||
this.exclude = new EndpointPatterns(bind(binder, prefix + ".exclude"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link IncludExcludeEndpointFilter} with specific include/exclude
|
||||
* rules.
|
||||
* @param endpointType the endpoint type that should be considered (other types always
|
||||
* match)
|
||||
* @param include the include patterns
|
||||
* @param exclude the exclude patterns
|
||||
* @param defaultIncludes the default {@code includes} to use when none are specified.
|
||||
*/
|
||||
public IncludExcludeEndpointFilter(Class<E> endpointType, Collection<String> include, Collection<String> exclude,
|
||||
String... defaultIncludes) {
|
||||
this(endpointType, include, exclude, new EndpointPatterns(defaultIncludes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link IncludExcludeEndpointFilter} with specific include/exclude
|
||||
* rules.
|
||||
* @param endpointType the endpoint type that should be considered (other types always
|
||||
* match)
|
||||
* @param include the include patterns
|
||||
* @param exclude the exclude patterns
|
||||
* @param defaultIncludes the default {@code includes} to use when none are specified.
|
||||
*/
|
||||
public IncludExcludeEndpointFilter(Class<E> endpointType, Collection<String> include, Collection<String> exclude,
|
||||
DefaultIncludes defaultIncludes) {
|
||||
this(endpointType, include, exclude, DefaultIncludes.patterns(defaultIncludes));
|
||||
}
|
||||
|
||||
private IncludExcludeEndpointFilter(Class<E> endpointType, Collection<String> include, Collection<String> exclude,
|
||||
EndpointPatterns defaultIncludes) {
|
||||
Assert.notNull(endpointType, "EndpointType Type must not be null");
|
||||
Assert.notNull(defaultIncludes, "DefaultIncludes must not be null");
|
||||
this.endpointType = endpointType;
|
||||
this.include = new EndpointPatterns(include);
|
||||
this.defaultIncludes = defaultIncludes;
|
||||
this.exclude = new EndpointPatterns(exclude);
|
||||
}
|
||||
|
||||
private List<String> bind(Binder binder, String name) {
|
||||
return binder.bind(name, Bindable.listOf(String.class)).orElseGet(ArrayList::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(E endpoint) {
|
||||
if (!this.endpointType.isInstance(endpoint)) {
|
||||
// Leave non-matching types for other filters
|
||||
return true;
|
||||
}
|
||||
return match(endpoint.getEndpointId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the filter matches.
|
||||
* @param endpointId the endpoint ID to check
|
||||
* @return {@code true} if the filter matches
|
||||
*/
|
||||
protected final boolean match(EndpointId endpointId) {
|
||||
return isIncluded(endpointId) && !isExcluded(endpointId);
|
||||
}
|
||||
|
||||
private boolean isIncluded(EndpointId endpointId) {
|
||||
if (this.include.isEmpty()) {
|
||||
return this.defaultIncludes.matches(endpointId);
|
||||
}
|
||||
return this.include.matches(endpointId);
|
||||
}
|
||||
|
||||
private boolean isExcluded(EndpointId endpointId) {
|
||||
if (this.exclude.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.exclude.matches(endpointId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default include patterns that can be used.
|
||||
*/
|
||||
public enum DefaultIncludes {
|
||||
|
||||
/**
|
||||
* The default set of include patterns used for JMX.
|
||||
*/
|
||||
JMX("*"),
|
||||
|
||||
/**
|
||||
* The default set of include patterns used for web.
|
||||
*/
|
||||
WEB("info", "health");
|
||||
|
||||
private final EndpointPatterns patterns;
|
||||
|
||||
DefaultIncludes(String... patterns) {
|
||||
this.patterns = new EndpointPatterns(patterns);
|
||||
}
|
||||
|
||||
static EndpointPatterns patterns(DefaultIncludes defaultIncludes) {
|
||||
return (defaultIncludes != null) ? defaultIncludes.patterns : (EndpointPatterns) null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of endpoint patterns used to match IDs.
|
||||
*/
|
||||
private static class EndpointPatterns {
|
||||
|
||||
private final boolean empty;
|
||||
|
||||
private final boolean matchesAll;
|
||||
|
||||
private final Set<EndpointId> endpointIds;
|
||||
|
||||
EndpointPatterns(String[] patterns) {
|
||||
this((patterns != null) ? Arrays.asList(patterns) : (Collection<String>) null);
|
||||
}
|
||||
|
||||
EndpointPatterns(Collection<String> patterns) {
|
||||
patterns = (patterns != null) ? patterns : Collections.emptySet();
|
||||
boolean matchesAll = false;
|
||||
Set<EndpointId> endpointIds = new LinkedHashSet<>();
|
||||
for (String pattern : patterns) {
|
||||
if ("*".equals(pattern)) {
|
||||
matchesAll = true;
|
||||
}
|
||||
else {
|
||||
endpointIds.add(EndpointId.fromPropertyValue(pattern));
|
||||
}
|
||||
}
|
||||
this.empty = patterns.isEmpty();
|
||||
this.matchesAll = matchesAll;
|
||||
this.endpointIds = endpointIds;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.empty;
|
||||
}
|
||||
|
||||
boolean matches(EndpointId endpointId) {
|
||||
return this.matchesAll || this.endpointIds.contains(endpointId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Endpoint exposure logic used for auto-configuration and conditions.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.expose;
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@ -23,7 +23,7 @@ import javax.management.MBeanServer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
|
||||
@ -97,9 +97,9 @@ public class JmxEndpointAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExposeExcludePropertyEndpointFilter<ExposableJmxEndpoint> jmxIncludeExcludePropertyEndpointFilter() {
|
||||
public IncludExcludeEndpointFilter<ExposableJmxEndpoint> jmxIncludeExcludePropertyEndpointFilter() {
|
||||
JmxEndpointProperties.Exposure exposure = this.properties.getExposure();
|
||||
return new ExposeExcludePropertyEndpointFilter<>(ExposableJmxEndpoint.class, exposure.getInclude(),
|
||||
return new IncludExcludeEndpointFilter<>(ExposableJmxEndpoint.class, exposure.getInclude(),
|
||||
exposure.getExclude(), "*");
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableServletEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar;
|
||||
@ -47,10 +47,10 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
public class ServletEndpointManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public ExposeExcludePropertyEndpointFilter<ExposableServletEndpoint> servletExposeExcludePropertyEndpointFilter(
|
||||
public IncludExcludeEndpointFilter<ExposableServletEndpoint> servletExposeExcludePropertyEndpointFilter(
|
||||
WebEndpointProperties properties) {
|
||||
WebEndpointProperties.Exposure exposure = properties.getExposure();
|
||||
return new ExposeExcludePropertyEndpointFilter<>(ExposableServletEndpoint.class, exposure.getInclude(),
|
||||
return new IncludExcludeEndpointFilter<>(ExposableServletEndpoint.class, exposure.getInclude(),
|
||||
exposure.getExclude());
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@ -22,7 +22,8 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter.DefaultIncludes;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@ -112,16 +113,16 @@ public class WebEndpointAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExposeExcludePropertyEndpointFilter<ExposableWebEndpoint> webExposeExcludePropertyEndpointFilter() {
|
||||
public IncludExcludeEndpointFilter<ExposableWebEndpoint> webExposeExcludePropertyEndpointFilter() {
|
||||
WebEndpointProperties.Exposure exposure = this.properties.getExposure();
|
||||
return new ExposeExcludePropertyEndpointFilter<>(ExposableWebEndpoint.class, exposure.getInclude(),
|
||||
exposure.getExclude(), "info", "health");
|
||||
return new IncludExcludeEndpointFilter<>(ExposableWebEndpoint.class, exposure.getInclude(),
|
||||
exposure.getExclude(), DefaultIncludes.WEB);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExposeExcludePropertyEndpointFilter<ExposableControllerEndpoint> controllerExposeExcludePropertyEndpointFilter() {
|
||||
public IncludExcludeEndpointFilter<ExposableControllerEndpoint> controllerExposeExcludePropertyEndpointFilter() {
|
||||
WebEndpointProperties.Exposure exposure = this.properties.getExposure();
|
||||
return new ExposeExcludePropertyEndpointFilter<>(ExposableControllerEndpoint.class, exposure.getInclude(),
|
||||
return new IncludExcludeEndpointFilter<>(ExposableControllerEndpoint.class, exposure.getInclude(),
|
||||
exposure.getExclude());
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Deprecated
|
||||
class ExposeExcludePropertyEndpointFilterTests {
|
||||
|
||||
private ExposeExcludePropertyEndpointFilter<?> filter;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@ -182,6 +182,20 @@ class ConditionalOnAvailableEndpointTests {
|
||||
(context) -> assertThat(context).hasBean("info").hasBean("health").hasBean("spring").hasBean("test"));
|
||||
}
|
||||
|
||||
@Test // gh-21044
|
||||
void outcomeWhenIncludeAllShouldMatchDashedEndpoint() throws Exception {
|
||||
this.contextRunner.withUserConfiguration(DashedEndpointConfiguration.class)
|
||||
.withPropertyValues("management.endpoints.web.exposure.include=*")
|
||||
.run((context) -> assertThat(context).hasSingleBean(DashedEndpoint.class));
|
||||
}
|
||||
|
||||
@Test // gh-21044
|
||||
void outcomeWhenIncludeDashedShouldMatchDashedEndpoint() throws Exception {
|
||||
this.contextRunner.withUserConfiguration(DashedEndpointConfiguration.class)
|
||||
.withPropertyValues("management.endpoints.web.exposure.include=test-dashed")
|
||||
.run((context) -> assertThat(context).hasSingleBean(DashedEndpoint.class));
|
||||
}
|
||||
|
||||
@Endpoint(id = "health")
|
||||
static class HealthEndpoint {
|
||||
|
||||
@ -207,6 +221,11 @@ class ConditionalOnAvailableEndpointTests {
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test-dashed")
|
||||
static class DashedEndpoint {
|
||||
|
||||
}
|
||||
|
||||
@EndpointExtension(endpoint = SpringEndpoint.class, filter = TestFilter.class)
|
||||
static class SpringEndpointExtension {
|
||||
|
||||
@ -284,4 +303,15 @@ class ConditionalOnAvailableEndpointTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DashedEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnAvailableEndpoint
|
||||
DashedEndpoint dashedEndpoint() {
|
||||
return new DashedEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.expose;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link IncludExcludeEndpointFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class IncludExcludeEndpointFilterTests {
|
||||
|
||||
private IncludExcludeEndpointFilter<?> filter;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenEndpointTypeIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new IncludExcludeEndpointFilter<>(null, new MockEnvironment(), "foo"))
|
||||
.withMessageContaining("EndpointType must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenEnvironmentIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new IncludExcludeEndpointFilter<>(ExposableEndpoint.class, null, "foo"))
|
||||
.withMessageContaining("Environment must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenPrefixIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(
|
||||
() -> new IncludExcludeEndpointFilter<>(ExposableEndpoint.class, new MockEnvironment(), null))
|
||||
.withMessageContaining("Prefix must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenPrefixIsEmptyShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new IncludExcludeEndpointFilter<>(ExposableEndpoint.class, new MockEnvironment(), ""))
|
||||
.withMessageContaining("Prefix must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeIsEmptyAndExcludeIsEmptyAndInDefaultShouldMatch() {
|
||||
setupFilter("", "");
|
||||
assertThat(match(EndpointId.of("def"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeIsEmptyAndExcludeIsEmptyAndNotInDefaultShouldNotMatch() {
|
||||
setupFilter("", "");
|
||||
assertThat(match(EndpointId.of("bar"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeMatchesAndExcludeIsEmptyShouldMatch() {
|
||||
setupFilter("bar", "");
|
||||
assertThat(match(EndpointId.of("bar"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeDoesNotMatchAndExcludeIsEmptyShouldNotMatch() {
|
||||
setupFilter("bar", "");
|
||||
assertThat(match(EndpointId.of("baz"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeMatchesAndExcludeMatchesShouldNotMatch() {
|
||||
setupFilter("bar,baz", "baz");
|
||||
assertThat(match(EndpointId.of("baz"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeMatchesAndExcludeDoesNotMatchShouldMatch() {
|
||||
setupFilter("bar,baz", "buz");
|
||||
assertThat(match(EndpointId.of("baz"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExposeMatchesWithDifferentCaseShouldMatch() {
|
||||
setupFilter("bar", "");
|
||||
assertThat(match(EndpointId.of("bAr"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenDiscovererDoesNotMatchShouldMatch() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("foo.include", "bar");
|
||||
environment.setProperty("foo.exclude", "");
|
||||
this.filter = new IncludExcludeEndpointFilter<>(DifferentTestExposableWebEndpoint.class, environment, "foo");
|
||||
assertThat(match(EndpointId.of("baz"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenIncludeIsAsteriskShouldMatchAll() {
|
||||
setupFilter("*", "buz");
|
||||
assertThat(match(EndpointId.of("bar"))).isTrue();
|
||||
assertThat(match(EndpointId.of("baz"))).isTrue();
|
||||
assertThat(match(EndpointId.of("buz"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenExcludeIsAsteriskShouldMatchNone() {
|
||||
setupFilter("bar,baz,buz", "*");
|
||||
assertThat(match(EndpointId.of("bar"))).isFalse();
|
||||
assertThat(match(EndpointId.of("baz"))).isFalse();
|
||||
assertThat(match(EndpointId.of("buz"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchWhenMixedCaseShouldMatch() {
|
||||
setupFilter("foo-bar", "");
|
||||
assertThat(match(EndpointId.of("fooBar"))).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-20997
|
||||
void matchWhenDashInName() throws Exception {
|
||||
setupFilter("bus-refresh", "");
|
||||
assertThat(match(EndpointId.of("bus-refresh"))).isTrue();
|
||||
}
|
||||
|
||||
private void setupFilter(String include, String exclude) {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("foo.include", include);
|
||||
environment.setProperty("foo.exclude", exclude);
|
||||
this.filter = new IncludExcludeEndpointFilter<>(TestExposableWebEndpoint.class, environment, "foo", "def");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private boolean match(EndpointId id) {
|
||||
ExposableEndpoint<?> endpoint = mock(TestExposableWebEndpoint.class);
|
||||
given(endpoint.getEndpointId()).willReturn(id);
|
||||
return ((EndpointFilter) this.filter).match(endpoint);
|
||||
}
|
||||
|
||||
abstract static class TestExposableWebEndpoint implements ExposableWebEndpoint {
|
||||
|
||||
}
|
||||
|
||||
abstract static class DifferentTestExposableWebEndpoint implements ExposableWebEndpoint {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
||||
@ -104,7 +104,7 @@ class WebEndpointAutoConfigurationTests {
|
||||
@Test
|
||||
void webApplicationConfiguresExposeExcludePropertyEndpointFilter() {
|
||||
this.contextRunner
|
||||
.run((context) -> assertThat(context).getBeans(ExposeExcludePropertyEndpointFilter.class).containsKeys(
|
||||
.run((context) -> assertThat(context).getBeans(IncludExcludeEndpointFilter.class).containsKeys(
|
||||
"webExposeExcludePropertyEndpointFilter", "controllerExposeExcludePropertyEndpointFilter"));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user