Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ management.endpoint.health.group.live.additional-path="server:/healthz"

This would make the `live` health group available on the main server port at `/healthz`.
The prefix is mandatory and must be either `server:` (represents the main server port) or `management:` (represents the management port, if configured.)
The path must be a single path segment.
The path can contain one or more path segments.



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ void groupIsAvailableAtAdditionalPath() {
.run(withWebTestClient(this::testResponse, "local.server.port"));
}

@Test
void groupIsAvailableAtAdditionalPathWithMultipleSegments() {
this.runner
.withPropertyValues("management.endpoint.health.group.live.include=diskSpace",
"management.endpoint.health.group.live.additional-path=server:/myBasePath/health",
"management.endpoint.health.group.live.show-components=always")
.run(withWebTestClient((client) -> testResponses(client, "/myBasePath/health"), "local.server.port"));
}

@Test
void multipleGroupsAreAvailableAtAdditionalPaths() {
this.runner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Wan bin yu
* @since 4.0.0
*/
public final class AdditionalHealthEndpointPath {
Expand Down Expand Up @@ -99,8 +100,8 @@ public String toString() {

/**
* Creates an {@link AdditionalHealthEndpointPath} from the given input. The input
* must contain a prefix and value separated by a `:`. The value must be limited to
* one path segment. For example, `server:/healthz`.
* must contain a prefix and value separated by a `:`. The value can contain one or
* more path segments. For example, `server:/healthz`.
* @param value the value to parse
* @return the new instance
*/
Expand All @@ -110,7 +111,6 @@ public static AdditionalHealthEndpointPath from(String value) {
Assert.isTrue(values.length == 2, "'value' must contain a valid namespace and value separated by ':'.");
Assert.isTrue(StringUtils.hasText(values[0]), "'value' must contain a valid namespace.");
WebServerNamespace namespace = WebServerNamespace.from(values[0]);
validateValue(values[1]);
return new AdditionalHealthEndpointPath(namespace, values[1]);
}

Expand All @@ -124,13 +124,7 @@ public static AdditionalHealthEndpointPath from(String value) {
public static AdditionalHealthEndpointPath of(WebServerNamespace webServerNamespace, String value) {
Assert.notNull(webServerNamespace, "'webServerNamespace' must not be null.");
Assert.notNull(value, "'value' must not be null.");
validateValue(value);
return new AdditionalHealthEndpointPath(webServerNamespace, value);
}

private static void validateValue(String value) {
Assert.isTrue(StringUtils.countOccurrencesOf(value, "/") <= 1 && value.indexOf("/") <= 0,
"'value' must contain only one segment.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* @param <D> the descriptor type
* @author Phillip Webb
* @author Scott Frederick
* @author Wan bin yu
*/
abstract class HealthEndpointSupport<H, D> {

Expand Down Expand Up @@ -75,19 +76,29 @@ abstract class HealthEndpointSupport<H, D> {

@Nullable Result<D> getResult(ApiVersion apiVersion, @Nullable WebServerNamespace serverNamespace,
SecurityContext securityContext, boolean showAll, String... path) {
HealthEndpointGroup group = (path.length > 0) ? getGroup(serverNamespace, path) : null;
if (group != null) {
return getResult(apiVersion, group, securityContext, showAll, path, 1);
GroupMatch groupMatch = (path.length > 0) ? getGroup(serverNamespace, path) : null;
if (groupMatch != null) {
return getResult(apiVersion, groupMatch.group(), securityContext, showAll, path, groupMatch.pathOffset());
}
return getResult(apiVersion, this.groups.getPrimary(), securityContext, showAll, path, 0);
}

private @Nullable HealthEndpointGroup getGroup(@Nullable WebServerNamespace serverNamespace, String... path) {
if (this.groups.get(path[0]) != null) {
return this.groups.get(path[0]);
private @Nullable GroupMatch getGroup(@Nullable WebServerNamespace serverNamespace, String... path) {
HealthEndpointGroup group = this.groups.get(path[0]);
if (group != null) {
return new GroupMatch(group, 1);
}
if (serverNamespace != null) {
return this.groups.get(AdditionalHealthEndpointPath.of(serverNamespace, path[0]));
StringBuilder additionalPath = new StringBuilder();
GroupMatch groupMatch = null;
for (int i = 0; i < path.length; i++) {
additionalPath.append((i != 0) ? "/" : "").append(path[i]);
group = this.groups.get(AdditionalHealthEndpointPath.of(serverNamespace, additionalPath.toString()));
if (group != null) {
groupMatch = new GroupMatch(group, i + 1);
}
}
return groupMatch;
}
return null;
}
Expand Down Expand Up @@ -210,4 +221,8 @@ record Result<D>(D descriptor, HealthEndpointGroup group) {

}

private record GroupMatch(HealthEndpointGroup group, int pathOffset) {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ void fromPathWithEmptyNamespaceShouldThrowException() {
}

@Test
void fromPathWithMultipleSegmentsShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AdditionalHealthEndpointPath.from("server:/my-path/my-sub-path"));
void fromPathWithMultipleSegmentsShouldCreatePath() {
AdditionalHealthEndpointPath path = AdditionalHealthEndpointPath.from("server:/my-path/my-sub-path");
assertThat(path.getValue()).isEqualTo("/my-path/my-sub-path");
assertThat(path.getNamespace()).isEqualTo(WebServerNamespace.SERVER);
}

@Test
void fromPathWithMultipleSegmentsNotStartingWithSlashShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AdditionalHealthEndpointPath.from("server:my-path/my-sub-path"));
void fromPathWithMultipleSegmentsNotStartingWithSlashShouldCreatePath() {
AdditionalHealthEndpointPath path = AdditionalHealthEndpointPath.from("server:my-path/my-sub-path");
assertThat(path.getValue()).isEqualTo("my-path/my-sub-path");
assertThat(path.getNamespace()).isEqualTo(WebServerNamespace.SERVER);
}

@Test
Expand Down Expand Up @@ -109,9 +111,11 @@ void ofWithNullPathShouldThrowException() {
}

@Test
void ofWithMultipleSegmentValueShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AdditionalHealthEndpointPath.of(WebServerNamespace.SERVER, "/my-path/my-subpath"));
void ofWithMultipleSegmentValueShouldCreatePath() {
AdditionalHealthEndpointPath additionalPath = AdditionalHealthEndpointPath.of(WebServerNamespace.SERVER,
"/my-path/my-subpath");
assertThat(additionalPath.getValue()).isEqualTo("/my-path/my-subpath");
assertThat(additionalPath.getNamespace()).isEqualTo(WebServerNamespace.SERVER);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,20 @@ void getResultWhenGroupHasAdditionalPath() {
assertThat(descriptor.getComponents()).containsKey("test");
}

@Test
void getResultWhenGroupHasMultiSegmentAdditionalPath() {
R registry = createRegistry("test", createContributor(this.up));
TestHealthEndpointGroup testGroup = new TestHealthEndpointGroup((name) -> name.startsWith("test"));
testGroup.setAdditionalPath(AdditionalHealthEndpointPath.from("server:/myBasePath/health"));
HealthEndpointGroups groups = HealthEndpointGroups.of(this.primaryGroup, Map.of("testGroup", testGroup));
E endpoint = create(registry, groups);
Result<D> result = endpoint.getResult(ApiVersion.V3, WebServerNamespace.SERVER, SecurityContext.NONE, false,
"myBasePath", "health");
assertThat(result).isNotNull();
CompositeHealthDescriptor descriptor = (CompositeHealthDescriptor) getDescriptor(result);
assertThat(descriptor.getComponents()).containsKey("test");
}

@Test
void getResultWhenGroupHasAdditionalPathAndShowComponentsFalse() {
R registry = createRegistry("test", createContributor(this.up));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,9 @@ private Map<String, Object> getArguments(HttpServletRequest request, @Nullable M
private Object getRemainingPathSegments(HttpServletRequest request) {
String[] pathTokens = tokenize(request, HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, true);
String[] patternTokens = tokenize(request, HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, false);
if (pathTokens.length == patternTokens.length) {
return pathTokens;
}
int numberOfRemainingPathSegments = pathTokens.length - patternTokens.length + 1;
Assert.state(numberOfRemainingPathSegments >= 0, "Unable to extract remaining path segments");
String[] remainingPathSegments = new String[numberOfRemainingPathSegments];
Expand Down