Skip to content

Commit 6a4d98a

Browse files
committed
Allow exposure patterns to match dashed IDs
Update `ExposeExcludePropertyEndpointFilter` so that patterns will also match endpoint IDs that contain a dash. Closes gh-20997
1 parent 822d9f6 commit 6a4d98a

File tree

2 files changed

+68
-36
lines changed

2 files changed

+68
-36
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,10 +20,9 @@
2020
import java.util.Arrays;
2121
import java.util.Collection;
2222
import java.util.Collections;
23+
import java.util.LinkedHashSet;
2324
import java.util.List;
24-
import java.util.Locale;
2525
import java.util.Set;
26-
import java.util.stream.Collectors;
2726

2827
import org.springframework.boot.actuate.endpoint.EndpointFilter;
2928
import org.springframework.boot.actuate.endpoint.EndpointId;
@@ -35,7 +34,7 @@
3534

3635
/**
3736
* {@link EndpointFilter} that will filter endpoints based on {@code include} and
38-
* {@code exclude} properties.
37+
* {@code exclude} patterns.
3938
*
4039
* @param <E> the endpoint type
4140
* @author Phillip Webb
@@ -45,11 +44,11 @@ public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>>
4544

4645
private final Class<E> endpointType;
4746

48-
private final Set<String> include;
47+
private final EndpointPatterns include;
4948

50-
private final Set<String> exclude;
49+
private final EndpointPatterns exclude;
5150

52-
private final Set<String> exposeDefaults;
51+
private final EndpointPatterns exposeDefaults;
5352

5453
public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, Environment environment, String prefix,
5554
String... exposeDefaults) {
@@ -58,37 +57,22 @@ public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, Environment en
5857
Assert.hasText(prefix, "Prefix must not be empty");
5958
Binder binder = Binder.get(environment);
6059
this.endpointType = endpointType;
61-
this.include = bind(binder, prefix + ".include");
62-
this.exclude = bind(binder, prefix + ".exclude");
63-
this.exposeDefaults = asSet(Arrays.asList(exposeDefaults));
60+
this.include = new EndpointPatterns(bind(binder, prefix + ".include"));
61+
this.exclude = new EndpointPatterns(bind(binder, prefix + ".exclude"));
62+
this.exposeDefaults = new EndpointPatterns(exposeDefaults);
6463
}
6564

6665
public ExposeExcludePropertyEndpointFilter(Class<E> endpointType, Collection<String> include,
6766
Collection<String> exclude, String... exposeDefaults) {
6867
Assert.notNull(endpointType, "EndpointType Type must not be null");
6968
this.endpointType = endpointType;
70-
this.include = asSet(include);
71-
this.exclude = asSet(exclude);
72-
this.exposeDefaults = asSet(Arrays.asList(exposeDefaults));
69+
this.include = new EndpointPatterns(include);
70+
this.exclude = new EndpointPatterns(exclude);
71+
this.exposeDefaults = new EndpointPatterns(exposeDefaults);
7372
}
7473

75-
private Set<String> bind(Binder binder, String name) {
76-
return asSet(binder.bind(name, Bindable.listOf(String.class)).map(this::cleanup).orElseGet(ArrayList::new));
77-
}
78-
79-
private List<String> cleanup(List<String> values) {
80-
return values.stream().map(this::cleanup).collect(Collectors.toList());
81-
}
82-
83-
private String cleanup(String value) {
84-
return "*".equals(value) ? "*" : EndpointId.fromPropertyValue(value).toLowerCaseString();
85-
}
86-
87-
private Set<String> asSet(Collection<String> items) {
88-
if (items == null) {
89-
return Collections.emptySet();
90-
}
91-
return items.stream().map((item) -> item.toLowerCase(Locale.ENGLISH)).collect(Collectors.toSet());
74+
private List<String> bind(Binder binder, String name) {
75+
return binder.bind(name, Bindable.listOf(String.class)).orElseGet(ArrayList::new);
9276
}
9377

9478
@Override
@@ -101,20 +85,62 @@ public boolean match(E endpoint) {
10185

10286
private boolean isExposed(ExposableEndpoint<?> endpoint) {
10387
if (this.include.isEmpty()) {
104-
return this.exposeDefaults.contains("*") || contains(this.exposeDefaults, endpoint);
88+
return this.exposeDefaults.matchesAll() || this.exposeDefaults.matches(endpoint);
10589
}
106-
return this.include.contains("*") || contains(this.include, endpoint);
90+
return this.include.matchesAll() || this.include.matches(endpoint);
10791
}
10892

10993
private boolean isExcluded(ExposableEndpoint<?> endpoint) {
11094
if (this.exclude.isEmpty()) {
11195
return false;
11296
}
113-
return this.exclude.contains("*") || contains(this.exclude, endpoint);
97+
return this.exclude.matchesAll() || this.exclude.matches(endpoint);
11498
}
11599

116-
private boolean contains(Set<String> items, ExposableEndpoint<?> endpoint) {
117-
return items.contains(endpoint.getEndpointId().toLowerCaseString());
100+
/**
101+
* A set of endpoint patterns used to match IDs.
102+
*/
103+
private static class EndpointPatterns {
104+
105+
private final boolean empty;
106+
107+
private final boolean matchesAll;
108+
109+
private final Set<EndpointId> endpointIds;
110+
111+
EndpointPatterns(String[] patterns) {
112+
this((patterns != null) ? Arrays.asList(patterns) : (Collection<String>) null);
113+
}
114+
115+
EndpointPatterns(Collection<String> patterns) {
116+
patterns = (patterns != null) ? patterns : Collections.emptySet();
117+
boolean matchesAll = false;
118+
Set<EndpointId> endpointIds = new LinkedHashSet<>();
119+
for (String pattern : patterns) {
120+
if ("*".equals(pattern)) {
121+
matchesAll = true;
122+
}
123+
else {
124+
endpointIds.add(EndpointId.fromPropertyValue(pattern));
125+
}
126+
}
127+
this.empty = patterns.isEmpty();
128+
this.matchesAll = matchesAll;
129+
this.endpointIds = endpointIds;
130+
}
131+
132+
boolean isEmpty() {
133+
return this.empty;
134+
}
135+
136+
boolean matchesAll() {
137+
return this.matchesAll;
138+
}
139+
140+
boolean matches(ExposableEndpoint<?> endpoint) {
141+
return this.endpointIds.contains(endpoint.getEndpointId());
142+
}
143+
118144
}
119145

120146
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/ExposeExcludePropertyEndpointFilterTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -147,6 +147,12 @@ public void matchWhenMixedCaseShouldMatch() {
147147
assertThat(match(EndpointId.of("fooBar"))).isTrue();
148148
}
149149

150+
@Test // gh-20997
151+
public void matchWhenDashInName() throws Exception {
152+
setupFilter("bus-refresh", "");
153+
assertThat(match(EndpointId.of("bus-refresh"))).isTrue();
154+
}
155+
150156
private void setupFilter(String include, String exclude) {
151157
MockEnvironment environment = new MockEnvironment();
152158
environment.setProperty("foo.include", include);

0 commit comments

Comments
 (0)