From 2d64fb4d840d956998c0a6e40c69e28267328a63 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 25 Jul 2014 15:25:11 +0200 Subject: [PATCH] DATAREST-326 - Improved configuration setup for better Boot compatibility. We now don't expose the PersistentEntityJackson2Module as bean anymore to prevent global registration in case of a Boot setup. The HttpMessageConverters registered are now TypeConstrainedMappingJackson2HttpMessageConverters so that they only get used of the object to marshal is of type ResourceSupport. --- spring-data-rest-core/pom.xml | 2 +- ...ResourceHandlerMethodArgumentResolver.java | 2 +- .../RepositoryRestMvcConfiguration.java | 28 +++++++++++++------ .../webmvc/AbstractWebIntegrationTests.java | 2 +- ...ryRestMvConfigurationIntegrationTests.java | 27 ++++++++++++++++++ 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/spring-data-rest-core/pom.xml b/spring-data-rest-core/pom.xml index 19acbc889..1b358e514 100644 --- a/spring-data-rest-core/pom.xml +++ b/spring-data-rest-core/pom.xml @@ -16,7 +16,7 @@ - 0.15.0.RELEASE + 0.16.0.BUILD-SNAPSHOT 1.1.0.RELEASE 1.2 diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java index 395cc7afc..742d61ada 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java @@ -115,7 +115,7 @@ public class PersistentEntityResourceHandlerMethodArgumentResolver implements Ha for (HttpMessageConverter converter : messageConverters) { - if (!converter.canRead(domainType, contentType)) { + if (!converter.canRead(PersistentEntityResource.class, contentType)) { continue; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index f1d0da8fa..0ba00fdba 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -98,12 +98,14 @@ import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RelProvider; import org.springframework.hateoas.ResourceProcessor; +import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.core.EvoInflectorRelProvider; import org.springframework.hateoas.hal.CurieProvider; import org.springframework.hateoas.hal.Jackson2HalModule; import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator; +import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -373,7 +375,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon */ @Bean public ObjectMapper objectMapper() { - return basicObjectMapper(); + + ObjectMapper mapper = basicObjectMapper(); + mapper.registerModule(persistentEntityJackson2Module()); + + return mapper; } /** @@ -394,7 +400,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon mediaTypes.add(MediaType.APPLICATION_JSON); } - MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(); + MappingJackson2HttpMessageConverter jacksonConverter = new TypeConstrainedMappingJackson2HttpMessageConverter( + ResourceSupport.class); jacksonConverter.setObjectMapper(objectMapper()); jacksonConverter.setSupportedMediaTypes(mediaTypes); @@ -416,7 +423,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon mediaTypes.add(MediaType.APPLICATION_JSON); } - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + MappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter( + ResourceSupport.class); converter.setObjectMapper(halObjectMapper()); converter.setSupportedMediaTypes(mediaTypes); @@ -429,6 +437,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon HalHandlerInstantiator instantiator = new HalHandlerInstantiator(getDefaultedRelProvider(), curieProvider); ObjectMapper mapper = basicObjectMapper(); + mapper.registerModule(persistentEntityJackson2Module()); mapper.registerModule(new Jackson2HalModule()); mapper.setHandlerInstantiator(instantiator); @@ -508,8 +517,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon * * @return */ - @Bean - public Module persistentEntityJackson2Module() { + private Module persistentEntityJackson2Module() { return new PersistentEntityJackson2Module(resourceMappings(), persistentEntities(), config(), uriToEntityConverter()); } @@ -555,6 +563,10 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon messageConverters.add(halJacksonHttpMessageConverter()); } + MappingJackson2HttpMessageConverter fallbackJsonConverter = new MappingJackson2HttpMessageConverter(); + fallbackJsonConverter.setObjectMapper(basicObjectMapper()); + + messageConverters.add(fallbackJsonConverter); messageConverters.add(uriListHttpMessageConverter()); return messageConverters; @@ -622,12 +634,12 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon private ObjectMapper basicObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - // Our special PersistentEntityResource Module - objectMapper.registerModule(persistentEntityJackson2Module()); - objectMapper.registerModule(geoModule); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + objectMapper.registerModule(geoModule); Jackson2DatatypeHelper.configureObjectMapper(objectMapper); // Configure custom Modules configureJacksonObjectMapper(objectMapper); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java index 7bdef8ddd..c3f7edc44 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java @@ -380,7 +380,7 @@ public abstract class AbstractWebIntegrationTests { public void servesHalWhenJsonIsRequested() throws Exception { mvc.perform(get("/").accept(MediaType.APPLICATION_JSON)). // - andExpect(content().contentType(MediaType.APPLICATION_JSON)). // + andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)). // andExpect(jsonPath("$._links", notNullValue())); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java index f1b23b847..3a23a190d 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java @@ -18,12 +18,14 @@ package org.springframework.data.rest.webmvc.config; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.Collection; import java.util.Date; import java.util.Locale; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,13 +34,16 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.webmvc.RepositoryLinksResource; +import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module; import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.datetime.DateFormatter; import org.springframework.hateoas.LinkDiscoverers; import org.springframework.hateoas.core.DefaultRelProvider; +import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder; @@ -127,6 +132,28 @@ public class RepositoryRestMvConfigurationIntegrationTests { assertThat(result, is((Object) formatter.print(sample.date, Locale.US))); } + /** + * @see DATAREST-362 + */ + @Test(expected = NoSuchBeanDefinitionException.class) + public void doesNotExposePersistentEntityJackson2ModuleAsBean() { + context.getBean(PersistentEntityJackson2Module.class); + } + + /** + * @see DATAREST-362 + */ + @Test + public void registeredHttpMessageConvertersAreTypeConstrained() { + + Collection converters = context.getBeansOfType( + MappingJackson2HttpMessageConverter.class).values(); + + for (HttpMessageConverter converter : converters) { + assertThat(converter, is(instanceOf(TypeConstrainedMappingJackson2HttpMessageConverter.class))); + } + } + @Configuration static class ExtendingConfiguration extends RepositoryRestMvcConfiguration {