diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
index d0151ebc913..2de8b044da4 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
@@ -410,6 +410,11 @@
hsqldb
test
+
+ org.glassfish.jersey.ext
+ jersey-spring4
+ test
+
org.glassfish.jersey.media
jersey-media-json-jackson
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 d0aed7f3a51..1cee0e313f7 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
@@ -54,13 +54,15 @@ class CloudFoundryWebFluxEndpointHandlerMapping
private final CloudFoundrySecurityInterceptor securityInterceptor;
- private final EndpointLinksResolver linksResolver = new EndpointLinksResolver();
+ private final EndpointLinksResolver linksResolver;
CloudFoundryWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection endpoints,
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
- CloudFoundrySecurityInterceptor securityInterceptor) {
+ CloudFoundrySecurityInterceptor securityInterceptor,
+ EndpointLinksResolver linksResolver) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration);
+ this.linksResolver = linksResolver;
this.securityInterceptor = securityInterceptor;
}
@@ -83,7 +85,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping
AccessLevel accessLevel = exchange
.getAttribute(AccessLevel.REQUEST_ATTRIBUTE);
Map links = this.linksResolver
- .resolveLinks(getEndpoints(), request.getURI().toString());
+ .resolveLinks(request.getURI().toString());
return new ResponseEntity<>(
Collections.singletonMap("_links",
getAccessibleLinks(accessLevel, links)),
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
index 8eae3c7ceb6..0e4edfa56c9 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
@@ -16,18 +16,25 @@
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryWebEndpointDiscoverer;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
+import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
+import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -85,17 +92,22 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
@Bean
public CloudFoundryWebFluxEndpointHandlerMapping cloudFoundryWebFluxEndpointHandlerMapping(
ParameterValueMapper parameterMapper, EndpointMediaTypes endpointMediaTypes,
- WebClient.Builder webClientBuilder) {
+ WebClient.Builder webClientBuilder,
+ ControllerEndpointsSupplier controllerEndpointsSupplier) {
CloudFoundryWebEndpointDiscoverer endpointDiscoverer = new CloudFoundryWebEndpointDiscoverer(
this.applicationContext, parameterMapper, endpointMediaTypes,
PathMapper.useEndpointId(), Collections.emptyList(),
Collections.emptyList());
CloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor(
webClientBuilder, this.applicationContext.getEnvironment());
+ Collection webEndpoints = endpointDiscoverer.getEndpoints();
+ List> allEndpoints = new ArrayList<>();
+ allEndpoints.addAll(webEndpoints);
+ allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
return new CloudFoundryWebFluxEndpointHandlerMapping(
- new EndpointMapping("/cloudfoundryapplication"),
- endpointDiscoverer.getEndpoints(), endpointMediaTypes,
- getCorsConfiguration(), securityInterceptor);
+ new EndpointMapping("/cloudfoundryapplication"), webEndpoints,
+ endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
+ new EndpointLinksResolver(allEndpoints));
}
private CloudFoundrySecurityInterceptor getSecurityInterceptor(
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java
index 4185f30c677..3d12e473989 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java
@@ -16,17 +16,25 @@
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryWebEndpointDiscoverer;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
+import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
+import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
+import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -87,17 +95,24 @@ public class CloudFoundryActuatorAutoConfiguration {
@Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
ParameterValueMapper parameterMapper, EndpointMediaTypes endpointMediaTypes,
- RestTemplateBuilder restTemplateBuilder) {
+ RestTemplateBuilder restTemplateBuilder,
+ ServletEndpointsSupplier servletEndpointsSupplier,
+ ControllerEndpointsSupplier controllerEndpointsSupplier) {
CloudFoundryWebEndpointDiscoverer discoverer = new CloudFoundryWebEndpointDiscoverer(
this.applicationContext, parameterMapper, endpointMediaTypes,
PathMapper.useEndpointId(), Collections.emptyList(),
Collections.emptyList());
CloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor(
restTemplateBuilder, this.applicationContext.getEnvironment());
+ Collection webEndpoints = discoverer.getEndpoints();
+ List> allEndpoints = new ArrayList<>();
+ allEndpoints.addAll(webEndpoints);
+ allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
+ allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
return new CloudFoundryWebEndpointServletHandlerMapping(
- new EndpointMapping("/cloudfoundryapplication"),
- discoverer.getEndpoints(), endpointMediaTypes, getCorsConfiguration(),
- securityInterceptor);
+ new EndpointMapping("/cloudfoundryapplication"), webEndpoints,
+ endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
+ new EndpointLinksResolver(allEndpoints));
}
private CloudFoundrySecurityInterceptor getSecurityInterceptor(
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 2b45b1edd1c..3ffcdc0491a 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
@@ -52,14 +52,16 @@ class CloudFoundryWebEndpointServletHandlerMapping
private final CloudFoundrySecurityInterceptor securityInterceptor;
- private final EndpointLinksResolver linksResolver = new EndpointLinksResolver();
+ private final EndpointLinksResolver linksResolver;
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
Collection endpoints,
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
- CloudFoundrySecurityInterceptor securityInterceptor) {
+ CloudFoundrySecurityInterceptor securityInterceptor,
+ EndpointLinksResolver linksResolver) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration);
this.securityInterceptor = securityInterceptor;
+ this.linksResolver = linksResolver;
}
@Override
@@ -80,8 +82,8 @@ class CloudFoundryWebEndpointServletHandlerMapping
}
AccessLevel accessLevel = (AccessLevel) request
.getAttribute(AccessLevel.REQUEST_ATTRIBUTE);
- Map links = this.linksResolver.resolveLinks(getEndpoints(),
- request.getRequestURL().toString());
+ Map links = this.linksResolver
+ .resolveLinks(request.getRequestURL().toString());
Map filteredLinks = new LinkedHashMap<>();
if (accessLevel == null) {
return Collections.singletonMap("_links", filteredLinks);
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 cdf50e32323..55a3c51ecdd 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
@@ -16,19 +16,25 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
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.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
+import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -55,17 +61,24 @@ class JerseyWebEndpointManagementContextConfiguration {
@Bean
public ResourceConfigCustomizer webEndpointRegistrar(
WebEndpointsSupplier webEndpointsSupplier,
+ ServletEndpointsSupplier servletEndpointsSupplier,
+ ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes,
WebEndpointProperties webEndpointProperties) {
+ List> allEndpoints = new ArrayList<>();
+ allEndpoints.addAll(webEndpointsSupplier.getEndpoints());
+ allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
+ allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
return (resourceConfig) -> {
JerseyEndpointResourceFactory resourceFactory = new JerseyEndpointResourceFactory();
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
- Collection endpoints = Collections
+ Collection webEndpoints = Collections
.unmodifiableCollection(webEndpointsSupplier.getEndpoints());
resourceConfig.registerResources(
new HashSet<>(resourceFactory.createEndpointResources(endpointMapping,
- endpoints, endpointMediaTypes)));
+ webEndpoints, endpointMediaTypes,
+ new EndpointLinksResolver(allEndpoints))));
};
}
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 6b99bef0e7d..8124b490ff9 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
@@ -16,12 +16,19 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive;
+import java.util.ArrayList;
+import java.util.Collection;
+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.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
+import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping;
@@ -54,13 +61,18 @@ public class WebFluxEndpointManagementContextConfiguration {
@ConditionalOnMissingBean
public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier,
+ ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
EndpointMapping endpointMapping = new EndpointMapping(
webEndpointProperties.getBasePath());
- return new WebFluxEndpointHandlerMapping(endpointMapping,
- webEndpointsSupplier.getEndpoints(), endpointMediaTypes,
- corsProperties.toCorsConfiguration());
+ 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));
}
@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 2072a43e888..679760eb679 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
@@ -16,14 +16,22 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet;
+import java.util.ArrayList;
+import java.util.Collection;
+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.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
+import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
+import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.ControllerEndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -53,13 +61,21 @@ public class WebMvcEndpointManagementContextConfiguration {
@ConditionalOnMissingBean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier,
+ ServletEndpointsSupplier servletEndpointsSupplier,
+ ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
+ 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());
- return new WebMvcEndpointHandlerMapping(endpointMapping,
- webEndpointsSupplier.getEndpoints(), endpointMediaTypes,
- corsProperties.toCorsConfiguration());
+ return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints,
+ endpointMediaTypes, corsProperties.toCorsConfiguration(),
+ new EndpointLinksResolver(allEndpoints));
}
@Bean
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java
index b94ff8e93d0..56152f3b076 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java
@@ -34,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@@ -221,7 +222,8 @@ public class CloudFoundryWebFluxEndpointIntegrationTests {
return new CloudFoundryWebFluxEndpointHandlerMapping(
new EndpointMapping("/cfApplication"),
webEndpointDiscoverer.getEndpoints(), endpointMediaTypes,
- corsConfiguration, interceptor);
+ corsConfiguration, interceptor,
+ new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
}
@Bean
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java
index 7c5da5dedee..6dd6a226f24 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java
@@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@@ -207,7 +208,8 @@ public class CloudFoundryMvcWebEndpointIntegrationTests {
return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cfApplication"),
webEndpointDiscoverer.getEndpoints(), endpointMediaTypes,
- corsConfiguration, interceptor);
+ corsConfiguration, interceptor,
+ new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
}
@Bean
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JerseyEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JerseyEndpointIntegrationTests.java
new file mode 100644
index 00000000000..5bfd7c70609
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JerseyEndpointIntegrationTests.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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 org.springframework.boot.actuate.autoconfigure.integrationtest;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Test;
+
+import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
+import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * Integration tests for the Jersey actuator endpoints.
+ *
+ * @author Andy Wilkinson
+ */
+public class JerseyEndpointIntegrationTests {
+
+ @Test
+ public void linksAreProvidedToAllEndpointTypes() throws Exception {
+ new WebApplicationContextRunner(
+ AnnotationConfigServletWebServerApplicationContext::new)
+ .withConfiguration(
+ AutoConfigurations.of(JacksonAutoConfiguration.class,
+ JerseyAutoConfiguration.class,
+ EndpointAutoConfiguration.class,
+ ServletWebServerFactoryAutoConfiguration.class,
+ WebEndpointAutoConfiguration.class,
+ ManagementContextAutoConfiguration.class,
+ BeansEndpointAutoConfiguration.class))
+ .withUserConfiguration(EndpointsConfiguration.class)
+ .withPropertyValues("management.endpoints.web.expose:*",
+ "server.port:0")
+ .run((context) -> {
+ int port = context.getSourceApplicationContext(
+ AnnotationConfigServletWebServerApplicationContext.class)
+ .getWebServer().getPort();
+ WebTestClient client = WebTestClient.bindToServer()
+ .baseUrl("http://localhost:" + port).build();
+ client.get().uri("/actuator").exchange().expectStatus().isOk()
+ .expectBody().jsonPath("_links.beans").isNotEmpty()
+ .jsonPath("_links.restcontroller").isNotEmpty()
+ .jsonPath("_links.controller").isNotEmpty();
+ });
+ }
+
+ @ControllerEndpoint(id = "controller")
+ static class TestControllerEndpoint {
+
+ }
+
+ @RestControllerEndpoint(id = "restcontroller")
+ static class TestRestControllerEndpoint {
+
+ }
+
+ @Configuration
+ static class EndpointsConfiguration {
+
+ @Bean
+ ResourceConfig testResourceConfig() {
+ return new ResourceConfig();
+ }
+
+ @Bean
+ TestControllerEndpoint testControllerEndpoint() {
+ return new TestControllerEndpoint();
+ }
+
+ @Bean
+ TestRestControllerEndpoint testRestControllerEndpoint() {
+ return new TestRestControllerEndpoint();
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebFluxEndpointIntegrationTests.java
new file mode 100644
index 00000000000..254032951bb
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebFluxEndpointIntegrationTests.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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 org.springframework.boot.actuate.autoconfigure.integrationtest;
+
+import org.junit.Test;
+
+import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementContextAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
+import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
+import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * Integration tests for the WebFlux actuator endpoints.
+ *
+ * @author Andy Wilkinson
+ */
+public class WebFluxEndpointIntegrationTests {
+
+ @Test
+ public void linksAreProvidedToAllEndpointTypes() throws Exception {
+ new ReactiveWebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class,
+ CodecsAutoConfiguration.class, WebFluxAutoConfiguration.class,
+ HttpHandlerAutoConfiguration.class,
+ EndpointAutoConfiguration.class,
+ WebEndpointAutoConfiguration.class,
+ ManagementContextAutoConfiguration.class,
+ ReactiveManagementContextAutoConfiguration.class,
+ BeansEndpointAutoConfiguration.class))
+ .withUserConfiguration(EndpointsConfiguration.class)
+ .withPropertyValues("management.endpoints.web.expose:*")
+ .run((context) -> {
+ WebTestClient client = createWebTestClient(context);
+ client.get().uri("/actuator").exchange().expectStatus().isOk()
+ .expectBody().jsonPath("_links.beans").isNotEmpty()
+ .jsonPath("_links.restcontroller").isNotEmpty()
+ .jsonPath("_links.controller").isNotEmpty();
+ });
+ }
+
+ private WebTestClient createWebTestClient(ApplicationContext context) {
+ return WebTestClient.bindToApplicationContext(context).configureClient()
+ .baseUrl("https://spring.example.org").build();
+ }
+
+ @ControllerEndpoint(id = "controller")
+ static class TestControllerEndpoint {
+
+ }
+
+ @RestControllerEndpoint(id = "restcontroller")
+ static class TestRestControllerEndpoint {
+
+ }
+
+ @Configuration
+ static class EndpointsConfiguration {
+
+ @Bean
+ TestControllerEndpoint testControllerEndpoint() {
+ return new TestControllerEndpoint();
+ }
+
+ @Bean
+ TestRestControllerEndpoint testRestControllerEndpoint() {
+ return new TestRestControllerEndpoint();
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java
index 1d4fb3f0490..b8bdf6547b8 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointIntegrationTests.java
@@ -16,6 +16,10 @@
package org.springframework.boot.actuate.autoconfigure.integrationtest;
+import java.util.function.Supplier;
+
+import javax.servlet.http.HttpServlet;
+
import org.junit.After;
import org.junit.Test;
@@ -25,6 +29,10 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfi
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
+import org.springframework.boot.actuate.endpoint.web.EndpointServlet;
+import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
+import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
+import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
@@ -35,6 +43,8 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfi
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockServletContext;
@@ -46,8 +56,11 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.hasKey;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
@@ -97,6 +110,17 @@ public class WebMvcEndpointIntegrationTests {
mockMvc.perform(get("/management/beans")).andExpect(status().isOk());
}
+ @Test
+ public void linksAreProvidedToAllEndpointTypes() throws Exception {
+ this.context = new AnnotationConfigWebApplicationContext();
+ this.context.register(DefaultConfiguration.class, EndpointsConfiguration.class);
+ TestPropertyValues.of("management.endpoints.web.expose=*").applyTo(this.context);
+ MockMvc mockMvc = doCreateMockMvc();
+ mockMvc.perform(get("/actuator").accept("*/*")).andExpect(status().isOk())
+ .andExpect(jsonPath("_links", both(hasKey("beans")).and(hasKey("servlet"))
+ .and(hasKey("restcontroller")).and(hasKey("controller"))));
+ }
+
private MockMvc createSecureMockMvc() {
return doCreateMockMvc(springSecurity());
}
@@ -142,4 +166,45 @@ public class WebMvcEndpointIntegrationTests {
}
+ @ServletEndpoint(id = "servlet")
+ static class TestServletEndpoint implements Supplier {
+
+ @Override
+ public EndpointServlet get() {
+ return new EndpointServlet(new HttpServlet() {
+ });
+ }
+
+ }
+
+ @ControllerEndpoint(id = "controller")
+ static class TestControllerEndpoint {
+
+ }
+
+ @RestControllerEndpoint(id = "restcontroller")
+ static class TestRestControllerEndpoint {
+
+ }
+
+ @Configuration
+ static class EndpointsConfiguration {
+
+ @Bean
+ TestServletEndpoint testServletEndpoint() {
+ return new TestServletEndpoint();
+ }
+
+ @Bean
+ TestControllerEndpoint testControllerEndpoint() {
+ return new TestControllerEndpoint();
+ }
+
+ @Bean
+ TestRestControllerEndpoint testRestControllerEndpoint() {
+ return new TestRestControllerEndpoint();
+ }
+
+ }
+
}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java
index e95ff06cc26..dbcee560c70 100644
--- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolver.java
@@ -20,6 +20,8 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
+
/**
* A resolver for {@link Link links} to web endpoints.
*
@@ -28,22 +30,29 @@ import java.util.Map;
*/
public class EndpointLinksResolver {
+ private final Collection extends ExposableEndpoint>> endpoints;
+
+ public EndpointLinksResolver(Collection extends ExposableEndpoint>> endpoints) {
+ this.endpoints = endpoints;
+ }
+
/**
- * Resolves links to the operations of the given {code webEndpoints} based on a
- * request with the given {@code requestUrl}.
- * @param endpoints the source endpoints
+ * Resolves links to the known endpoints based on a request with the given
+ * {@code requestUrl}.
* @param requestUrl the url of the request for the endpoint links
* @return the links
*/
- public Map resolveLinks(Collection endpoints,
- String requestUrl) {
+ public Map resolveLinks(String requestUrl) {
String normalizedUrl = normalizeRequestUrl(requestUrl);
Map links = new LinkedHashMap<>();
links.put("self", new Link(normalizedUrl));
- for (ExposableWebEndpoint endpoint : endpoints) {
- for (WebOperation operation : endpoint.getOperations()) {
- endpoints.stream().map(ExposableWebEndpoint::getId).forEach((id) -> links
- .put(operation.getId(), createLink(normalizedUrl, operation)));
+ for (ExposableEndpoint> endpoint : this.endpoints) {
+ if (endpoint instanceof ExposableWebEndpoint) {
+ collectLinks(links, (ExposableWebEndpoint) endpoint, normalizedUrl);
+ }
+ else if (endpoint instanceof PathMappedEndpoint) {
+ links.put(endpoint.getId(), createLink(normalizedUrl,
+ ((PathMappedEndpoint) endpoint).getRootPath()));
}
}
return links;
@@ -56,8 +65,18 @@ public class EndpointLinksResolver {
return requestUrl;
}
+ private void collectLinks(Map links, ExposableWebEndpoint endpoint,
+ String normalizedUrl) {
+ for (WebOperation operation : endpoint.getOperations()) {
+ links.put(operation.getId(), createLink(normalizedUrl, operation));
+ }
+ }
+
private Link createLink(String requestUrl, WebOperation operation) {
- String path = operation.getRequestPredicate().getPath();
+ return createLink(requestUrl, operation.getRequestPredicate().getPath());
+ }
+
+ private Link createLink(String requestUrl, String path) {
return new Link(requestUrl + (path.startsWith("/") ? path : "/" + path));
}
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 72d9aecb062..19fa276241b 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
@@ -62,26 +62,25 @@ import org.springframework.util.StringUtils;
*/
public class JerseyEndpointResourceFactory {
- private final EndpointLinksResolver endpointLinksResolver = new EndpointLinksResolver();
-
/**
* Creates {@link Resource Resources} for the operations of the given
* {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param endpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
+ * @param linksResolver resolver for determining links to available endpoints
* @return the resources for the operations
*/
public Collection createEndpointResources(EndpointMapping endpointMapping,
Collection endpoints,
- EndpointMediaTypes endpointMediaTypes) {
+ EndpointMediaTypes endpointMediaTypes, EndpointLinksResolver linksResolver) {
List resources = new ArrayList<>();
endpoints.stream().flatMap((endpoint) -> endpoint.getOperations().stream())
.map((operation) -> createResource(endpointMapping, operation))
.forEach(resources::add);
if (StringUtils.hasText(endpointMapping.getPath())) {
Resource resource = createEndpointLinksResource(endpointMapping.getPath(),
- endpoints, endpointMediaTypes);
+ endpointMediaTypes, linksResolver);
resources.add(resource);
}
return resources;
@@ -105,14 +104,12 @@ public class JerseyEndpointResourceFactory {
}
private Resource createEndpointLinksResource(String endpointPath,
- Collection endpoints,
- EndpointMediaTypes endpointMediaTypes) {
+ EndpointMediaTypes endpointMediaTypes, EndpointLinksResolver linksResolver) {
Builder resourceBuilder = Resource.builder().path(endpointPath);
resourceBuilder.addMethod("GET")
.produces(endpointMediaTypes.getProduced()
.toArray(new String[endpointMediaTypes.getProduced().size()]))
- .handledBy(new EndpointLinksInflector(endpoints,
- this.endpointLinksResolver));
+ .handledBy(new EndpointLinksInflector(linksResolver));
return resourceBuilder.build();
}
@@ -266,20 +263,16 @@ public class JerseyEndpointResourceFactory {
private static final class EndpointLinksInflector
implements Inflector {
- private final Collection endpoints;
-
private final EndpointLinksResolver linksResolver;
- private EndpointLinksInflector(Collection endpoints,
- EndpointLinksResolver linksResolver) {
- this.endpoints = endpoints;
+ private EndpointLinksInflector(EndpointLinksResolver linksResolver) {
this.linksResolver = linksResolver;
}
@Override
public Response apply(ContainerRequestContext request) {
- Map links = this.linksResolver.resolveLinks(this.endpoints,
- request.getUriInfo().getAbsolutePath().toString());
+ Map links = this.linksResolver
+ .resolveLinks(request.getUriInfo().getAbsolutePath().toString());
return Response.ok(Collections.singletonMap("_links", links)).build();
}
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 7e19a5450ca..42bd357b2b2 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
@@ -43,7 +43,7 @@ import org.springframework.web.util.UriComponentsBuilder;
public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandlerMapping
implements InitializingBean {
- private final EndpointLinksResolver linksResolver = new EndpointLinksResolver();
+ private final EndpointLinksResolver linksResolver;
/**
* Creates a new {@code WebFluxEndpointHandlerMapping} instance that provides mappings
@@ -52,11 +52,14 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle
* @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 linksResolver resolver for determining links to available endpoints
*/
public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection endpoints,
- EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
+ EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
+ EndpointLinksResolver linksResolver) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration);
+ this.linksResolver = linksResolver;
setOrder(-100);
}
@@ -66,7 +69,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle
String requestUri = UriComponentsBuilder.fromUri(exchange.getRequest().getURI())
.replaceQuery(null).toUriString();
return Collections.singletonMap("_links",
- this.linksResolver.resolveLinks(getEndpoints(), requestUri));
+ this.linksResolver.resolveLinks(requestUri));
}
}
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 2b91e40ec92..ca1aae006d6 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
@@ -42,7 +42,7 @@ import org.springframework.web.servlet.HandlerMapping;
*/
public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerMapping {
- private final EndpointLinksResolver linksResolver = new EndpointLinksResolver();
+ private final EndpointLinksResolver linksResolver;
/**
* Creates a new {@code WebMvcEndpointHandlerMapping} instance that provides mappings
@@ -51,11 +51,14 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
* @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 linksResolver resolver for determining links to available endpoints
*/
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection endpoints,
- EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
+ EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
+ EndpointLinksResolver linksResolver) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration);
+ this.linksResolver = linksResolver;
setOrder(-100);
}
@@ -63,9 +66,8 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
@ResponseBody
protected Map> links(HttpServletRequest request,
HttpServletResponse response) {
- String requestUri = request.getRequestURL().toString();
return Collections.singletonMap("_links",
- this.linksResolver.resolveLinks(getEndpoints(), requestUri));
+ this.linksResolver.resolveLinks(request.getRequestURL().toString()));
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java
index 48826753b35..32ae0fe60ef 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointLinksResolverTests.java
@@ -25,6 +25,7 @@ import org.assertj.core.api.Condition;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.OperationType;
+import org.springframework.boot.actuate.endpoint.web.annotation.ExposableControllerEndpoint;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -37,12 +38,10 @@ import static org.mockito.Mockito.mock;
*/
public class EndpointLinksResolverTests {
- private final EndpointLinksResolver linksResolver = new EndpointLinksResolver();
-
@Test
public void linkResolutionWithTrailingSlashStripsSlashOnSelfLink() {
- Map links = this.linksResolver.resolveLinks(Collections.emptyList(),
- "https://api.example.com/actuator/");
+ Map links = new EndpointLinksResolver(Collections.emptyList())
+ .resolveLinks("https://api.example.com/actuator/");
assertThat(links).hasSize(1);
assertThat(links).hasEntrySatisfying("self",
linkWithHref("https://api.example.com/actuator"));
@@ -50,15 +49,15 @@ public class EndpointLinksResolverTests {
@Test
public void linkResolutionWithoutTrailingSlash() {
- Map links = this.linksResolver.resolveLinks(Collections.emptyList(),
- "https://api.example.com/actuator");
+ Map links = new EndpointLinksResolver(Collections.emptyList())
+ .resolveLinks("https://api.example.com/actuator");
assertThat(links).hasSize(1);
assertThat(links).hasEntrySatisfying("self",
linkWithHref("https://api.example.com/actuator"));
}
@Test
- public void resolvedLinksContainsALinkForEachEndpointOperation() {
+ public void resolvedLinksContainsALinkForEachWebEndpointOperation() {
List operations = new ArrayList<>();
operations.add(operationWithPath("/alpha", "alpha"));
operations.add(operationWithPath("/alpha/{name}", "alpha-name"));
@@ -67,8 +66,8 @@ public class EndpointLinksResolverTests {
given(endpoint.isEnableByDefault()).willReturn(true);
given(endpoint.getOperations()).willReturn(operations);
String requestUrl = "https://api.example.com/actuator";
- Map links = this.linksResolver
- .resolveLinks(Collections.singletonList(endpoint), requestUrl);
+ Map links = new EndpointLinksResolver(
+ Collections.singletonList(endpoint)).resolveLinks(requestUrl);
assertThat(links).hasSize(3);
assertThat(links).hasEntrySatisfying("self",
linkWithHref("https://api.example.com/actuator"));
@@ -78,6 +77,39 @@ public class EndpointLinksResolverTests {
linkWithHref("https://api.example.com/actuator/alpha/{name}"));
}
+ @Test
+ public void resolvedLinksContainsALinkForServletEndpoint() {
+ ExposableServletEndpoint servletEndpoint = mock(ExposableServletEndpoint.class);
+ given(servletEndpoint.getId()).willReturn("alpha");
+ given(servletEndpoint.isEnableByDefault()).willReturn(true);
+ given(servletEndpoint.getRootPath()).willReturn("alpha");
+ String requestUrl = "https://api.example.com/actuator";
+ Map links = new EndpointLinksResolver(
+ Collections.singletonList(servletEndpoint)).resolveLinks(requestUrl);
+ assertThat(links).hasSize(2);
+ assertThat(links).hasEntrySatisfying("self",
+ linkWithHref("https://api.example.com/actuator"));
+ assertThat(links).hasEntrySatisfying("alpha",
+ linkWithHref("https://api.example.com/actuator/alpha"));
+ }
+
+ @Test
+ public void resolvedLinksContainsALinkForControllerEndpoint() {
+ ExposableControllerEndpoint controllerEndpoint = mock(
+ ExposableControllerEndpoint.class);
+ given(controllerEndpoint.getId()).willReturn("alpha");
+ given(controllerEndpoint.isEnableByDefault()).willReturn(true);
+ given(controllerEndpoint.getRootPath()).willReturn("alpha");
+ String requestUrl = "https://api.example.com/actuator";
+ Map links = new EndpointLinksResolver(
+ Collections.singletonList(controllerEndpoint)).resolveLinks(requestUrl);
+ assertThat(links).hasSize(2);
+ assertThat(links).hasEntrySatisfying("self",
+ linkWithHref("https://api.example.com/actuator"));
+ assertThat(links).hasEntrySatisfying("alpha",
+ linkWithHref("https://api.example.com/actuator/alpha"));
+ }
+
private WebOperation operationWithPath(String path, String id) {
WebOperationRequestPredicate predicate = new WebOperationRequestPredicate(path,
WebEndpointHttpMethod.GET, Collections.emptyList(),
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 b2b3fbbbfb4..ce75518d70f 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
@@ -35,6 +35,7 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.servlet.ServletContainer;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
@@ -110,7 +111,8 @@ public class JerseyWebEndpointIntegrationTests extends
Collection resources = new JerseyEndpointResourceFactory()
.createEndpointResources(
new EndpointMapping(environment.getProperty("endpointPath")),
- endpointDiscoverer.getEndpoints(), endpointMediaTypes);
+ endpointDiscoverer.getEndpoints(), endpointMediaTypes,
+ new EndpointLinksResolver(endpointDiscoverer.getEndpoints()));
resourceConfig.registerResources(new HashSet<>(resources));
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()),
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 a1db891b195..ff33eb81050 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
@@ -22,6 +22,7 @@ import java.util.Arrays;
import org.junit.Test;
import reactor.core.publisher.Mono;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
@@ -132,7 +133,8 @@ public class WebFluxEndpointIntegrationTests
return new WebFluxEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.getEndpoints(), endpointMediaTypes,
- corsConfiguration);
+ corsConfiguration,
+ new EndpointLinksResolver(endpointDiscoverer.getEndpoints()));
}
@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 baa71015583..43995e74dbe 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
@@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
@@ -129,7 +130,8 @@ public class MvcWebEndpointIntegrationTests extends
return new WebMvcEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.getEndpoints(), endpointMediaTypes,
- corsConfiguration);
+ corsConfiguration,
+ new EndpointLinksResolver(endpointDiscoverer.getEndpoints()));
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java
index 2eadacbc1bb..b0076658f94 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/JerseyEndpointsRunner.java
@@ -31,6 +31,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@@ -104,7 +105,8 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner {
Collections.emptyList(), Collections.emptyList());
Collection resources = new JerseyEndpointResourceFactory()
.createEndpointResources(new EndpointMapping("/actuator"),
- discoverer.getEndpoints(), endpointMediaTypes);
+ discoverer.getEndpoints(), endpointMediaTypes,
+ new EndpointLinksResolver(discoverer.getEndpoints()));
config.registerResources(new HashSet<>(resources));
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java
index 83e1d5dce71..8735d051418 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebFluxEndpointsRunner.java
@@ -25,6 +25,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@@ -110,7 +111,8 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner {
Collections.emptyList(), Collections.emptyList());
return new WebFluxEndpointHandlerMapping(new EndpointMapping("/actuator"),
discoverer.getEndpoints(), endpointMediaTypes,
- new CorsConfiguration());
+ new CorsConfiguration(),
+ new EndpointLinksResolver(discoverer.getEndpoints()));
}
}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java
index a3b71d4ed6c..da424a19952 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebMvcEndpointRunner.java
@@ -25,6 +25,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
+import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@@ -93,7 +94,8 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner {
Collections.emptyList(), Collections.emptyList());
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"),
discoverer.getEndpoints(), endpointMediaTypes,
- new CorsConfiguration());
+ new CorsConfiguration(),
+ new EndpointLinksResolver(discoverer.getEndpoints()));
}
}