diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java index 5a3307157ab..0df6400d0a0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java @@ -60,7 +60,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH Collection endpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor, EndpointLinksResolver linksResolver) { - super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration); + super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true); this.linksResolver = linksResolver; this.securityInterceptor = securityInterceptor; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java index 4f692456ed2..bb2331e7bc2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java @@ -59,7 +59,7 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin Collection endpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor, EndpointLinksResolver linksResolver) { - super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration); + super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true); this.securityInterceptor = securityInterceptor; this.linksResolver = linksResolver; } 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 729f2141f93..42a0c9c0b9d 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 @@ -26,6 +26,7 @@ import org.glassfish.jersey.server.ResourceConfig; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -42,6 +43,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; /** * {@link ManagementContextConfiguration @ManagementContextConfiguration} for Jersey @@ -62,7 +65,7 @@ class JerseyWebEndpointManagementContextConfiguration { @Bean ResourceConfigCustomizer webEndpointRegistrar(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, - WebEndpointProperties webEndpointProperties) { + WebEndpointProperties webEndpointProperties, Environment environment) { List> allEndpoints = new ArrayList<>(); allEndpoints.addAll(webEndpointsSupplier.getEndpoints()); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); @@ -73,8 +76,14 @@ class JerseyWebEndpointManagementContextConfiguration { Collection webEndpoints = Collections .unmodifiableCollection(webEndpointsSupplier.getEndpoints()); resourceConfig.registerResources(new HashSet<>(resourceFactory.createEndpointResources(endpointMapping, - webEndpoints, endpointMediaTypes, new EndpointLinksResolver(allEndpoints, basePath)))); + webEndpoints, endpointMediaTypes, new EndpointLinksResolver(allEndpoints, basePath), + shouldRegisterLinksMapping(environment, basePath)))); }; } + private boolean shouldRegisterLinksMapping(Environment environment, String basePath) { + return StringUtils.hasText(basePath) + || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java index e0ef4be2ac0..aa758ae72d7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -40,7 +41,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.util.StringUtils; import org.springframework.web.reactive.DispatcherHandler; /** @@ -62,15 +65,22 @@ public class WebFluxEndpointManagementContextConfiguration { @ConditionalOnMissingBean public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, - CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties) { - EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath()); + CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, + Environment environment) { + String basePath = webEndpointProperties.getBasePath(); + EndpointMapping endpointMapping = new EndpointMapping(basePath); Collection endpoints = webEndpointsSupplier.getEndpoints(); List> allEndpoints = new ArrayList<>(); allEndpoints.addAll(endpoints); allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints()); return new WebFluxEndpointHandlerMapping(endpointMapping, endpoints, endpointMediaTypes, - corsProperties.toCorsConfiguration(), - new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath())); + corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), + shouldRegisterLinksMapping(environment, basePath)); + } + + private boolean shouldRegisterLinksMapping(Environment environment, String basePath) { + return StringUtils.hasText(basePath) + || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java index 023d65c8504..91064d4d5ae 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -41,6 +42,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; import org.springframework.web.servlet.DispatcherServlet; /** @@ -63,16 +66,19 @@ public class WebMvcEndpointManagementContextConfiguration { public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, - WebEndpointProperties webEndpointProperties) { + WebEndpointProperties webEndpointProperties, Environment environment) { List> allEndpoints = new ArrayList<>(); Collection webEndpoints = webEndpointsSupplier.getEndpoints(); allEndpoints.addAll(webEndpoints); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints()); - EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath()); + String basePath = webEndpointProperties.getBasePath(); + EndpointMapping endpointMapping = new EndpointMapping(basePath); + boolean shouldRegisterLinksMapping = StringUtils.hasText(basePath) + || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT); return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, - corsProperties.toCorsConfiguration(), - new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath())); + corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), + shouldRegisterLinksMapping); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java index d4d6b872eb1..c613fc3e512 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java @@ -133,7 +133,7 @@ class JerseyEndpointRequestIntegrationTests extends AbstractEndpointRequestInteg Arrays.asList(EndpointId::toString), Collections.emptyList(), Collections.emptyList()); Collection resources = new JerseyEndpointResourceFactory().createEndpointResources( new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, - new EndpointLinksResolver(discoverer.getEndpoints())); + new EndpointLinksResolver(discoverer.getEndpoints()), true); config.registerResources(new HashSet<>(resources)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java index 5e04dcd57da..0df5dc2f40c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java @@ -119,7 +119,8 @@ class MvcEndpointRequestIntegrationTests extends AbstractEndpointRequestIntegrat new ConversionServiceParameterValueMapper(), endpointMediaTypes, Arrays.asList(EndpointId::toString), Collections.emptyList(), Collections.emptyList()); return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), - endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints())); + endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), + true); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java index 425f08c983c..ed6f61df95f 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java @@ -71,15 +71,16 @@ public class JerseyEndpointResourceFactory { * @param endpoints the web endpoints * @param endpointMediaTypes media types consumed and produced by the endpoints * @param linksResolver resolver for determining links to available endpoints + * @param shouldRegisterLinks should register links * @return the resources for the operations */ public Collection createEndpointResources(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, - EndpointLinksResolver linksResolver) { + EndpointLinksResolver linksResolver, boolean shouldRegisterLinks) { List resources = new ArrayList<>(); endpoints.stream().flatMap((endpoint) -> endpoint.getOperations().stream()) .map((operation) -> createResource(endpointMapping, operation)).forEach(resources::add); - if (StringUtils.hasText(endpointMapping.getPath())) { + if (shouldRegisterLinks) { Resource resource = createEndpointLinksResource(endpointMapping.getPath(), endpointMediaTypes, linksResolver); resources.add(resource); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 8f1fb5128f5..c16e6ebb0e3 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -95,6 +95,8 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private final Method handleReadMethod = ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", ServerWebExchange.class); + private final boolean shouldRegisterLinksMapping; + /** * Creates a new {@code AbstractWebFluxEndpointHandlerMapping} that provides mappings * for the operations of the given {@code webEndpoints}. @@ -102,14 +104,16 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi * @param endpoints the web endpoints * @param endpointMediaTypes media types consumed and produced by the endpoints * @param corsConfiguration the CORS configuration for the endpoints + * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public AbstractWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, - CorsConfiguration corsConfiguration) { + CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) { this.endpointMapping = endpointMapping; this.endpoints = endpoints; this.endpointMediaTypes = endpointMediaTypes; this.corsConfiguration = corsConfiguration; + this.shouldRegisterLinksMapping = shouldRegisterLinksMapping; setOrder(-100); } @@ -120,7 +124,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi registerMappingForOperation(endpoint, operation); } } - if (StringUtils.hasText(this.endpointMapping.getPath())) { + if (this.shouldRegisterLinksMapping) { registerLinksMapping(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java index 4b5e440bd84..2b0a7ae83f0 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java @@ -53,11 +53,12 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle * @param endpointMediaTypes media types consumed and produced by the endpoints * @param corsConfiguration the CORS configuration for the endpoints or {@code null} * @param linksResolver resolver for determining links to available endpoints + * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, - EndpointLinksResolver linksResolver) { - super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration); + EndpointLinksResolver linksResolver, boolean shouldRegisterLinksMapping) { + super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping); this.linksResolver = linksResolver; setOrder(-100); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index e579f19e3ae..8c4783b2d0d 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -81,6 +81,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin private final CorsConfiguration corsConfiguration; + private final boolean shouldRegisterLinksMapping; + private final Method handleMethod = ReflectionUtils.findMethod(OperationHandler.class, "handle", HttpServletRequest.class, Map.class); @@ -92,10 +94,12 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin * @param endpointMapping the base mapping for all endpoints * @param endpoints the web endpoints * @param endpointMediaTypes media types consumed and produced by the endpoints + * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection endpoints, EndpointMediaTypes endpointMediaTypes) { - this(endpointMapping, endpoints, endpointMediaTypes, null); + Collection endpoints, EndpointMediaTypes endpointMediaTypes, + boolean shouldRegisterLinksMapping) { + this(endpointMapping, endpoints, endpointMediaTypes, null, shouldRegisterLinksMapping); } /** @@ -105,14 +109,16 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin * @param endpoints the web endpoints * @param endpointMediaTypes media types consumed and produced by the endpoints * @param corsConfiguration the CORS configuration for the endpoints or {@code null} + * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, - CorsConfiguration corsConfiguration) { + CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) { this.endpointMapping = endpointMapping; this.endpoints = endpoints; this.endpointMediaTypes = endpointMediaTypes; this.corsConfiguration = corsConfiguration; + this.shouldRegisterLinksMapping = shouldRegisterLinksMapping; setOrder(-100); } @@ -123,7 +129,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin registerMappingForOperation(endpoint, operation); } } - if (StringUtils.hasText(this.endpointMapping.getPath())) { + if (this.shouldRegisterLinksMapping) { registerLinksMapping(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java index caa86b3cf2d..350c4a41e97 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java @@ -52,11 +52,12 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM * @param endpointMediaTypes media types consumed and produced by the endpoints * @param corsConfiguration the CORS configuration for the endpoints or {@code null} * @param linksResolver resolver for determining links to available endpoints + * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, - EndpointLinksResolver linksResolver) { - super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration); + EndpointLinksResolver linksResolver, boolean shouldRegisterLinksMapping) { + super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping); this.linksResolver = linksResolver; setOrder(-100); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java index a3c7b90766f..06bfcbebebe 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyWebEndpointIntegrationTests.java @@ -52,6 +52,7 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; /** @@ -106,9 +107,10 @@ public class JerseyWebEndpointIntegrationTests ResourceConfig resourceConfig(Environment environment, WebEndpointDiscoverer endpointDiscoverer, EndpointMediaTypes endpointMediaTypes) { ResourceConfig resourceConfig = new ResourceConfig(); + String endpointPath = environment.getProperty("endpointPath"); Collection resources = new JerseyEndpointResourceFactory().createEndpointResources( - new EndpointMapping(environment.getProperty("endpointPath")), endpointDiscoverer.getEndpoints(), - endpointMediaTypes, new EndpointLinksResolver(endpointDiscoverer.getEndpoints())); + new EndpointMapping(endpointPath), endpointDiscoverer.getEndpoints(), endpointMediaTypes, + new EndpointLinksResolver(endpointDiscoverer.getEndpoints()), StringUtils.hasText(endpointPath)); resourceConfig.registerResources(new HashSet<>(resources)); resourceConfig.register(JacksonFeature.class); resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()), ContextResolver.class); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java index 4b5637dab99..40bae128044 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointIntegrationTests.java @@ -41,6 +41,7 @@ import org.springframework.http.server.reactive.HttpHandler; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.server.WebFilter; @@ -122,9 +123,10 @@ class WebFluxEndpointIntegrationTests CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST")); - return new WebFluxEndpointHandlerMapping(new EndpointMapping(environment.getProperty("endpointPath")), + String endpointPath = environment.getProperty("endpointPath"); + return new WebFluxEndpointHandlerMapping(new EndpointMapping(endpointPath), endpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, - new EndpointLinksResolver(endpointDiscoverer.getEndpoints())); + new EndpointLinksResolver(endpointDiscoverer.getEndpoints()), StringUtils.hasText(endpointPath)); } @Bean diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java index 84f82b400ab..9ff0ed2fcb1 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/MvcWebEndpointIntegrationTests.java @@ -52,6 +52,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; +import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.handler.RequestMatchResult; @@ -145,9 +146,10 @@ class MvcWebEndpointIntegrationTests CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST")); - return new WebMvcEndpointHandlerMapping(new EndpointMapping(environment.getProperty("endpointPath")), + String endpointPath = environment.getProperty("endpointPath"); + return new WebMvcEndpointHandlerMapping(new EndpointMapping(endpointPath), endpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, - new EndpointLinksResolver(endpointDiscoverer.getEndpoints())); + new EndpointLinksResolver(endpointDiscoverer.getEndpoints()), StringUtils.hasText(endpointPath)); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java index 8886f5f7c56..cb62ec73ca8 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java @@ -242,7 +242,7 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation Collections.emptyList()); Collection resources = new JerseyEndpointResourceFactory().createEndpointResources( new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, - new EndpointLinksResolver(discoverer.getEndpoints())); + new EndpointLinksResolver(discoverer.getEndpoints()), true); config.registerResources(new HashSet<>(resources)); } @@ -288,7 +288,8 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), Collections.emptyList()); return new WebFluxEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), - endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints())); + endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), + true); } } @@ -317,7 +318,8 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), Collections.emptyList()); return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), - endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints())); + endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), + true); } } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..fc32f7f942f --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.actuator; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports with empty endpoint + * base path. + * + * @author HaiTao Zhang + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "management.endpoints.web.base-path=/", "management.server.port=0" }) +class ManagementDifferentPortSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @Test + void linksEndpointShouldBeAvailable() { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + + private String getPassword() { + return "password"; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..8cc580e0a26 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.jersey; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports with empty base path + * for endpoints. + * + * @author HaiTao Zhang + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "management.server.port=0", "management.endpoints.web.base-path=/" }) +class JerseyDifferentPortSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @Test + void linksEndpointShouldBeAvailable() { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + + private String getPassword() { + return "password"; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..d0450b91707 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.webflux; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports with empty endpoint + * base path. + * + * @author HaiTao Zhang + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "management.server.port=0", "management.endpoints.web.base-path=/" }) +class WebFluxDifferentPortSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @Test + void linksEndpointShouldBeAvailable() { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + + private String getPassword() { + return "password"; + } + +}