From bd0483f5e697eb35e01106311b95ad57dadbe5b7 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 25 Jun 2015 16:04:31 +0200 Subject: [PATCH] DATAREST-597 - Improved AlpsJsonHttpMessageConverter for Spring 4.2 compatibility. AlpsJsonHttpMessageConverter now implements ResponseBodyAdvice to apply the potentially necessary conversion from RootResourceInformation to Alps to make sure we also get it applied on latest Spring 4.2 snapshots that invoke a newly introduced method to handle generic types in Jackson marshaling. --- .../alps/AlpsJsonHttpMessageConverter.java | 29 ++++++++++++------- .../RepositoryRestMvcConfiguration.java | 12 +++++++- ...ryRestMvConfigurationIntegrationTests.java | 8 +++-- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsJsonHttpMessageConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsJsonHttpMessageConverter.java index b53ba8440..2ebc703e2 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsJsonHttpMessageConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsJsonHttpMessageConverter.java @@ -15,19 +15,20 @@ */ package org.springframework.data.rest.webmvc.alps; -import java.io.IOException; import java.lang.reflect.Type; import java.util.Arrays; +import org.springframework.core.MethodParameter; import org.springframework.core.convert.converter.Converter; import org.springframework.data.rest.webmvc.RootResourceInformation; import org.springframework.hateoas.alps.Alps; -import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.Assert; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.ObjectMapper; @@ -38,7 +39,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; * * @author Oliver Gierke */ -public class AlpsJsonHttpMessageConverter extends MappingJackson2HttpMessageConverter { +public class AlpsJsonHttpMessageConverter extends MappingJackson2HttpMessageConverter + implements ResponseBodyAdvice { private static final MediaType ALPS_MEDIA_TYPE = MediaType.parseMediaType("application/alps+json"); @@ -83,15 +85,22 @@ public class AlpsJsonHttpMessageConverter extends MappingJackson2HttpMessageConv /* * (non-Javadoc) - * @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#writeInternal(java.lang.Object, org.springframework.http.HttpOutputMessage) + * @see org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice#beforeBodyWrite(java.lang.Object, org.springframework.core.MethodParameter, org.springframework.http.MediaType, java.lang.Class, org.springframework.http.server.ServerHttpRequest, org.springframework.http.server.ServerHttpResponse) */ @Override - protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, - HttpMessageNotWritableException { + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> selectedConverterType, ServerHttpRequest request, + ServerHttpResponse response) { - Object toWrite = object instanceof RootResourceInformation ? converter.convert((RootResourceInformation) object) - : object; + return body instanceof RootResourceInformation ? converter.convert((RootResourceInformation) body) : body; + } - super.writeInternal(toWrite, outputMessage); + /* + * (non-Javadoc) + * @see org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice#supports(org.springframework.core.MethodParameter, java.lang.Class) + */ + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + return converterType.equals(AlpsJsonHttpMessageConverter.class); } } 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 97f24ee1b..515293b31 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 @@ -122,6 +122,7 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.Module; @@ -496,6 +497,10 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon handlerAdapter.setWebBindingInitializer(initializer); handlerAdapter.setMessageConverters(messageConverters); + if (config().metadataConfiguration().alpsEnabled()) { + handlerAdapter.setResponseBodyAdvice(Arrays.> asList(alpsJsonHttpMessageConverter())); + } + return handlerAdapter; } @@ -589,7 +594,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon List> messageConverters = new ArrayList>(); if (config().metadataConfiguration().alpsEnabled()) { - messageConverters.add(new AlpsJsonHttpMessageConverter(alpsConverter())); + messageConverters.add(alpsJsonHttpMessageConverter()); } if (config().getDefaultMediaType().equals(MediaTypes.HAL_JSON)) { @@ -609,6 +614,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon return messageConverters; } + @Bean + public AlpsJsonHttpMessageConverter alpsJsonHttpMessageConverter() { + return new AlpsJsonHttpMessageConverter(alpsConverter()); + } + /* * (non-Javadoc) * @see org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration#pageableResolver() 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 b970bfaa7..7bc9834c2 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 @@ -40,6 +40,7 @@ import org.springframework.data.geo.Point; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.webmvc.RepositoryLinksResource; import org.springframework.data.rest.webmvc.RestMediaTypes; +import org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter; import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module; import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; @@ -153,11 +154,12 @@ public class RepositoryRestMvConfigurationIntegrationTests { @Test public void registeredHttpMessageConvertersAreTypeConstrained() { - Collection converters = context.getBeansOfType( - MappingJackson2HttpMessageConverter.class).values(); + Collection converters = context + .getBeansOfType(MappingJackson2HttpMessageConverter.class).values(); for (HttpMessageConverter converter : converters) { - assertThat(converter, is(instanceOf(TypeConstrainedMappingJackson2HttpMessageConverter.class))); + assertThat(converter, is(anyOf(instanceOf(TypeConstrainedMappingJackson2HttpMessageConverter.class), + instanceOf(AlpsJsonHttpMessageConverter.class)))); } }