Skip to content

Commit 0866461

Browse files
committed
Merge branch '3.4.x'
Closes gh-45803
2 parents 6c992aa + 6f1216c commit 0866461

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,11 @@ class OnAvailableEndpointCondition extends SpringBootCondition {
7171

7272
@Override
7373
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
74-
Environment environment = context.getEnvironment();
7574
MergedAnnotation<ConditionalOnAvailableEndpoint> conditionAnnotation = metadata.getAnnotations()
7675
.get(ConditionalOnAvailableEndpoint.class);
7776
Class<?> target = getTarget(context, metadata, conditionAnnotation);
7877
MergedAnnotation<Endpoint> endpointAnnotation = getEndpointAnnotation(target);
79-
return getMatchOutcome(environment, conditionAnnotation, endpointAnnotation);
78+
return getMatchOutcome(context, conditionAnnotation, endpointAnnotation);
8079
}
8180

8281
private Class<?> getTarget(ConditionContext context, AnnotatedTypeMetadata metadata,
@@ -109,16 +108,17 @@ protected MergedAnnotation<Endpoint> getEndpointAnnotation(Class<?> target) {
109108
return getEndpointAnnotation(extension.getClass("endpoint"));
110109
}
111110

112-
private ConditionOutcome getMatchOutcome(Environment environment,
111+
private ConditionOutcome getMatchOutcome(ConditionContext context,
113112
MergedAnnotation<ConditionalOnAvailableEndpoint> conditionAnnotation,
114113
MergedAnnotation<Endpoint> endpointAnnotation) {
115114
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnAvailableEndpoint.class);
115+
Environment environment = context.getEnvironment();
116116
EndpointId endpointId = EndpointId.of(environment, endpointAnnotation.getString("id"));
117117
ConditionOutcome accessOutcome = getAccessOutcome(environment, endpointAnnotation, endpointId, message);
118118
if (!accessOutcome.isMatch()) {
119119
return accessOutcome;
120120
}
121-
ConditionOutcome exposureOutcome = getExposureOutcome(environment, conditionAnnotation, endpointAnnotation,
121+
ConditionOutcome exposureOutcome = getExposureOutcome(context, conditionAnnotation, endpointAnnotation,
122122
endpointId, message);
123123
return (exposureOutcome != null) ? exposureOutcome : ConditionOutcome.noMatch(message.because("not exposed"));
124124
}
@@ -137,11 +137,11 @@ private Access getAccess(Environment environment, EndpointId endpointId, Access
137137
.accessFor(endpointId, defaultAccess);
138138
}
139139

140-
private ConditionOutcome getExposureOutcome(Environment environment,
140+
private ConditionOutcome getExposureOutcome(ConditionContext context,
141141
MergedAnnotation<ConditionalOnAvailableEndpoint> conditionAnnotation,
142142
MergedAnnotation<Endpoint> endpointAnnotation, EndpointId endpointId, Builder message) {
143143
Set<EndpointExposure> exposures = getExposures(conditionAnnotation);
144-
Set<EndpointExposureOutcomeContributor> outcomeContributors = getExposureOutcomeContributors(environment);
144+
Set<EndpointExposureOutcomeContributor> outcomeContributors = getExposureOutcomeContributors(context);
145145
for (EndpointExposureOutcomeContributor outcomeContributor : outcomeContributors) {
146146
ConditionOutcome outcome = outcomeContributor.getExposureOutcome(endpointId, exposures, message);
147147
if (outcome != null && outcome.isMatch()) {
@@ -166,23 +166,25 @@ private Set<EndpointExposure> replaceCloudFoundryExposure(Collection<EndpointExp
166166
return result;
167167
}
168168

169-
private Set<EndpointExposureOutcomeContributor> getExposureOutcomeContributors(Environment environment) {
169+
private Set<EndpointExposureOutcomeContributor> getExposureOutcomeContributors(ConditionContext context) {
170+
Environment environment = context.getEnvironment();
170171
Set<EndpointExposureOutcomeContributor> contributors = exposureOutcomeContributorsCache.get(environment);
171172
if (contributors == null) {
172173
contributors = new LinkedHashSet<>();
173174
contributors.add(new StandardExposureOutcomeContributor(environment, EndpointExposure.WEB));
174175
if (environment.getProperty(JMX_ENABLED_KEY, Boolean.class, false)) {
175176
contributors.add(new StandardExposureOutcomeContributor(environment, EndpointExposure.JMX));
176177
}
177-
contributors.addAll(loadExposureOutcomeContributors(environment));
178+
contributors.addAll(loadExposureOutcomeContributors(context.getClassLoader(), environment));
178179
exposureOutcomeContributorsCache.put(environment, contributors);
179180
}
180181
return contributors;
181182
}
182183

183-
private List<EndpointExposureOutcomeContributor> loadExposureOutcomeContributors(Environment environment) {
184+
private List<EndpointExposureOutcomeContributor> loadExposureOutcomeContributors(ClassLoader classLoader,
185+
Environment environment) {
184186
ArgumentResolver argumentResolver = ArgumentResolver.of(Environment.class, environment);
185-
return SpringFactoriesLoader.forDefaultResourceLocation()
187+
return SpringFactoriesLoader.forDefaultResourceLocation(classLoader)
186188
.load(EndpointExposureOutcomeContributor.class, argumentResolver);
187189
}
188190

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ void whenDisabledAndAccessibleByDefaultEndpointCanBeAvailable() {
294294
.run((context) -> assertThat(context).hasSingleBean(DisabledButAccessibleEndpoint.class));
295295
}
296296

297+
@Test
298+
@WithTestEndpointOutcomeExposureContributor
299+
void exposureOutcomeContributorCanMakeEndpointAvailable() {
300+
this.contextRunner.withPropertyValues("management.endpoints.test.exposure.include=test")
301+
.run((context) -> assertThat(context).hasSingleBean(TestEndpoint.class));
302+
}
303+
297304
@Endpoint(id = "health")
298305
static class HealthEndpoint {
299306

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
import java.util.Set;
25+
26+
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;
27+
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter;
28+
import org.springframework.boot.actuate.endpoint.EndpointId;
29+
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
30+
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
31+
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
32+
import org.springframework.boot.testsupport.classpath.resources.WithResource;
33+
import org.springframework.core.env.Environment;
34+
import org.springframework.core.io.support.SpringFactoriesLoader;
35+
36+
/**
37+
* Makes a test {@link EndpointExposureOutcomeContributor} available via
38+
* {@link SpringFactoriesLoader}.
39+
*
40+
* @author Andy Wilkinson
41+
*/
42+
@Inherited
43+
@Retention(RetentionPolicy.RUNTIME)
44+
@Target(ElementType.METHOD)
45+
@WithResource(name = "META-INF/spring.factories",
46+
content = """
47+
org.springframework.boot.actuate.autoconfigure.endpoint.condition.EndpointExposureOutcomeContributor=\
48+
org.springframework.boot.actuate.autoconfigure.endpoint.condition.WithTestEndpointOutcomeExposureContributor.TestEndpointExposureOutcomeContributor
49+
""")
50+
public @interface WithTestEndpointOutcomeExposureContributor {
51+
52+
class TestEndpointExposureOutcomeContributor implements EndpointExposureOutcomeContributor {
53+
54+
private final IncludeExcludeEndpointFilter<?> filter;
55+
56+
TestEndpointExposureOutcomeContributor(Environment environment) {
57+
this.filter = new IncludeExcludeEndpointFilter<>(ExposableEndpoint.class, environment,
58+
"management.endpoints.test.exposure");
59+
}
60+
61+
@Override
62+
public ConditionOutcome getExposureOutcome(EndpointId endpointId, Set<EndpointExposure> exposures,
63+
Builder message) {
64+
if (this.filter.match(endpointId)) {
65+
return ConditionOutcome.match();
66+
}
67+
return null;
68+
}
69+
70+
}
71+
72+
}

0 commit comments

Comments
 (0)