diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java index 84c39eade7..24736e2647 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java @@ -197,7 +197,7 @@ class JerseyWebEndpointManagementContextConfiguration { JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory( WebServerNamespace.MANAGEMENT, this.groups); Collection endpointResources = resourceFactory - .createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false) + .createEndpointResources(mapping, Collections.singletonList(this.endpoint)) .stream() .filter(Objects::nonNull) .toList(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java index 6a027585c7..b0924d9280 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java @@ -162,7 +162,7 @@ class HealthEndpointWebExtensionConfiguration { JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory( WebServerNamespace.SERVER, this.groups); Collection endpointResources = resourceFactory - .createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false) + .createEndpointResources(mapping, Collections.singletonList(this.endpoint)) .stream() .filter(Objects::nonNull) .toList(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java index 5e31018325..b0d41c2a9b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java @@ -27,6 +27,8 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient; +import static org.assertj.core.api.Assertions.assertThatNoException; + /** * Abstract base class for health groups with an additional path. * @@ -52,6 +54,18 @@ abstract class AbstractHealthEndpointAdditionalPathIntegrationTests testResponses(client, "/alpha", "/bravo"), "local.server.port")); + } + @Test void groupIsAvailableAtAdditionalPathWithoutSlash() { this.runner @@ -125,17 +139,24 @@ abstract class AbstractHealthEndpointAdditionalPathIntegrationTests client.get() + .uri(path) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("status") + .isEqualTo("UP") + .jsonPath("components.diskSpace") + .exists()); + } } private ContextConsumer withWebTestClient(Consumer consumer, String property) { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyHealthEndpointAdditionalPathResourceFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyHealthEndpointAdditionalPathResourceFactory.java index e5253de2da..9e744585c2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyHealthEndpointAdditionalPathResourceFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyHealthEndpointAdditionalPathResourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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,11 +16,17 @@ package org.springframework.boot.actuate.endpoint.web.jersey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.glassfish.jersey.server.model.Resource; import org.springframework.boot.actuate.endpoint.web.EndpointMapping; +import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; @@ -35,7 +41,9 @@ import org.springframework.boot.actuate.health.HealthEndpointGroups; * @author Madhura Bhave * @since 2.6.0 */ -public class JerseyHealthEndpointAdditionalPathResourceFactory extends JerseyEndpointResourceFactory { +public final class JerseyHealthEndpointAdditionalPathResourceFactory { + + private final JerseyEndpointResourceFactory delegate = new JerseyEndpointResourceFactory(); private final Set groups; @@ -47,20 +55,30 @@ public class JerseyHealthEndpointAdditionalPathResourceFactory extends JerseyEnd this.groups = groups.getAllWithAdditionalPath(serverNamespace); } - @Override - protected Resource createResource(EndpointMapping endpointMapping, WebOperation operation) { + public Collection createEndpointResources(EndpointMapping endpointMapping, + Collection endpoints) { + return endpoints.stream() + .flatMap((endpoint) -> endpoint.getOperations().stream()) + .flatMap((operation) -> createResources(endpointMapping, operation)) + .collect(Collectors.toList()); + } + + private Stream createResources(EndpointMapping endpointMapping, WebOperation operation) { WebOperationRequestPredicate requestPredicate = operation.getRequestPredicate(); String matchAllRemainingPathSegmentsVariable = requestPredicate.getMatchAllRemainingPathSegmentsVariable(); if (matchAllRemainingPathSegmentsVariable != null) { + List resources = new ArrayList<>(); for (HealthEndpointGroup group : this.groups) { AdditionalHealthEndpointPath additionalPath = group.getAdditionalPath(); if (additionalPath != null) { - return getResource(endpointMapping, operation, requestPredicate, additionalPath.getValue(), - this.serverNamespace, (data, pathSegmentsVariable) -> data.getUriInfo().getPath()); + resources.add(this.delegate.getResource(endpointMapping, operation, requestPredicate, + additionalPath.getValue(), this.serverNamespace, + (data, pathSegmentsVariable) -> data.getUriInfo().getPath())); } } + return resources.stream(); } - return null; + return Stream.empty(); } }