From af520871dc00fee3452c445602f7e3493e0ff203 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 19 Dec 2013 14:44:45 +0100 Subject: [PATCH] DATAREST-203, DATAREST-75 - Initial support for HAL. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces support for HAL rendering and making it the default rendering option. The configuration now registers an additional MappingJackson2HttpMessageConverter with the according HAL modules registered and makes this the default JSON renderer if the default media type is configured to HAL (which it is by default). To change this, set RepositoryRestConfiguration.setDefaultMediaType(…) back to application/json. Refactored the PersistentEntityJackson2Module to turn the PersistentEntityResource into it's correct shape (turning associations into links) and the delegate to the default rendering, so that the HAL customizations provided by Spring HATEOAS can kick in. Adapted test cases to the change in default representation format. Upgraded to Jackson 2.3.0, Spring 3.2.6 and Spring HATEOAS 0.9.0.BUILD-SNAPSHOT. Upgraded to Gradle 1.10. --- build.gradle | 8 +- gradle/wrapper/gradle-wrapper.jar | Bin 49875 -> 50508 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- .../config/RepositoryRestConfiguration.java | 3 +- .../rest/webmvc/RepositoryController.java | 3 +- .../webmvc/RepositoryEntityController.java | 15 +-- .../rest/webmvc/RepositoryLinksResource.java | 1 - ...RepositoryPropertyReferenceController.java | 7 +- .../RepositoryRestMvcConfiguration.java | 99 +++++++++++++--- .../json/PersistentEntityJackson2Module.java | 71 +++++++----- .../webmvc/AbstractWebIntegrationTests.java | 109 ++++++++++++++---- ...ryRestMvConfigurationIntegrationTests.java | 12 +- .../data/rest/webmvc/jpa/JpaWebTests.java | 20 ++++ .../PersistentEntitySerializationTests.java | 4 +- .../webmvc/json/RepositoryTestsConfig.java | 9 ++ .../rest/webmvc/mongodb/MongoWebTests.java | 8 +- 16 files changed, 271 insertions(+), 102 deletions(-) diff --git a/build.gradle b/build.gradle index 14c2bfd02..c5f08d132 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ ext { logbackVersion = "1.0.11" // Spring - springVersion = "3.2.5.RELEASE" - hateoasVersion = "0.8.0.RELEASE" + springVersion = "3.2.6.RELEASE" + hateoasVersion = "0.9.0.BUILD-SNAPSHOT" springPluginVersion = "0.8.0.RELEASE" springSecurityVersion = "3.1.3.RELEASE" sdCommonsVersion = "1.7.0.M1" @@ -20,7 +20,7 @@ ext { sdNeo4jVersion = "3.0.0.M1" // Libraries - jacksonVersion = "2.2.2" + jacksonVersion = "2.3.0" jodaVersion = "2.1" hibernateVersion = "4.2.0.Final" hibernateValidatorVersion = "4.3.1.Final" @@ -361,7 +361,7 @@ configure(rootProject) { task wrapper(type: Wrapper) { description = "Generates gradlew[.bat] scripts" - gradleVersion = "1.8" + gradleVersion = "1.10" doLast() { def gradleOpts = "-XX:MaxPermSize=1024m -Xmx1024m" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a7634b071cb255e91a4572934e55b8cd8877b3e4..d5c591c9c532da774b062aa6d7ab16405ff8700a 100644 GIT binary patch delta 38729 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 delta 38246 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⩔G(Cg^fWe9>zFo}4N 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9f82a2054..7e010bda6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Nov 12 18:38:36 CET 2013 +#Tue Dec 17 11:32:11 CET 2013 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java index 062376642..b493b720d 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; import org.springframework.util.Assert; @@ -36,7 +37,7 @@ public class RepositoryRestConfiguration { private String pageParamName = "page"; private String limitParamName = "limit"; private String sortParamName = "sort"; - private MediaType defaultMediaType = MediaType.APPLICATION_JSON; + private MediaType defaultMediaType = MediaTypes.HAL_JSON; private boolean returnBodyOnCreate = false; private boolean returnBodyOnUpdate = false; private List> exposeIdsFor = new ArrayList>(); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java index aa9878571..979a0cb37 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java @@ -54,8 +54,7 @@ public class RepositoryController extends AbstractRepositoryRestController { * @return */ @ResponseBody - @RequestMapping(value = "/", method = RequestMethod.GET, // - produces = { "application/json", "application/x-spring-data-compact+json" }) + @RequestMapping(value = "/", method = RequestMethod.GET) public RepositoryLinksResource listRepositories() { RepositoryLinksResource resource = new RepositoryLinksResource(); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java index 4c754ef41..32387427d 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java @@ -105,8 +105,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem } @ResponseBody - @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json", - "application/x-spring-data-verbose+json" }) + @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET) public Resources listEntities(final RepositoryRestRequest request, Pageable pageable, Sort sort) throws ResourceNotFoundException { @@ -158,8 +157,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem } @ResponseBody - @RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST, consumes = { "application/json" }, produces = { - "application/json", "text/uri-list" }) + @RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST, consumes = { "application/json" }) public ResponseEntity createNewEntity(RepositoryRestRequest repoRequest, PersistentEntityResource incoming) { @@ -189,9 +187,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem * @return * @throws ResourceNotFoundException */ - @ResponseBody - @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.GET, produces = { "application/json", - "application/x-spring-data-compact+json", "text/uri-list" }) + @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.GET) public ResponseEntity> getSingleEntity(RepositoryRestRequest repoRequest, @PathVariable String id) { RepositoryInvoker repoMethodInvoker = repoRequest.getRepositoryInvoker(); @@ -217,9 +213,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem * @param id * @return */ - @ResponseBody - @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT, consumes = { "application/json" }, - produces = { "application/json", "text/uri-list" }) + @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT, consumes = { "application/json" }) public ResponseEntity updateEntity(RepositoryRestRequest request, PersistentEntityResource incoming, @PathVariable String id) { @@ -251,7 +245,6 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem } @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.DELETE) - @ResponseBody public ResponseEntity deleteEntity(final RepositoryRestRequest repoRequest, @PathVariable final String id) throws ResourceNotFoundException, HttpRequestMethodNotSupportedException { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryLinksResource.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryLinksResource.java index a9f0e40db..97c881943 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryLinksResource.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryLinksResource.java @@ -12,5 +12,4 @@ public class RepositoryLinksResource extends Resources { public RepositoryLinksResource() { super(Collections.emptyList()); } - } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java index 1f33bfb35..70c155e09 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java @@ -96,9 +96,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes this.publisher = applicationEventPublisher; } - @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json", - "application/x-spring-data-verbose+json" }) - @ResponseBody + @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET) public ResponseEntity followPropertyReference(final RepositoryRestRequest repoRequest, @PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException { @@ -147,7 +145,6 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes } @RequestMapping(value = BASE_MAPPING, method = RequestMethod.DELETE) - @ResponseBody public ResponseEntity deletePropertyReference(final RepositoryRestRequest repoRequest, @PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException, HttpRequestMethodNotSupportedException { @@ -193,7 +190,6 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT); } - @ResponseBody @RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET, produces = { "application/json", "application/x-spring-data-verbose+json", "application/x-spring-data-compact+json", "text/uri-list" }) public ResponseEntity followPropertyReference(final RepositoryRestRequest repoRequest, @@ -246,7 +242,6 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK); } - @ResponseBody @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/x-spring-data-compact+json", "text/uri-list" }) public ResponseEntity followPropertyReferenceCompact(RepositoryRestRequest repoRequest, 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 0b15b6667..c5d59b431 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 @@ -21,7 +21,6 @@ import java.util.Collections; import java.util.List; import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; @@ -61,8 +60,15 @@ import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler; import org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.hateoas.EntityLinks; +import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RelProvider; import org.springframework.hateoas.ResourceProcessor; +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.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -89,6 +95,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; @Configuration @ComponentScan(basePackageClasses = RepositoryRestController.class, includeFilters = @Filter(RepositoryRestController.class), useDefaultFilters = false) +@EnableHypermediaSupport(type = HypermediaType.HAL) @ImportResource("classpath*:META-INF/spring-data-rest/**/*.xml") public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebConfiguration { @@ -99,6 +106,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Autowired ListableBeanFactory beanFactory; @Autowired(required = false) List> resourceProcessors = Collections.emptyList(); + @Autowired(required = false) RelProvider relProvider; + @Autowired(required = false) CurieProvider curieProvider; @Bean public Repositories repositories() { @@ -256,15 +265,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon */ @Bean public ObjectMapper objectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); - // Our special PersistentEntityResource Module - objectMapper.registerModule(persistentEntityJackson2Module()); - Jackson2DatatypeHelper.configureObjectMapper(objectMapper); - // Configure custom Modules - configureJacksonObjectMapper(objectMapper); - - return objectMapper; + return basicObjectMapper(); } /** @@ -274,14 +275,58 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon */ @Bean public MappingJackson2HttpMessageConverter jacksonHttpMessageConverter() { + + List mediaTypes = new ArrayList(); + mediaTypes.addAll(Arrays.asList(MediaType.valueOf("application/schema+json"), + MediaType.valueOf("application/x-spring-data-verbose+json"), + MediaType.valueOf("application/x-spring-data-compact+json"))); + + // Configure this mapper to be used if HAL is not the default media type + if (!config().getDefaultMediaType().equals(MediaTypes.HAL_JSON)) { + mediaTypes.add(MediaType.APPLICATION_JSON); + } + MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(); jacksonConverter.setObjectMapper(objectMapper()); - jacksonConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, - MediaType.valueOf("application/schema+json"), MediaType.valueOf("application/x-spring-data-verbose+json"), - MediaType.valueOf("application/x-spring-data-compact+json"))); + jacksonConverter.setSupportedMediaTypes(mediaTypes); + return jacksonConverter; } + // + // HAL setup + // + + @Bean + public MappingJackson2HttpMessageConverter halJacksonHttpMessageConverter() { + + ArrayList mediaTypes = new ArrayList(); + mediaTypes.add(MediaTypes.HAL_JSON); + + // Enable returning HAL if application/json is asked if it's configured to be the default type + if (config().getDefaultMediaType().equals(MediaTypes.HAL_JSON)) { + mediaTypes.add(MediaType.APPLICATION_JSON); + } + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setObjectMapper(halObjectMapper()); + converter.setSupportedMediaTypes(mediaTypes); + + return converter; + } + + @Bean + public ObjectMapper halObjectMapper() { + + HalHandlerInstantiator instantiator = new HalHandlerInstantiator(getDefaultedRelProvider(), curieProvider); + + ObjectMapper mapper = basicObjectMapper(); + mapper.registerModule(new Jackson2HalModule()); + mapper.setHandlerInstantiator(instantiator); + + return mapper; + } + /** * The {@link HttpMessageConverter} used to create {@literal text/uri-list} responses. * @@ -338,12 +383,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon Repositories repositories = repositories(); RepositoryRestConfiguration config = config(); - try { - RelProvider relProvider = beanFactory.getBean(RelProvider.class); - return new ResourceMappings(config, repositories, relProvider); - } catch (NoSuchBeanDefinitionException e) { - return new ResourceMappings(config, repositories); - } + return new ResourceMappings(config, repositories, getDefaultedRelProvider()); } /** @@ -381,9 +421,13 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon } private List> defaultMessageConverters() { + List> messageConverters = new ArrayList>(); + + messageConverters.add(halJacksonHttpMessageConverter()); messageConverters.add(jacksonHttpMessageConverter()); messageConverters.add(uriListHttpMessageConverter()); + return messageConverters; } @@ -393,6 +437,23 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon resourceMetadataHandlerMethodArgumentResolver()); } + private ObjectMapper basicObjectMapper() { + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); + // Our special PersistentEntityResource Module + objectMapper.registerModule(persistentEntityJackson2Module()); + Jackson2DatatypeHelper.configureObjectMapper(objectMapper); + // Configure custom Modules + configureJacksonObjectMapper(objectMapper); + + return objectMapper; + } + + private RelProvider getDefaultedRelProvider() { + return this.relProvider != null ? relProvider : new EvoInflectorRelProvider(); + } + /** * Override this method to add additional configuration. * diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java index 4d56cb7ed..e14c79938 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,9 +32,12 @@ import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.PersistentEntityResource; import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder; import org.springframework.hateoas.Link; +import org.springframework.hateoas.Resource; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.Assert; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -54,11 +58,13 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init private static final long serialVersionUID = -7289265674870906323L; private static final Logger LOG = LoggerFactory.getLogger(PersistentEntityJackson2Module.class); private static final TypeDescriptor URI_TYPE = TypeDescriptor.valueOf(URI.class); + + private final ResourceMappings mappings; + private final ConversionService conversionService; + @Autowired private Repositories repositories; @Autowired private RepositoryRestConfiguration config; @Autowired private UriDomainClassConverter uriDomainClassConverter; - private final ResourceMappings mappings; - private final ConversionService conversionService; public PersistentEntityJackson2Module(ResourceMappings resourceMappings, ConversionService conversionService) { @@ -123,11 +129,10 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init @SuppressWarnings({ "unchecked", "incomplete-switch", "unused" }) @Override public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - Object entity = instantiateClass(getValueClass()); + Object entity = instantiateClass(handledType()); BeanWrapper wrapper = BeanWrapper.create(entity, conversionService); - - ResourceMetadata metadata = mappings.getMappingFor(getValueClass()); + ResourceMetadata metadata = mappings.getMappingFor(handledType()); for (JsonToken tok = jp.nextToken(); tok != JsonToken.END_OBJECT; tok = jp.nextToken()) { String name = jp.getCurrentName(); @@ -256,14 +261,14 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init final PersistentEntity entity = resource.getPersistentEntity(); final BeanWrapper, Object> wrapper = BeanWrapper.create(obj, null); final Object entityId = wrapper.getProperty(entity.getIdProperty()); - final ResourceMappings mappings = new ResourceMappings(config, repositories); final ResourceMetadata metadata = mappings.getMappingFor(entity.getType()); final RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash(entityId); final List links = new ArrayList(); // Start with ResourceProcessor-added links links.addAll(resource.getLinks()); - jgen.writeStartObject(); + + final Map model = new LinkedHashMap(); try { @@ -284,12 +289,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init } // Property is a normal or non-managed property. - Object propertyValue = wrapper.getProperty(property); - try { - jgen.writeObjectField(property.getName(), propertyValue); - } catch (IOException e) { - throw new IllegalStateException(e); - } + model.put(property.getName(), wrapper.getProperty(property)); } }); @@ -310,28 +310,45 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init } // Association Link was not added, probably because this isn't a managed type. Add value of property inline. - Object propertyValue = wrapper.getProperty(property); - try { - jgen.writeObjectField(property.getName(), propertyValue); - } catch (IOException e) { - throw new IllegalStateException(e); + if (metadata.isExported(property)) { + model.put(property.getName(), wrapper.getProperty(property)); } + } }); - jgen.writeArrayFieldStart("links"); - - for (Link l : links) { - jgen.writeObject(l); - } - - jgen.writeEndArray(); + MapResource mapResource = new MapResource(model, links); + jgen.writeObject(mapResource); } catch (IllegalStateException e) { throw (IOException) e.getCause(); - } finally { - jgen.writeEndObject(); } } } + + private static class MapResource extends Resource> { + + /** + * @param content + * @param links + */ + public MapResource(Map content, Iterable links) { + super(content, links); + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.Resource#getContent() + */ + @Override + @JsonIgnore + public Map getContent() { + return super.getContent(); + } + + @JsonAnyGetter + public Map any() { + return getContent(); + } + } } 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 61a0b3f53..cebc410b1 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 @@ -29,7 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkDiscoverer; -import org.springframework.hateoas.core.DefaultLinkDiscoverer; +import org.springframework.hateoas.LinkDiscoverers; +import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; @@ -52,20 +53,24 @@ import com.jayway.jsonpath.JsonPath; @ContextConfiguration(classes = RepositoryRestMvcConfiguration.class) public abstract class AbstractWebIntegrationTests { + protected static MediaType DEFAULT_MEDIA_TYPE = org.springframework.hateoas.MediaTypes.HAL_JSON; + @Autowired WebApplicationContext context; + @Autowired LinkDiscoverers discoverers; protected MockMvc mvc; - LinkDiscoverer links = new DefaultLinkDiscoverer(); @Before public void setUp() { - mvc = MockMvcBuilders.webAppContextSetup(context).build(); + + mvc = MockMvcBuilders.webAppContextSetup(context).// + defaultRequest(get("/").accept(DEFAULT_MEDIA_TYPE)).build(); } protected MockHttpServletResponse request(String href, MediaType contentType) throws Exception { return mvc.perform(get(href).accept(contentType)). // andExpect(status().isOk()). // - andExpect(content().contentType(MediaType.APPLICATION_JSON)). // + andExpect(content().contentType(contentType)). // andReturn().getResponse(); } @@ -74,7 +79,7 @@ public abstract class AbstractWebIntegrationTests { } protected MockHttpServletResponse request(String href) throws Exception { - return request(href, MediaType.APPLICATION_JSON); + return request(href, DEFAULT_MEDIA_TYPE); } protected ResultActions follow(Link link) throws Exception { @@ -93,21 +98,30 @@ public abstract class AbstractWebIntegrationTests { } protected List discover(Link root, String rel) throws Exception { - String s = mvc.perform(get(root.getHref())).andExpect(status().isOk()).andExpect(hasLinkWithRel(rel)).andReturn() - .getResponse().getContentAsString(); - return links.findLinksWithRel(rel, s); + + MockHttpServletResponse response = mvc.perform(get(root.getHref()).accept(DEFAULT_MEDIA_TYPE)).// + andExpect(status().isOk()).// + andExpect(hasLinkWithRel(rel)).// + andReturn().getResponse(); + + String s = response.getContentAsString(); + return getDiscoverer(response).findLinksWithRel(rel, s); } protected Link discoverUnique(Link root, String rel) throws Exception { - String s = mvc.perform(get(root.getHref())).andExpect(status().isOk()).andExpect(hasLinkWithRel(rel)).andReturn() - .getResponse().getContentAsString(); - return links.findLinkWithRel(rel, s); + + MockHttpServletResponse response = mvc.perform(get(root.getHref()).accept(DEFAULT_MEDIA_TYPE)).// + andExpect(status().isOk()).// + andExpect(hasLinkWithRel(rel)).// + andReturn().getResponse(); + + return assertHasLinkWithRel(rel, response); } protected Link assertHasLinkWithRel(String rel, MockHttpServletResponse response) throws Exception { String content = response.getContentAsString(); - Link link = links.findLinkWithRel(rel, content); + Link link = getDiscoverer(response).findLinkWithRel(rel, content); assertThat("Expected to find link with rel " + rel + " but found none in " + content + "!", link, is(notNullValue())); @@ -117,8 +131,8 @@ public abstract class AbstractWebIntegrationTests { protected Link assertHasContentLinkWithRel(String rel, MockHttpServletResponse response) throws Exception { - String href = JsonPath - .read(response.getContentAsString(), String.format("$..links[?(@.rel == '%s')].href[0]", rel)).toString(); + String content = response.getContentAsString(); + String href = JsonPath.read(content, String.format("$.._links.%s.href[0]", rel)).toString(); assertThat("Expected to find a link with rel" + rel + " in the content section of the response!", href, is(notNullValue())); @@ -128,23 +142,43 @@ public abstract class AbstractWebIntegrationTests { protected void assertDoesNotHaveLinkWithRel(String rel, MockHttpServletResponse response) throws Exception { String content = response.getContentAsString(); - Link link = links.findLinkWithRel(rel, content); + Link link = getDiscoverer(response).findLinkWithRel(rel, content); assertThat("Expected not to find link with rel " + rel + " but found " + link + "!", link, is(nullValue())); } - protected void assertHasJsonPathValue(String path, MockHttpServletResponse response) throws Exception { - assertThat(JsonPath.read(response.getContentAsString(), path), is(notNullValue())); + private LinkDiscoverer getDiscoverer(MockHttpServletResponse response) { + + String contentType = response.getContentType(); + LinkDiscoverer linkDiscovererFor = discoverers.getLinkDiscovererFor(contentType); + + assertThat("Did not find a LinkDiscoverer for returned media type " + contentType + "!", linkDiscovererFor, + is(notNullValue())); + + return linkDiscovererFor; + } + + @SuppressWarnings("unchecked") + protected T assertHasJsonPathValue(String path, MockHttpServletResponse response) throws Exception { + + Object jsonPathResult = JsonPath.read(response.getContentAsString(), path); + assertThat(jsonPathResult, is(notNullValue())); + + return (T) jsonPathResult; } protected ResultMatcher hasLinkWithRel(final String rel) { return new ResultMatcher() { + @Override public void match(MvcResult result) throws Exception { - String s = result.getResponse().getContentAsString(); - assertThat("Expected to find link with rel " + rel + " but found none in " + s, links.findLinkWithRel(rel, s), - notNullValue()); + + MockHttpServletResponse response = result.getResponse(); + String s = response.getContentAsString(); + + assertThat("Expected to find link with rel " + rel + " but found none in " + s, // + getDiscoverer(response).findLinkWithRel(rel, s), notNullValue()); } }; } @@ -155,17 +189,22 @@ public abstract class AbstractWebIntegrationTests { @Override public void match(MvcResult result) throws Exception { - String s = result.getResponse().getContentAsString(); - assertThat("Expected not to find link with rel " + rel + " but found one in " + s, - links.findLinkWithRel(rel, s), nullValue()); + + MockHttpServletResponse response = result.getResponse(); + String s = response.getContentAsString(); + + assertThat("Expected not to find link with rel " + rel + " but found one in " + s, // + getDiscoverer(response).findLinkWithRel(rel, s), nullValue()); } }; } + // Root test cases + @Test public void exposesRootResource() throws Exception { - ResultActions actions = mvc.perform(get("/")).andExpect(status().isOk()); + ResultActions actions = mvc.perform(get("/").accept(DEFAULT_MEDIA_TYPE)).andExpect(status().isOk()); for (String rel : expectedRootLinkRels()) { actions.andExpect(hasLinkWithRel(rel)); @@ -194,5 +233,27 @@ public abstract class AbstractWebIntegrationTests { } } + /** + * @see DATAREST-203 + */ + @Test + public void servesHalWhenRequested() throws Exception { + + mvc.perform(get("/")). // + andExpect(content().contentType(MediaTypes.HAL_JSON)). // + andExpect(jsonPath("$._links", notNullValue())); + } + + /** + * @see DATAREST-203 + */ + @Test + public void servesHalWhenJsonIsRequested() throws Exception { + + mvc.perform(get("/").accept(MediaType.APPLICATION_JSON)). // + andExpect(content().contentType(MediaType.APPLICATION_JSON)). // + andExpect(jsonPath("$._links", notNullValue())); + } + protected abstract Iterable expectedRootLinkRels(); } 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 50606ec2e..a02930e00 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 @@ -22,8 +22,12 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.data.rest.webmvc.RepositoryLinksResource; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.hateoas.core.DefaultRelProvider; +import org.springframework.http.converter.HttpMessageConverter; + +import com.fasterxml.jackson.databind.ObjectMapper; /** * @author Oliver Gierke @@ -45,8 +49,14 @@ public class RepositoryRestMvConfigurationIntegrationTests { } @Test - public void foo() { + public void assertBeansBeingSetUp() throws Exception { + context.getBean(PageableHandlerMethodArgumentResolver.class); + + // Verify HAL setup + context.getBean("halJacksonHttpMessageConverter", HttpMessageConverter.class); + ObjectMapper mapper = context.getBean("halObjectMapper", ObjectMapper.class); + mapper.writeValueAsString(new RepositoryLinksResource()); } @Configuration diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java index 35c87a431..91416907c 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java @@ -47,6 +47,8 @@ import org.springframework.util.StringUtils; @ContextConfiguration(classes = JpaRepositoryConfig.class) public class JpaWebTests extends AbstractWebIntegrationTests { + static final String LINK_TO_SIBLINGS_OF = "$._embedded..[?(@.firstName == '%s')]._links.siblings.href[0]"; + @Autowired TestDataPopulator loader; @Autowired ResourceMappings mappings; @@ -139,6 +141,24 @@ public class JpaWebTests extends AbstractWebIntegrationTests { ).andExpect(status().isCreated()); } + @Test + public void listsSiblingsWithContentCorrectly() throws Exception { + + MockHttpServletResponse response = mvc.perform(get("/people")).andReturn().getResponse(); + String href = assertHasJsonPathValue(String.format(LINK_TO_SIBLINGS_OF, "John"), response); + + mvc.perform(get(href)).andExpect(status().isOk()); + } + + @Test + public void listsEmptySiblingsCorrectly() throws Exception { + + MockHttpServletResponse response = mvc.perform(get("/people")).andReturn().getResponse(); + String href = assertHasJsonPathValue(String.format(LINK_TO_SIBLINGS_OF, "Billy Bob"), response); + + mvc.perform(get(href)).andExpect(status().isOk()); + } + private String readFile(String name) throws Exception { ClassPathResource file = new ClassPathResource(name, getClass()); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java index 8627f5a3f..c1ad4929e 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java @@ -18,7 +18,7 @@ import org.springframework.data.rest.webmvc.jpa.Person; import org.springframework.data.rest.webmvc.jpa.PersonRepository; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkDiscoverer; -import org.springframework.hateoas.core.DefaultLinkDiscoverer; +import org.springframework.hateoas.hal.HalLinkDiscoverer; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -44,7 +44,7 @@ public class PersistentEntitySerializationTests { @Before public void setUp() { - linkDiscoverer = new DefaultLinkDiscoverer(); + linkDiscoverer = new HalLinkDiscoverer(); } @Test diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java index 47bfc61b3..3bfa71a43 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java @@ -16,6 +16,9 @@ import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig; import org.springframework.data.rest.webmvc.jpa.Person; import org.springframework.data.rest.webmvc.jpa.PersonRepository; import org.springframework.format.support.DefaultFormattingConversionService; +import org.springframework.hateoas.RelProvider; +import org.springframework.hateoas.core.EvoInflectorRelProvider; +import org.springframework.hateoas.hal.Jackson2HalModule; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; @@ -77,8 +80,14 @@ public class RepositoryTestsConfig { @Bean public ObjectMapper objectMapper() { + + RelProvider relProvider = new EvoInflectorRelProvider(); ObjectMapper mapper = new ObjectMapper(); + + mapper.registerModule(new Jackson2HalModule()); mapper.registerModule(persistentEntityModule()); + mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, null)); + return mapper; } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java index 9a0eb9a2b..eb4ed7a8f 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests; import org.springframework.hateoas.Link; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; /** * Integration tests for MongoDB repositories. @@ -83,7 +84,8 @@ public class MongoWebTests extends AbstractWebIntegrationTests { public void foo() throws Exception { Link profileLink = discoverUnique("profiles"); - follow(profileLink).andExpect(jsonPath("$.content").value(hasSize(2))); + follow(profileLink).// + andExpect(jsonPath("$._embedded.profiles").value(hasSize(2))); } @Test @@ -91,6 +93,8 @@ public class MongoWebTests extends AbstractWebIntegrationTests { Link usersLink = discoverUnique("users"); Link userLink = assertHasContentLinkWithRel("self", request(usersLink)); - follow(userLink).andExpect(jsonPath("$.address.zipCode").value(is(notNullValue()))); + follow(userLink).// + andDo(MockMvcResultHandlers.print()). // + andExpect(jsonPath("$.address.zipCode").value(is(notNullValue()))); } }