From bba78849bda96c9484149f911f740863dd58d624 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 | 38 +++++++++++++------ .../webmvc/AbstractWebIntegrationTests.java | 2 +- ...ryRestMvConfigurationIntegrationTests.java | 28 ++++++++++++++ 5 files changed, 57 insertions(+), 15 deletions(-) diff --git a/spring-data-rest-core/pom.xml b/spring-data-rest-core/pom.xml index 4b0b2f5c9..dcfc898b3 100644 --- a/spring-data-rest-core/pom.xml +++ b/spring-data-rest-core/pom.xml @@ -16,7 +16,7 @@ - 0.14.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 236993ec9..3985f5e52 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 @@ -93,7 +93,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 b397a0639..fc5bc876f 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 @@ -89,12 +89,14 @@ import org.springframework.hateoas.EntityLinks; 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; @@ -334,11 +336,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Bean public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() { - ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.setBasename("classpath:rest-messages"); - messageSource.setUseCodeAsDefaultMessage(true); + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:rest-messages"); + messageSource.setUseCodeAsDefaultMessage(true); - return new MessageSourceAccessor(messageSource); + return new MessageSourceAccessor(messageSource); } /** @@ -348,7 +350,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon */ @Bean public ObjectMapper objectMapper() { - return basicObjectMapper(); + + ObjectMapper mapper = basicObjectMapper(); + mapper.registerModule(persistentEntityJackson2Module()); + + return mapper; } /** @@ -369,7 +375,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); @@ -391,7 +398,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); @@ -404,6 +412,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); @@ -479,8 +488,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon * * @return */ - @Bean - public Module persistentEntityJackson2Module() { + private Module persistentEntityJackson2Module() { return new PersistentEntityJackson2Module(resourceMappings(), persistentEntities(), config(), uriToEntityConverter()); } @@ -521,6 +529,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon messageConverters.add(jacksonHttpMessageConverter()); messageConverters.add(halJacksonHttpMessageConverter()); } + + MappingJackson2HttpMessageConverter fallbackJsonConverter = new MappingJackson2HttpMessageConverter(); + fallbackJsonConverter.setObjectMapper(basicObjectMapper()); + + messageConverters.add(fallbackJsonConverter); messageConverters.add(uriListHttpMessageConverter()); return messageConverters; @@ -583,11 +596,12 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon private ObjectMapper basicObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); - // Our special PersistentEntityResource Module - objectMapper.registerModule(persistentEntityJackson2Module()); - objectMapper.registerModule(geoModule); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 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 123fa3958..f36764505 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 @@ -377,7 +377,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 16f0f1315..4b70f630e 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,9 +18,12 @@ package org.springframework.data.rest.webmvc.config; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.Collection; + 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; @@ -29,11 +32,14 @@ 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.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; @@ -102,6 +108,28 @@ public class RepositoryRestMvConfigurationIntegrationTests { assertThat(params.get("mySize").get(0), is("7000")); } + /** + * @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 {