diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index d7441c9489..fffaee3a60 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -45,22 +45,22 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
- true
+ true
org.springframework.hateoas
spring-hateoas
- true
+ true
org.springframework.plugin
spring-plugin-core
- true
+ true
org.webjars
hal-browser
- true
+ true
com.google.guava
@@ -161,6 +161,13 @@
org.springframework.data
spring-data-solr
true
+
+
+
+ wstx-asl
+ org.codehaus.woodstox
+
+
org.springframework.integration
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java
index 41555c55f6..40f4f46208 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java
@@ -42,6 +42,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
@@ -90,11 +91,11 @@ import org.springframework.web.servlet.DispatcherServlet;
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnWebApplication
@AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
- EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
- ManagementServerPropertiesAutoConfiguration.class,
- HypermediaAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
+ EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
+ ManagementServerPropertiesAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class,
+ HypermediaAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
- BeanFactoryAware, SmartInitializingSingleton {
+BeanFactoryAware, SmartInitializingSingleton {
private static final Log logger = LogFactory
.getLog(EndpointWebMvcAutoConfiguration.class);
@@ -129,7 +130,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
if (managementPort == ManagementServerPort.DIFFERENT
&& this.applicationContext instanceof EmbeddedWebApplicationContext
&& ((EmbeddedWebApplicationContext) this.applicationContext)
- .getEmbeddedServletContainer() != null) {
+ .getEmbeddedServletContainer() != null) {
createChildManagementContext();
}
if (managementPort == ManagementServerPort.SAME
@@ -149,7 +150,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
EmbeddedServletContainerAutoConfiguration.class,
DispatcherServletAutoConfiguration.class);
CloseEventPropagationListener
- .addIfPossible(this.applicationContext, childContext);
+ .addIfPossible(this.applicationContext, childContext);
try {
childContext.refresh();
managementContextResolver().setApplicationContext(childContext);
@@ -221,7 +222,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
+ throws ServletException, IOException {
if (this.properties == null) {
this.properties = this.applicationContext
.getBean(ManagementServerProperties.class);
@@ -240,7 +241,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
* parent to a child.
*/
private static class CloseEventPropagationListener implements
- ApplicationListener {
+ ApplicationListener {
private final ApplicationContext parentContext;
@@ -275,7 +276,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
}
private static class OnManagementMvcCondition extends SpringBootCondition implements
- ConfigurationCondition {
+ ConfigurationCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
@@ -324,7 +325,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
return ((managementPort == null)
|| (serverPort == null && managementPort.equals(8080))
|| (managementPort != 0 && managementPort.equals(serverPort)) ? SAME
- : DIFFERENT);
+ : DIFFERENT);
}
private static Integer getPortProperty(Environment environment, String prefix) {
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java
index 243a3e03d4..317cc81c9a 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java
@@ -16,6 +16,8 @@
package org.springframework.boot.actuate.autoconfigure;
+import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
+
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
@@ -23,7 +25,11 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorDocsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalBrowserEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HypermediaDisabled;
@@ -41,6 +47,7 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
@@ -48,7 +55,9 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.MethodMetadata;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
@@ -61,6 +70,7 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.TypeUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -74,8 +84,6 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
-import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
-
/**
* Configuration for hypermedia in HTTP endpoints.
*
@@ -128,7 +136,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
@Configuration("EndpointHypermediaAutoConfiguration.MissingResourceCondition")
@ConditionalOnResource(resources = "classpath:/META-INF/spring-data-rest/hal-browser/index.html")
protected static class MissingSpringDataRestResourceCondition extends
- SpringBootCondition {
+ SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
@@ -149,12 +157,106 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
@ConditionalOnProperty(value = "endpoints.links.enabled", matchIfMissing = true)
public static class LinksConfiguration {
+ @Autowired
+ private ListableBeanFactory beanFactory;
+
+ @Autowired
+ private ManagementServerProperties management;
+
@Bean
+ @Conditional(NotSpringDataRestHomePageCondition.class)
public LinksMvcEndpoint linksMvcEndpoint(ResourceProperties resources) {
- String defaultPath = (resources.getWelcomePage() != null ? "/links" : "");
+ String defaultPath = getDefaultPath(resources);
return new LinksMvcEndpoint(defaultPath);
}
+ private String getDefaultPath(ResourceProperties resources) {
+ return resources.getWelcomePage() != null ? "/links" : "";
+ }
+
+ private static class NotSpringDataRestHomePageCondition extends
+ SpringBootCondition {
+
+ @Override
+ public ConditionOutcome getMatchOutcome(ConditionContext context,
+ AnnotatedTypeMetadata metadata) {
+ if (!ClassUtils
+ .isPresent(
+ "org.springframework.data.rest.core.config.RepositoryRestConfiguration",
+ null)) {
+ return ConditionOutcome.match("Spring Data REST is not present");
+ }
+ Class> type = ClassUtils
+ .resolveClassName(
+ "org.springframework.data.rest.core.config.RepositoryRestConfiguration",
+ null);
+ ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
+ if (beanFactory.getBeanNamesForType(type, true, false).length == 0) {
+ return ConditionOutcome.match("Spring Data REST is not configured");
+ }
+ Environment environment = context.getEnvironment();
+ String path = getProperty(environment, "management.", "contextPath");
+ if (path == null
+ && hasCustomBeanDefinition(beanFactory,
+ ManagementServerProperties.class,
+ ManagementServerPropertiesAutoConfiguration.class)) {
+ ManagementServerProperties bean = beanFactory
+ .getBean(ManagementServerProperties.class);
+ path = bean.getContextPath();
+ }
+ if (isHome(path)) {
+ path = getProperty(environment, "endpoints.links.", "path");
+ if (isHome(path)) {
+ return ConditionOutcome
+ .noMatch("Management context path is home and so is links path");
+ }
+ else {
+ return ConditionOutcome
+ .match("Management context path is home but links path is not: '"
+ + path + "'");
+ }
+ }
+ else {
+ // N.B. we don't cover the case where the user has Spring Data REST
+ // but changes *its* home page - you'd have to instantiate the
+ // RepositoryRestConfiguration and look at it's basePath for that.
+ return ConditionOutcome
+ .match("Management context path is not home: '" + path + "'");
+ }
+ }
+
+ private static boolean isHome(String path) {
+ return path == null || "".equals(path) || "/".equals(path);
+ }
+
+ private static String getProperty(Environment environment, String prefix,
+ String name) {
+ RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
+ environment, prefix);
+ return resolver.getProperty(name, String.class);
+ }
+
+ private static boolean hasCustomBeanDefinition(
+ ConfigurableListableBeanFactory beanFactory, Class type,
+ Class> configClass) {
+ String[] names = beanFactory.getBeanNamesForType(type, true, false);
+ if (names == null || names.length != 1) {
+ return false;
+ }
+ BeanDefinition definition = beanFactory.getBeanDefinition(names[0]);
+ if (definition instanceof AnnotatedBeanDefinition) {
+ MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) definition)
+ .getFactoryMethodMetadata();
+ if (factoryMethodMetadata != null) {
+ String className = factoryMethodMetadata.getDeclaringClassName();
+ return !configClass.getName().equals(className);
+ }
+ }
+ return true;
+ }
+
+ }
+
/**
* Controller advice that adds links to the home page and/or the management
* context path. The home page is enhanced if it is composed already of a
@@ -166,7 +268,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
@Autowired
private MvcEndpoints endpoints;
- @Autowired
+ @Autowired(required = false)
private LinksMvcEndpoint linksEndpoint;
@Autowired
@@ -200,7 +302,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class extends HttpMessageConverter>> selectedConverterType,
- ServerHttpRequest request, ServerHttpResponse response) {
+ ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
beforeBodyWrite(body, (ServletServerHttpRequest) request);
}
@@ -217,8 +319,8 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
if (isHomePage(path) && hasManagementPath()) {
String rel = this.management.getContextPath().substring(1);
resource.add(linkTo(
- EndpointWebMvcHypermediaManagementContextConfiguration.class).slash(
- this.management.getContextPath()).withRel(rel));
+ EndpointWebMvcHypermediaManagementContextConfiguration.class)
+ .slash(this.management.getContextPath()).withRel(rel));
}
else {
this.linksEnhancer.addEndpointLinks(resource, "");
@@ -236,8 +338,9 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
}
private boolean isLinksPath(String path) {
- return (this.management.getContextPath() + this.linksEndpoint.getPath())
- .equals(path);
+ return this.linksEndpoint != null
+ && (this.management.getContextPath() + this.linksEndpoint
+ .getPath()).equals(path);
}
private boolean isHomePage(String path) {
@@ -280,7 +383,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class extends HttpMessageConverter>> selectedConverterType,
- ServerHttpRequest request, ServerHttpResponse response) {
+ ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
return beforeBodyWrite(body, returnType, selectedContentType,
selectedConverterType, (ServletServerHttpRequest) request,
@@ -292,7 +395,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
private Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class extends HttpMessageConverter>> selectedConverterType,
- ServletServerHttpRequest request, ServerHttpResponse response) {
+ ServletServerHttpRequest request, ServerHttpResponse response) {
if (body == null || body instanceof Resource) {
// Assume it already was handled or it already has its links
return body;
@@ -317,7 +420,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
@SuppressWarnings("unchecked")
private HttpMessageConverter