Skip to content

Commit 52f7329

Browse files
committed
Support multiple health groups with an additional path with Jersey
This commit knowingly makes breaking API changes to JerseyHealthEndpointAdditionalPathResourceFactory. We considered other options but they all had the potential to be backwards incompatible in one way or another. Faced with that situation we concluded that the likelihood of anyone using the modified API directly is small enough to warrant making the breaking changes. If it becomes apparent that we have misjudged things we can revisit the changes in the future. Closes gh-36250
1 parent 76cd102 commit 52f7329

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private void register(ResourceConfig config) {
184184
JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory(
185185
WebServerNamespace.MANAGEMENT, this.groups);
186186
Collection<Resource> endpointResources = resourceFactory
187-
.createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false)
187+
.createEndpointResources(mapping, Collections.singletonList(this.endpoint))
188188
.stream()
189189
.filter(Objects::nonNull)
190190
.collect(Collectors.toList());

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ private void register(ResourceConfig config) {
163163
JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory(
164164
WebServerNamespace.SERVER, this.groups);
165165
Collection<Resource> endpointResources = resourceFactory
166-
.createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false)
166+
.createEndpointResources(mapping, Collections.singletonList(this.endpoint))
167167
.stream()
168168
.filter(Objects::nonNull)
169169
.collect(Collectors.toList());

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.springframework.http.MediaType;
2828
import org.springframework.test.web.reactive.server.WebTestClient;
2929

30+
import static org.assertj.core.api.Assertions.assertThatNoException;
31+
3032
/**
3133
* Abstract base class for health groups with an additional path.
3234
*
@@ -52,6 +54,18 @@ void groupIsAvailableAtAdditionalPath() {
5254
.run(withWebTestClient(this::testResponse, "local.server.port"));
5355
}
5456

57+
@Test
58+
void multipleGroupsAreAvailableAtAdditionalPaths() {
59+
this.runner
60+
.withPropertyValues("management.endpoint.health.group.one.include=diskSpace",
61+
"management.endpoint.health.group.two.include=diskSpace",
62+
"management.endpoint.health.group.one.additional-path=server:/alpha",
63+
"management.endpoint.health.group.two.additional-path=server:/bravo",
64+
"management.endpoint.health.group.one.show-components=always",
65+
"management.endpoint.health.group.two.show-components=always")
66+
.run(withWebTestClient((client) -> testResponses(client, "/alpha", "/bravo"), "local.server.port"));
67+
}
68+
5569
@Test
5670
void groupIsAvailableAtAdditionalPathWithoutSlash() {
5771
this.runner
@@ -125,17 +139,24 @@ void groupsAreNotConfiguredWhenHealthEndpointIsNotExposedWithDifferentManagement
125139
}
126140

127141
private void testResponse(WebTestClient client) {
128-
client.get()
129-
.uri("/healthz")
130-
.accept(MediaType.APPLICATION_JSON)
131-
.exchange()
132-
.expectStatus()
133-
.isOk()
134-
.expectBody()
135-
.jsonPath("status")
136-
.isEqualTo("UP")
137-
.jsonPath("components.diskSpace")
138-
.exists();
142+
testResponses(client, "/healthz");
143+
}
144+
145+
private void testResponses(WebTestClient client, String... paths) {
146+
for (String path : paths) {
147+
assertThatNoException().as(path)
148+
.isThrownBy(() -> client.get()
149+
.uri(path)
150+
.accept(MediaType.APPLICATION_JSON)
151+
.exchange()
152+
.expectStatus()
153+
.isOk()
154+
.expectBody()
155+
.jsonPath("status")
156+
.isEqualTo("UP")
157+
.jsonPath("components.diskSpace")
158+
.exists());
159+
}
139160
}
140161

141162
private ContextConsumer<A> withWebTestClient(Consumer<WebTestClient> consumer, String property) {

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyHealthEndpointAdditionalPathResourceFactory.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2023 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.
@@ -16,11 +16,17 @@
1616

1717
package org.springframework.boot.actuate.endpoint.web.jersey;
1818

19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.List;
1922
import java.util.Set;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
2025

2126
import org.glassfish.jersey.server.model.Resource;
2227

2328
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
29+
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
2430
import org.springframework.boot.actuate.endpoint.web.WebOperation;
2531
import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate;
2632
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
@@ -35,7 +41,9 @@
3541
* @author Madhura Bhave
3642
* @since 2.6.0
3743
*/
38-
public class JerseyHealthEndpointAdditionalPathResourceFactory extends JerseyEndpointResourceFactory {
44+
public final class JerseyHealthEndpointAdditionalPathResourceFactory {
45+
46+
private final JerseyEndpointResourceFactory delegate = new JerseyEndpointResourceFactory();
3947

4048
private final Set<HealthEndpointGroup> groups;
4149

@@ -47,20 +55,30 @@ public JerseyHealthEndpointAdditionalPathResourceFactory(WebServerNamespace serv
4755
this.groups = groups.getAllWithAdditionalPath(serverNamespace);
4856
}
4957

50-
@Override
51-
protected Resource createResource(EndpointMapping endpointMapping, WebOperation operation) {
58+
public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping,
59+
Collection<ExposableWebEndpoint> endpoints) {
60+
return endpoints.stream()
61+
.flatMap((endpoint) -> endpoint.getOperations().stream())
62+
.flatMap((operation) -> createResources(endpointMapping, operation))
63+
.collect(Collectors.toList());
64+
}
65+
66+
private Stream<Resource> createResources(EndpointMapping endpointMapping, WebOperation operation) {
5267
WebOperationRequestPredicate requestPredicate = operation.getRequestPredicate();
5368
String matchAllRemainingPathSegmentsVariable = requestPredicate.getMatchAllRemainingPathSegmentsVariable();
5469
if (matchAllRemainingPathSegmentsVariable != null) {
70+
List<Resource> resources = new ArrayList<>();
5571
for (HealthEndpointGroup group : this.groups) {
5672
AdditionalHealthEndpointPath additionalPath = group.getAdditionalPath();
5773
if (additionalPath != null) {
58-
return getResource(endpointMapping, operation, requestPredicate, additionalPath.getValue(),
59-
this.serverNamespace, (data, pathSegmentsVariable) -> data.getUriInfo().getPath());
74+
resources.add(this.delegate.getResource(endpointMapping, operation, requestPredicate,
75+
additionalPath.getValue(), this.serverNamespace,
76+
(data, pathSegmentsVariable) -> data.getUriInfo().getPath()));
6077
}
6178
}
79+
return resources.stream();
6280
}
63-
return null;
81+
return Stream.empty();
6482
}
6583

6684
}

0 commit comments

Comments
 (0)