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> 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> 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> 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 findConverter( Class> selectedConverterType, - MediaType mediaType) { + MediaType mediaType) { if (this.converterCache.containsKey(mediaType)) { return (HttpMessageConverter) this.converterCache .get(mediaType); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java index 1d1e84197b..9fc42fee97 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java @@ -50,7 +50,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl * @author Dave Syer */ public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements - ApplicationContextAware { +ApplicationContextAware { private final Set endpoints; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java index f10e9b2b9a..4863c923f0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java @@ -16,12 +16,16 @@ package org.springframework.boot.actuate.autoconfigure; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -38,11 +42,12 @@ import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration; -import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.test.IntegrationTest; @@ -51,6 +56,7 @@ import org.springframework.boot.test.TestRestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -60,9 +66,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - /** * Integration tests for MVC {@link Endpoint}s. * @@ -82,7 +85,7 @@ public class EndpointMvcIntegrationTests { private TestInterceptor interceptor; @Test - public void envEndpointNotHidden() throws InterruptedException { + public void envEndpointHidden() throws InterruptedException { String body = new TestRestTemplate().getForObject("http://localhost:" + this.port + "/env/user.dir", String.class); assertNotNull(body); @@ -90,14 +93,23 @@ public class EndpointMvcIntegrationTests { assertTrue(this.interceptor.invoked()); } + @Test + public void healthEndpointNotHidden() throws InterruptedException { + String body = new TestRestTemplate().getForObject("http://localhost:" + this.port + + "/health", String.class); + assertNotNull(body); + assertTrue("Wrong body: \n" + body, body.contains("status")); + assertTrue(this.interceptor.invoked()); + } + @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({ EmbeddedServletContainerAutoConfiguration.class, - ServerPropertiesAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, - ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) + ServerPropertiesAutoConfiguration.class, + DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, + JacksonAutoConfiguration.class, ErrorMvcAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) protected static @interface MinimalWebConfiguration { } @@ -105,8 +117,8 @@ public class EndpointMvcIntegrationTests { @Configuration @MinimalWebConfiguration @Import({ ManagementServerPropertiesAutoConfiguration.class, - JacksonAutoConfiguration.class, EndpointAutoConfiguration.class, - EndpointWebMvcAutoConfiguration.class }) + JacksonAutoConfiguration.class, EndpointAutoConfiguration.class, + EndpointWebMvcAutoConfiguration.class }) @RestController protected static class Application { @@ -122,6 +134,15 @@ public class EndpointMvcIntegrationTests { return Collections.singletonMap("foo", (Object) "bar"); } + @Autowired(required = false) + private final List> converters = Collections.emptyList(); + + @Bean + @ConditionalOnMissingBean + public HttpMessageConverters messageConverters() { + return new HttpMessageConverters(this.converters); + } + @Bean public EndpointHandlerMappingCustomizer mappingCustomizer() { return new EndpointHandlerMappingCustomizer() { diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests.java new file mode 100644 index 0000000000..a2383b44fb --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2015 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 sample.hypermedia.jpa; + +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; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class) +@DirtiesContext +@TestPropertySource(properties = { "debug=true", "management.contextPath=", +"endpoints.docs.curies.enabled=false" }) +public class SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private MvcEndpoints mvcEndpoints; + + private MockMvc mockMvc; + + @Before + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + } + + @Test + public void links() throws Exception { + this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$._links").exists()) + .andExpect(jsonPath("$._links.health").exists()) + .andExpect(jsonPath("$._links.books").exists()); + } + + @Test + public void health() throws Exception { + this.mockMvc.perform(get("/health").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$._links").exists()); + } + + @Test + public void docs() throws Exception { + MvcResult response = this.mockMvc + .perform(get("/docs/").accept(MediaType.TEXT_HTML)) + .andExpect(status().isOk()).andReturn(); + System.err.println(response.getResponse().getContentAsString()); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationSharedRootIntegrationTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationSharedRootIntegrationTests.java new file mode 100644 index 0000000000..21184f4ef4 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-jpa/src/test/java/sample/hypermedia/jpa/SampleHypermediaJpaApplicationSharedRootIntegrationTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2015 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 sample.hypermedia.jpa; + +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; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class) +@DirtiesContext +@TestPropertySource(properties = { "endpoints.links.path=/admin", "management.contextPath=", +"endpoints.docs.curies.enabled=false" }) +public class SampleHypermediaJpaApplicationSharedRootIntegrationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private MvcEndpoints mvcEndpoints; + + private MockMvc mockMvc; + + @Before + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + } + + @Test + public void home() throws Exception { + this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$._links").exists()) + .andExpect(jsonPath("$._links.health").exists()) + .andExpect(jsonPath("$._links.admin").exists()) + .andExpect(jsonPath("$._links.books").exists()); + } + + @Test + public void health() throws Exception { + this.mockMvc.perform(get("/health").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$._links").exists()); + } + + @Test + public void links() throws Exception { + this.mockMvc.perform(get("/admin").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._links.health").exists()) + .andExpect(jsonPath("$._links").exists()); + } + + @Test + public void docs() throws Exception { + MvcResult response = this.mockMvc + .perform(get("/docs/").accept(MediaType.TEXT_HTML)) + .andExpect(status().isOk()).andReturn(); + System.err.println(response.getResponse().getContentAsString()); + } + +}