Workaround problems with order of endpoint handler mapping

When Spring Data REST is owning the home page it has its own
HandlerMapping with a fix (relatively) low priority. The /links
endpoint wants to own the home page as well, and our handler mapping
has a high priority for good reasons. This change addresses the
issue by checking if Spring Data REST is configured and if
the management context path (or  more specifically, the links
endpoint) is the same as the home page.

Fixes gh-3486
This commit is contained in:
Dave Syer 2015-07-14 17:12:52 +01:00
parent e22ae39b35
commit de95012635
7 changed files with 344 additions and 39 deletions

View File

@ -45,22 +45,22 @@
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<optional>true</optional>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
<optional>true</optional>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<optional>true</optional>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
<optional>true</optional>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@ -161,6 +161,13 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-solr</artifactId>
<optional>true</optional>
<exclusions>
<!-- the version of solr here is incompatible with jackson-xml because of an old woodstox -->
<exclusion>
<artifactId>wstx-asl</artifactId>
<groupId>org.codehaus.woodstox</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>

View File

@ -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<ContextClosedEvent> {
ApplicationListener<ContextClosedEvent> {
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) {

View File

@ -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 <T> boolean hasCustomBeanDefinition(
ConfigurableListableBeanFactory beanFactory, Class<T> 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<Object> findConverter(
Class<? extends HttpMessageConverter<?>> selectedConverterType,
MediaType mediaType) {
MediaType mediaType) {
if (this.converterCache.containsKey(mediaType)) {
return (HttpMessageConverter<Object>) this.converterCache
.get(mediaType);

View File

@ -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<MvcEndpoint> endpoints;

View File

@ -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<HttpMessageConverter<?>> converters = Collections.emptyList();
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters() {
return new HttpMessageConverters(this.converters);
}
@Bean
public EndpointHandlerMappingCustomizer mappingCustomizer() {
return new EndpointHandlerMappingCustomizer() {

View File

@ -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());
}
}

View File

@ -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());
}
}