From 4ee1850a37f95ae35152c04b94a65171c3593036 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 14 Nov 2013 09:54:03 +0000 Subject: [PATCH] DATAREST-93 - Improvements after API updates in Spring Data Commons. Switched to the usage of Simple(Property|Association)Handler to avoid having to use raw PersistentProperty instances. --- .../rest/core/mapping/ResourceMappings.java | 4 +- .../rest/core/support/DomainObjectMerger.java | 44 +++++++++++---- .../webmvc/RepositorySearchController.java | 6 +- .../json/PersistentEntityJackson2Module.java | 55 +++++++++++-------- ...PersistentEntityToJsonSchemaConverter.java | 52 +++++++++++------- 5 files changed, 102 insertions(+), 59 deletions(-) diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java index 2564eedda..46010a1ab 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.Path; @@ -267,8 +266,7 @@ public class ResourceMappings implements Iterable { Assert.notNull(property, "PersistentProperty must not be null!"); this.property = property; this.typeMapping = typeMapping; - this.annotation = property instanceof AnnotationBasedPersistentProperty ? ((AnnotationBasedPersistentProperty) property) - .findAnnotation(RestResource.class) : null; + this.annotation = property.findAnnotation(RestResource.class); } /* diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/DomainObjectMerger.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/DomainObjectMerger.java index b5ad86628..79ab65e56 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/DomainObjectMerger.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/DomainObjectMerger.java @@ -18,15 +18,17 @@ package org.springframework.data.rest.core.support; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.Association; -import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.SimpleAssociationHandler; +import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.data.repository.support.Repositories; +import org.springframework.util.Assert; /** * @author Jon Brisbin + * @author Oliver Gierke */ public class DomainObjectMerger { @@ -35,32 +37,51 @@ public class DomainObjectMerger { @Autowired public DomainObjectMerger(Repositories repositories, ConversionService conversionService) { + + Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(conversionService, "ConversionService must not be null!"); + this.repositories = repositories; this.conversionService = conversionService; } - @SuppressWarnings({ "unchecked", "rawtypes" }) public void merge(Object from, Object target) { + if (null == from || null == target) { return; } + final BeanWrapper fromWrapper = BeanWrapper.create(from, conversionService); final BeanWrapper targetWrapper = BeanWrapper.create(target, conversionService); PersistentEntity entity = repositories.getPersistentEntity(target.getClass()); - entity.doWithProperties(new PropertyHandler() { + + entity.doWithProperties(new SimplePropertyHandler() { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimplePropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty) + */ @Override - public void doWithPersistentProperty(PersistentProperty persistentProperty) { - Object fromVal = fromWrapper.getProperty(persistentProperty); - if (null != fromVal && !fromVal.equals(targetWrapper.getProperty(persistentProperty))) { - targetWrapper.setProperty(persistentProperty, fromVal); + public void doWithPersistentProperty(PersistentProperty property) { + + Object fromVal = fromWrapper.getProperty(property); + if (null != fromVal && !fromVal.equals(targetWrapper.getProperty(property))) { + targetWrapper.setProperty(property, fromVal); } } }); - entity.doWithAssociations(new AssociationHandler() { + + entity.doWithAssociations(new SimpleAssociationHandler() { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimpleAssociationHandler#doWithAssociation(org.springframework.data.mapping.Association) + */ @Override - public void doWithAssociation(Association association) { - PersistentProperty persistentProperty = association.getInverse(); + public void doWithAssociation(Association> association) { + + PersistentProperty persistentProperty = association.getInverse(); Object fromVal = fromWrapper.getProperty(persistentProperty); if (null != fromVal && !fromVal.equals(targetWrapper.getProperty(persistentProperty))) { targetWrapper.setProperty(persistentProperty, fromVal); @@ -68,5 +89,4 @@ public class DomainObjectMerger { } }); } - } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java index b608d2679..b5ea01140 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java @@ -219,9 +219,11 @@ class RepositorySearchController extends AbstractRepositoryRestController { private Links getSearchLinks(Class domainType) { List links = new ArrayList(); - LinkBuilder builder = entityLinks.linkFor(domainType).slash(SEARCH); - for (ResourceMapping mapping : mappings.getSearchResourceMappings(domainType)) { + SearchResourceMappings searchMappings = mappings.getSearchResourceMappings(domainType); + LinkBuilder builder = entityLinks.linkFor(domainType).slash(searchMappings.getPath()); + + for (ResourceMapping mapping : searchMappings) { if (!mapping.isExported()) { continue; 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 f569facd8..fc3097dde 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 @@ -19,10 +19,10 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.TypeDescriptor; import org.springframework.data.mapping.Association; -import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.SimpleAssociationHandler; +import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.UriDomainClassConverter; @@ -234,26 +234,29 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init } } - @SuppressWarnings("rawtypes") - private class ResourceSerializer extends StdSerializer { + private class ResourceSerializer extends StdSerializer> { + @SuppressWarnings({ "unchecked", "rawtypes" }) private ResourceSerializer() { - super(PersistentEntityResource.class); + super((Class) PersistentEntityResource.class); } - @SuppressWarnings({ "unchecked" }) + /* + * (non-Javadoc) + * @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) + */ @Override - public void serialize(final PersistentEntityResource resource, final JsonGenerator jgen, + public void serialize(final PersistentEntityResource resource, final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonGenerationException { + if (LOG.isDebugEnabled()) { LOG.debug("Serializing PersistentEntity " + resource.getPersistentEntity()); } Object obj = resource.getContent(); - final PersistentEntity entity = resource.getPersistentEntity(); - - final BeanWrapper wrapper = BeanWrapper.create(obj, null); + 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()); @@ -262,12 +265,18 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init final List links = new ArrayList(); // Start with ResourceProcessor-added links links.addAll(resource.getLinks()); - jgen.writeStartObject(); + try { - entity.doWithProperties(new PropertyHandler() { + + entity.doWithProperties(new SimplePropertyHandler() { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimplePropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty) + */ @Override - public void doWithPersistentProperty(PersistentProperty property) { + public void doWithPersistentProperty(PersistentProperty property) { boolean idAvailableAndShallNotBeExposed = property.isIdProperty() && !config.isIdExposedFor(entity.getType()); @@ -276,10 +285,6 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init return; } - // if (property.isEntity() && maybeAddAssociationLink(builder, mappings, property, links)) { - // return; - // } - // Property is a normal or non-managed property. Object propertyValue = wrapper.getProperty(property); try { @@ -291,11 +296,16 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init }); // Add associations as links - entity.doWithAssociations(new AssociationHandler() { - @Override - public void doWithAssociation(Association association) { + entity.doWithAssociations(new SimpleAssociationHandler() { - PersistentProperty property = association.getInverse(); + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimpleAssociationHandler#doWithAssociation(org.springframework.data.mapping.Association) + */ + @Override + public void doWithAssociation(Association> association) { + + PersistentProperty property = association.getInverse(); if (!mappings.isMapped(property)) { return; @@ -315,9 +325,11 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init }); jgen.writeArrayFieldStart("links"); + for (Link l : links) { jgen.writeObject(l); } + jgen.writeEndArray(); } catch (IllegalStateException e) { @@ -327,5 +339,4 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init } } } - } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java index 63d89261e..8b4af4dce 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java @@ -13,19 +13,21 @@ import javax.validation.constraints.NotNull; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.data.mapping.Association; -import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.SimpleAssociationHandler; +import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.annotation.Description; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder; import org.springframework.hateoas.Link; +import org.springframework.util.Assert; /** * @author Jon Brisbin + * @author Oliver Gierke */ public class PersistentEntityToJsonSchemaConverter implements ConditionalGenericConverter { @@ -37,11 +39,17 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric private final Repositories repositories; /** + * Creates a new {@link PersistentEntityToJsonSchemaConverter} for the given {@link Repositories} and + * {@link ResourceMappings}. + * * @param repositories must not be {@literal null}. * @param mappings must not be {@literal null}. */ public PersistentEntityToJsonSchemaConverter(Repositories repositories, ResourceMappings mappings) { + Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(mappings, "ResourceMappings must not be null!"); + this.repositories = repositories; this.mappings = mappings; @@ -73,7 +81,10 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric return (JsonSchema) convert(domainType, STRING_TYPE, SCHEMA_TYPE); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) + */ @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { @@ -81,41 +92,42 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getType()); String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class) ? persistentEntity.getType() .getAnnotation(Description.class).value() : null; - final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), entityDesc); - persistentEntity.doWithProperties(new PropertyHandler() { + + persistentEntity.doWithProperties(new SimplePropertyHandler() { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty) + */ @Override - public void doWithPersistentProperty(PersistentProperty persistentProperty) { + public void doWithPersistentProperty(PersistentProperty persistentProperty) { + Class propertyType = persistentProperty.getType(); String type = uncapitalize(propertyType.getSimpleName()); - boolean notNull = persistentProperty.getField().isAnnotationPresent(NotNull.class) - || persistentProperty.getGetter().isAnnotationPresent(NotNull.class); - String desc = persistentProperty.getField().isAnnotationPresent(Description.class) ? persistentProperty - .getField().getAnnotation(Description.class).value() : persistentProperty.getGetter().isAnnotationPresent( - Description.class) ? persistentProperty.getGetter().getAnnotation(Description.class).value() : null; - JsonSchema.Property property; - if (persistentProperty.isCollectionLike()) { - property = new JsonSchema.ArrayProperty("array", desc, notNull); - } else { - property = new JsonSchema.Property(type, desc, notNull); - } + boolean notNull = persistentProperty.isAnnotationPresent(NotNull.class); + Description descriptionAnnotation = persistentProperty.findAnnotation(Description.class); + String desc = descriptionAnnotation == null ? null : descriptionAnnotation.value(); + + JsonSchema.Property property = persistentProperty.isCollectionLike() ? new JsonSchema.ArrayProperty("array", + desc, notNull) : new JsonSchema.Property(type, desc, notNull); jsonSchema.addProperty(persistentProperty.getName(), property); } }); final List links = new ArrayList(); - persistentEntity.doWithAssociations(new AssociationHandler() { + persistentEntity.doWithAssociations(new SimpleAssociationHandler() { /* * (non-Javadoc) * @see org.springframework.data.mapping.AssociationHandler#doWithAssociation(org.springframework.data.mapping.Association) */ @Override - public void doWithAssociation(Association association) { + public void doWithAssociation(Association> association) { - PersistentProperty persistentProperty = association.getInverse(); + PersistentProperty persistentProperty = association.getInverse(); if (!metadata.isExported(persistentProperty)) { return;