From a5b182471ab6e8d15fc2b9caa490cb78aaafa2c9 Mon Sep 17 00:00:00 2001 From: John Blum Date: Sat, 16 Dec 2017 18:23:29 -0800 Subject: [PATCH] SGF-705 - Fix MappingPdxSerializer to ignore transient, non-writable and non-entity-based (simple type) properties and fields. --- .../data/gemfire/GemfireUtils.java | 3 + .../mapping/GemfirePersistentProperty.java | 9 +- .../gemfire/mapping/MappingPdxSerializer.java | 371 ++++++++++------ .../data/gemfire/util/CacheUtils.java | 25 ++ .../MappingPdxSerializerIntegrationTests.java | 180 +++++--- .../MappingPdxSerializerUnitTests.java | 404 +++++++++++++++--- .../gemfire/repository/sample/Address.java | 37 +- .../gemfire/repository/sample/Person.java | 1 + 8 files changed, 784 insertions(+), 246 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java index 0060c591..bfe47f5a 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java @@ -44,6 +44,7 @@ public abstract class GemfireUtils extends RegionUtils { /* (non-Javadoc) */ public static String apacheGeodeProductName() { + try { return GemFireVersion.getProductName(); } @@ -54,6 +55,7 @@ public abstract class GemfireUtils extends RegionUtils { /* (non-Javadoc) */ public static String apacheGeodeVersion() { + try { return CacheFactory.getVersion(); } @@ -95,6 +97,7 @@ public abstract class GemfireUtils extends RegionUtils { /* (non-Javadoc) */ public static boolean isGemfireVersion8OrAbove() { + try { return isGemfireVersionGreaterThanEqualTo(8.0); } diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java index 08f33975..d6d01214 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java @@ -18,6 +18,7 @@ package org.springframework.data.gemfire.mapping; import static org.springframework.data.gemfire.util.CollectionUtils.*; +import java.lang.reflect.Modifier; import java.util.Set; import org.springframework.data.annotation.Id; @@ -86,11 +87,17 @@ public class GemfirePersistentProperty extends AnnotationBasedPersistentProperty return (super.isIdProperty() || SUPPORTED_IDENTIFIER_NAMES.contains(getName())); } + @Override + public boolean isTransient() { + return super.isTransient() + || getProperty().getField().filter(field -> Modifier.isTransient(field.getModifiers())).isPresent(); + } + /** * @inheritDoc */ @Override public boolean usePropertyAccess() { - return (super.usePropertyAccess() || !getProperty().isFieldBacked()); + return super.usePropertyAccess() || !getProperty().isFieldBacked(); } } diff --git a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java index 67b540b4..97541331 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -18,11 +18,11 @@ package org.springframework.data.gemfire.mapping; import java.util.Collections; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.geode.pdx.PdxReader; import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -38,6 +38,7 @@ import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; import org.springframework.data.mapping.model.SpELContext; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * GemFire {@link PdxSerializer} implementation that uses a Spring Data GemFire {@link GemfireMappingContext} @@ -50,26 +51,28 @@ import org.springframework.util.Assert; * @see org.springframework.context.ApplicationContextAware * @see org.springframework.core.convert.ConversionService * @see org.springframework.data.convert.EntityInstantiator + * @see org.springframework.data.convert.EntityInstantiators * @see org.springframework.data.mapping.PersistentEntity * @see org.springframework.data.mapping.PersistentPropertyAccessor - * @see org.springframework.data.mapping.model.PersistentEntityParameterValueProvider - * @see org.springframework.data.mapping.model.SpELContext + * @see org.springframework.data.mapping.model.ConvertingPropertyAccessor * @see org.apache.geode.pdx.PdxReader * @see org.apache.geode.pdx.PdxSerializer * @see org.apache.geode.pdx.PdxWriter + * @since 1.2.0 */ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAware { private final ConversionService conversionService; - private EntityInstantiators instantiators; + private EntityInstantiators entityInstantiators; private final GemfireMappingContext mappingContext; - protected final Log log = LogFactory.getLog(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); private Map, PdxSerializer> customSerializers; + // TODO: decide what to do with this; the SpELContext is not used private SpELContext context; /** @@ -89,8 +92,8 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw public static MappingPdxSerializer create(GemfireMappingContext mappingContext, ConversionService conversionService) { - mappingContext = (mappingContext != null ? mappingContext : new GemfireMappingContext()); - conversionService = (conversionService != null ? conversionService : new DefaultConversionService()); + mappingContext = mappingContext != null ? mappingContext : new GemfireMappingContext(); + conversionService = conversionService != null ? conversionService : new DefaultConversionService(); return new MappingPdxSerializer(mappingContext, conversionService); } @@ -115,166 +118,67 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw */ public MappingPdxSerializer(GemfireMappingContext mappingContext, ConversionService conversionService) { - Assert.notNull(mappingContext, "GemfireMappingContext must not be null"); - Assert.notNull(conversionService, "ConversionService must not be null"); + Assert.notNull(mappingContext, "MappingContext is required"); + Assert.notNull(conversionService, "ConversionService is required"); this.mappingContext = mappingContext; this.conversionService = conversionService; - this.instantiators = new EntityInstantiators(); + this.entityInstantiators = new EntityInstantiators(); this.customSerializers = Collections.emptyMap(); this.context = new SpELContext(PdxReaderPropertyAccessor.INSTANCE); } /** - * {@inheritDoc} + * Configures a reference to the Spring {@link ApplicationContext}. + * + * @param applicationContext reference to the Spring {@link ApplicationContext}. + * @see org.springframework.context.ApplicationContext */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = new SpELContext(this.context, applicationContext); } - /* (non-Javadoc) */ + /** + * Returns a reference to the configured {@link ConversionService} used to convert data store types + * to application domain object types. + * + * @return a refernce to the configured {@link ConversionService}. + * @see org.springframework.core.convert.ConversionService + */ protected ConversionService getConversionService() { - return conversionService; + return this.conversionService; } /** - * Configures custom PDX serializers to use for specific class types. + * Configures custom {@link PdxSerializer PDX serializers} used to serialize + * specific application {@link Class class types}. * - * @param customSerializers a mapping of domain object class types and their corresponding PDX serializer. + * @param customSerializers mapping of application domain object {@link Class class types} + * to {@link PdxSerializer PDX serializers} used to handle those {@link Class class types}. + * @throws IllegalArgumentException if the {@link Map} is {@literal null}. + * @see org.apache.geode.pdx.PdxSerializer + * @see java.util.Map */ public void setCustomSerializers(Map, PdxSerializer> customSerializers) { - Assert.notNull(customSerializers, "Custom PdxSerializers must not be null"); + + Assert.notNull(customSerializers, "Custom PdxSerializers are required"); + this.customSerializers = customSerializers; } - /* (non-Javadoc) */ - protected Map, PdxSerializer> getCustomSerializers() { - return Collections.unmodifiableMap(customSerializers); - } - /** - * Configures the {@link EntityInstantiator}s used to create the instances read by this PdxSerializer. + * Returns a {@link Map mapping} of application domain object {@link Class types} + * to custom {@link PdxSerializer PdxSerializers} used to handle custom serialization logic + * for the application domain objects. * - * @param gemfireInstantiators must not be {@literal null}. + * @return a {@link Map mapping} of application domain object {@link Class types} + * to custom {@link PdxSerializer PdxSerializers}. + * @see org.apache.geode.pdx.PdxSerializer + * @see java.util.Map */ - public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { - Assert.notNull(gemfireInstantiators, "EntityInstantiators must not be null"); - this.instantiators = new EntityInstantiators(gemfireInstantiators); - } - - /* (non-Javadoc) */ - protected EntityInstantiators getGemfireInstantiators() { - return instantiators; - } - - /* (non-Javadoc) */ - protected GemfireMappingContext getMappingContext() { - return mappingContext; - } - - /** - * {@inheritDoc} - */ - @Override - public Object fromData(Class type, PdxReader reader) { - - GemfirePersistentEntity entity = getPersistentEntity(type); - - Object instance = getInstantiatorFor(entity).createInstance(entity, - new PersistentEntityParameterValueProvider<>(entity, new GemfirePropertyValueProvider(reader), null)); - - PersistentPropertyAccessor propertyAccessor = - new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService()); - - entity.doWithProperties((PropertyHandler) persistentProperty -> { - - if (!entity.isConstructorArgument(persistentProperty)) { - - PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); - - Object value = null; - - try { - if (log.isDebugEnabled()) { - log.debug(String.format("Setting property [%1$s] for entity [%2$s] of type [%3$s] from PDX%4$s", - persistentProperty.getName(), instance, type, (customSerializer != null ? - String.format(" using custom PdxSerializer [%1$s]", customSerializer) : ""))); - } - - value = (customSerializer != null - ? customSerializer.fromData(persistentProperty.getType(), reader) - : reader.readField(persistentProperty.getName())); - - if (log.isDebugEnabled()) { - log.debug(String.format("... with value [%s]", value)); - } - - propertyAccessor.setProperty(persistentProperty, value); - } - catch (Exception e) { - throw new MappingException(String.format( - "While setting value [%1$s] of property [%2$s] for entity of type [%3$s] from PDX%4$s", - value, persistentProperty.getName(), type, (customSerializer != null ? - String.format(" using custom PdxSerializer [%14s]", customSerializer) : "")), e); - } - } - }); - - return propertyAccessor.getBean(); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public boolean toData(Object value, PdxWriter writer) { - - GemfirePersistentEntity entity = getPersistentEntity(value); - - PersistentPropertyAccessor propertyAccessor = - new ConvertingPropertyAccessor(entity.getPropertyAccessor(value), getConversionService()); - - entity.doWithProperties((PropertyHandler) persistentProperty -> { - - PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); - - Object propertyValue = null; - - try { - propertyValue = propertyAccessor.getProperty(persistentProperty); - - if (log.isDebugEnabled()) { - log.debug(String.format("Serializing entity property [%1$s] value [%2$s] of type [%3$s] to PDX%4$s", - persistentProperty.getName(), propertyValue, value.getClass(), (customSerializer != null ? - String.format(" using custom PdxSerializer [%s]", customSerializer) : ""))); - } - - if (customSerializer != null) { - customSerializer.toData(propertyValue, writer); - } - else { - writer.writeField(persistentProperty.getName(), propertyValue, - (Class) persistentProperty.getType()); - } - } - catch (Exception e) { - throw new MappingException(String.format( - "While serializing entity property [%1$s] value [%2$s] of type [%3$s] to PDX%4$s", - persistentProperty.getName(), propertyValue, value.getClass(), - (customSerializer != null ? String.format(" using custom PdxSerializer [%1$s].", - customSerializer.getClass().getName()) : ".")), e); - } - }); - - GemfirePersistentProperty idProperty = entity.getIdProperty(); - - if (idProperty != null) { - writer.markIdentityField(idProperty.getName()); - } - - return true; + protected Map, PdxSerializer> getCustomSerializers() { + return Collections.unmodifiableMap(this.customSerializers); } /** @@ -285,11 +189,50 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * for the given class type was registered. * @see #getCustomSerializers() * @see org.apache.geode.pdx.PdxSerializer + * @see java.lang.Class */ protected PdxSerializer getCustomSerializer(Class type) { return getCustomSerializers().get(type); } + /** + * Configures the {@link EntityInstantiator EntityInstantiators} used to create the instances + * read by this {@link PdxSerializer}. + * + * @param entityInstantiators {@link EntityInstantiator EntityInstantiators} used to create the instances + * read by this {@link PdxSerializer}; must not be {@literal null}. + * @see org.springframework.data.convert.EntityInstantiator + */ + public void setGemfireInstantiators(EntityInstantiators entityInstantiators) { + + Assert.notNull(entityInstantiators, "EntityInstantiators are required"); + + this.entityInstantiators = entityInstantiators; + } + + /** + * Configures the {@link EntityInstantiator EntityInstantiators} used to create the instances + * read by this {@link PdxSerializer}. + * + * @param gemfireInstantiators mapping of {@link Class types} to {@link EntityInstantiator} objects; + * must not be {@literal null}. + * @see org.springframework.data.convert.EntityInstantiator + * @see java.util.Map + */ + public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { + setGemfireInstantiators(new EntityInstantiators(gemfireInstantiators)); + } + + /** + * Returns the configured {@link EntityInstantiators} handling instantiation for GemFire persistent entities. + * + * @return the configured {@link EntityInstantiators} handling instantiation for GemFire persistent entities. + * @see org.springframework.data.convert.EntityInstantiators + */ + protected EntityInstantiators getGemfireInstantiators() { + return this.entityInstantiators; + } + /** * Looks up and returns an EntityInstantiator to construct and initialize an instance of the object defined * by the given PersistentEntity (meta-data). @@ -303,6 +246,28 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return getGemfireInstantiators().getInstantiatorFor(entity); } + /** + * Returns a reference to the configured {@link Logger} used to log {@link String messages} + * about the functions of this {@link PdxSerializer}. + * + * @return a reference to the configured {@link Logger}. + * @see org.slf4j.Logger + */ + protected Logger getLogger() { + return this.logger; + } + + /** + * Returns a reference to the configured {@link GemfireMappingContext mapping context} used to handling mapping + * logic between GemFire persistent entities and application domain object {@link Class types}. + * + * @return a reference to the configured {@link GemfireMappingContext mapping context} for Pivotal GemFire. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + */ + protected GemfireMappingContext getMappingContext() { + return this.mappingContext; + } + /** * Looks up and returns the {@link PersistentEntity} meta-data for the given entity object. * @@ -326,4 +291,128 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw protected GemfirePersistentEntity getPersistentEntity(Class entityType) { return getMappingContext().getPersistentEntity(entityType); } + + @Override + public Object fromData(Class type, PdxReader reader) { + + GemfirePersistentEntity entity = getPersistentEntity(type); + + Object instance = getInstantiatorFor(entity) + .createInstance(entity, new PersistentEntityParameterValueProvider<>(entity, + new GemfirePropertyValueProvider(reader), null)); + + PersistentPropertyAccessor propertyAccessor = + new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService()); + + entity.doWithProperties((PropertyHandler) persistentProperty -> { + + if (isWritable(entity, persistentProperty)) { + + PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); + + Object value = null; + + try { + if (getLogger().isDebugEnabled()) { + getLogger().debug(String.format("Setting property [%1$s] for entity [%2$s] of type [%3$s] from PDX%4$s", + persistentProperty.getName(), instance, type, (customSerializer != null ? + String.format(" using custom PdxSerializer [%1$s]", customSerializer) : ""))); + } + + value = (customSerializer != null + ? customSerializer.fromData(persistentProperty.getType(), reader) + : reader.readField(persistentProperty.getName())); + + if (getLogger().isDebugEnabled()) { + getLogger().debug(String.format("... with value [%s]", value)); + } + + propertyAccessor.setProperty(persistentProperty, value); + } + catch (Exception cause) { + throw new MappingException(String.format( + "While setting value [%1$s] of property [%2$s] for entity of type [%3$s] from PDX%4$s", + value, persistentProperty.getName(), type, (customSerializer != null ? + String.format(" using custom PdxSerializer [%14s]", customSerializer) : "")), cause); + } + } + }); + + return propertyAccessor.getBean(); + } + + /* (non-Javadoc) */ + boolean isWritable(GemfirePersistentEntity entity, GemfirePersistentProperty persistentProperty) { + + return !entity.isConstructorArgument(persistentProperty) + && persistentProperty.isWritable() + && !persistentProperty.isTransient(); + } + + @Override + @SuppressWarnings("unchecked") + public boolean toData(Object value, PdxWriter writer) { + + GemfirePersistentEntity entity = getPersistentEntity(value); + + // Entity will be null for simple types + if (entity != null) { + + PersistentPropertyAccessor propertyAccessor = + new ConvertingPropertyAccessor(entity.getPropertyAccessor(value), getConversionService()); + + entity.doWithProperties((PropertyHandler) persistentProperty -> { + + if (isReadable(persistentProperty)) { + + PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); + + Object propertyValue = null; + + try { + + propertyValue = propertyAccessor.getProperty(persistentProperty); + + if (getLogger().isDebugEnabled()) { + getLogger().debug(String.format("Serializing entity [%1$s] property [%2$s] value [%3$s] of type [%4$s] to PDX%5$s", + entity.getType().getName(), persistentProperty.getName(), propertyValue, + ObjectUtils.nullSafeClassName(propertyValue), (customSerializer != null + ? String.format(" using custom PdxSerializer [%s]", customSerializer) : ""))); + } + + if (customSerializer != null) { + customSerializer.toData(propertyValue, writer); + } + else { + writer.writeField(persistentProperty.getName(), propertyValue, + (Class) persistentProperty.getType()); + } + } + catch (Exception cause) { + throw new MappingException(String.format( + "While serializing entity [%1$s] property [%2$s] value [%3$s] of type [%4$s] to PDX%5$s", + entity.getType().getName(), persistentProperty.getName(), propertyValue, + ObjectUtils.nullSafeClassName(propertyValue), (customSerializer != null + ? String.format(" using custom PdxSerializer [%1$s].", + customSerializer.getClass().getName()) : "")), cause); + } + } + }); + + GemfirePersistentProperty idProperty = entity.getIdProperty(); + + if (idProperty != null) { + writer.markIdentityField(idProperty.getName()); + } + + return true; + } + + return false; + } + + /* (non-Javadoc) */ + boolean isReadable(GemfirePersistentProperty persistentProperty) { + return !persistentProperty.isTransient(); + } } diff --git a/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java b/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java index ed0f8a91..68532224 100644 --- a/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java @@ -91,6 +91,31 @@ public abstract class CacheUtils extends DistributedSystemUtils { return peer; } + /* (non-Javadoc) */ + public static boolean close() { + return close(resolveGemFireCache()); + } + + /* (non-Javadoc) */ + public static boolean close(GemFireCache gemfireCache) { + return close(gemfireCache, () -> {}); + } + + /* (non-Javadoc) */ + public static boolean close(GemFireCache gemfireCache, Runnable shutdownHook) { + + try { + gemfireCache.close(); + return true; + } + catch (Exception ignore) { + return false; + } + finally { + Optional.ofNullable(shutdownHook).ifPresent(Runnable::run); + } + } + /* (non-Javadoc) */ public static boolean closeCache() { diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java index c5d94bad..4609eae3 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java @@ -16,14 +16,12 @@ package org.springframework.data.gemfire.mapping; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import java.io.DataInput; import java.io.DataOutput; -import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; +import java.time.LocalDateTime; import org.apache.geode.DataSerializable; import org.apache.geode.Instantiator; @@ -35,9 +33,15 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.annotation.ReadOnlyProperty; +import org.springframework.data.annotation.Transient; +import org.springframework.data.gemfire.GemfireUtils; import org.springframework.data.gemfire.repository.sample.Address; import org.springframework.data.gemfire.repository.sample.Person; +import lombok.Getter; +import lombok.Setter; + /** * Integration tests for {@link MappingPdxSerializer}. * @@ -46,75 +50,115 @@ import org.springframework.data.gemfire.repository.sample.Person; */ public class MappingPdxSerializerIntegrationTests { - static Region region; - static Cache cache; + static Region region; + @BeforeClass public static void setUp() { - MappingPdxSerializer serializer = new MappingPdxSerializer(new GemfireMappingContext(), - new DefaultConversionService()); + + MappingPdxSerializer serializer = + new MappingPdxSerializer(new GemfireMappingContext(), new DefaultConversionService()); cache = new CacheFactory() .set("name", MappingPdxSerializerIntegrationTests.class.getSimpleName()) - .set("mcast-port", "0") - .set("log-level", "warning") + .set("log-level", "error") .setPdxSerializer(serializer) .setPdxPersistent(true) .create(); region = cache.createRegionFactory() - .setDataPolicy(DataPolicy.PERSISTENT_REPLICATE) - .create("foo"); + .setDataPolicy(DataPolicy.PARTITION) + .create("TemporaryRegion"); } @AfterClass @SuppressWarnings("all") public static void tearDown() { - try { - cache.close(); - } - catch (Exception ignore) { - } - finally { - for (String name : new File(".").list(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.startsWith("BACKUP"); - } - })) { - new File(name).delete(); - } - } + GemfireUtils.close(cache); } @Test - public void serializeAndDeserializeCorrectly() { + public void handlesEntityWithReadOnlyProperty() { + + EntityWithReadOnlyProperty entity = new EntityWithReadOnlyProperty(); + + entity.setName("ReadOnlyEntity"); + entity.setTimestamp(LocalDateTime.now()); + entity.processId = 123; + + region.put(100L, entity); + + Object target = region.get(100L); + + assertThat(target).isInstanceOf(EntityWithReadOnlyProperty.class); + assertThat(target).isNotSameAs(entity); + + EntityWithReadOnlyProperty deserializedEntity = (EntityWithReadOnlyProperty) target; + + assertThat(deserializedEntity.getName()).isEqualTo(entity.getName()); + assertThat(deserializedEntity.getTimestamp()).isEqualTo(entity.getTimestamp()); + assertThat(deserializedEntity.getProcessId()).isNull(); + } + + @Test + public void handlesEntityWithTransientProperty() { + + EntityWithTransientProperty entity = new EntityWithTransientProperty(); + + entity.setName("TransientEntity"); + entity.setValueOne("testOne"); + entity.setValueTwo("testTwo"); + + region.put(101L, entity); + + Object target = region.get(101L); + + assertThat(target).isInstanceOf(EntityWithTransientProperty.class); + assertThat(target).isNotSameAs(entity); + + EntityWithTransientProperty deserializedEntity = (EntityWithTransientProperty) target; + + assertThat(deserializedEntity.getName()).isEqualTo(entity.getName()); + assertThat(deserializedEntity.getValueOne()).isNull(); + assertThat(deserializedEntity.getValueTwo()).isNull(); + } + + @Test + @SuppressWarnings("unchecked") + public void serializesAndDeserializesEntityCorrectly() { Address address = new Address(); - address.zipCode = "01234"; + + address.street = "100 Main St."; address.city = "London"; + address.zipCode = "01234"; Person person = new Person(1L, "Oliver", "Gierke"); - person.address = address; + person.address = address; region.put(1L, person); + Object result = region.get(1L); - assertThat(result instanceof Person, is(true)); + assertThat(result).isInstanceOf(Person.class); + assertThat(result).isNotSameAs(person); - Person reference = person; - assertThat(reference.getFirstname(), is(person.getFirstname())); - assertThat(reference.getLastname(), is(person.getLastname())); - assertThat(reference.address, is(person.address)); + Person reference = (Person) result; + + assertThat(reference.getFirstname()).isEqualTo(person.getFirstname()); + assertThat(reference.getLastname()).isEqualTo(person.getLastname()); + assertThat(reference.getAddress()).isEqualTo(person.getAddress()); } - @Test - public void serializeAndDeserializeCorrectlyWithDataSerializable() { + public void serializesAndDeserializesEntityWithDataSerializableProperty() { Address address = new Address(); - address.zipCode = "01234"; + + address.street = "100 Main St."; address.city = "London"; + address.zipCode = "01234"; PersonWithDataSerializableProperty person = new PersonWithDataSerializableProperty(2L, "Oliver", "Gierke", @@ -123,32 +167,40 @@ public class MappingPdxSerializerIntegrationTests { person.address = address; region.put(2L, person); + Object result = region.get(2L); - assertThat(result instanceof PersonWithDataSerializableProperty, is(true)); + assertThat(result).isInstanceOf(PersonWithDataSerializableProperty.class); + assertThat(result).isNotSameAs(person); - PersonWithDataSerializableProperty reference = person; - assertThat(reference.getFirstname(), is(person.getFirstname())); - assertThat(reference.getLastname(), is(person.getLastname())); - assertThat(reference.address, is(person.address)); - assertThat(reference.dsProperty.getValue(),is("foo")); + PersonWithDataSerializableProperty reference = (PersonWithDataSerializableProperty) result; + + assertThat(reference.getFirstname()).isEqualTo(person.getFirstname()); + assertThat(reference.getLastname()).isEqualTo(person.getLastname()); + assertThat(reference.getAddress()).isEqualTo(person.getAddress()); + assertThat(reference.property.getValue()).isEqualTo("foo"); } - @SuppressWarnings("serial") + @SuppressWarnings({ "serial", "unused" }) public static class PersonWithDataSerializableProperty extends Person { - private DataSerializableProperty dsProperty; + private DataSerializableProperty property; public PersonWithDataSerializableProperty(Long id, String firstname, - String lastname, DataSerializableProperty dsProperty) { + String lastname, DataSerializableProperty property) { + super(id, firstname, lastname); - this.dsProperty = dsProperty; + + this.property = property; } public DataSerializableProperty getDataSerializableProperty() { - return this.dsProperty; + return this.property; } + public void setDataSerializableProperty(DataSerializableProperty property) { + this.property = property; + } } @SuppressWarnings("serial") @@ -170,20 +222,44 @@ public class MappingPdxSerializerIntegrationTests { @Override - public void fromData(DataInput dataInput) throws IOException, - ClassNotFoundException { - value = dataInput.readUTF(); + public void fromData(DataInput dataInput) throws IOException, ClassNotFoundException { + this.value = dataInput.readUTF(); } @Override public void toData(DataOutput dataOutput) throws IOException { - dataOutput.writeUTF(value); + dataOutput.writeUTF(this.value); } public String getValue() { return this.value; } + } + @Getter + static class EntityWithReadOnlyProperty { + + @Setter + LocalDateTime timestamp; + + @Setter + String name; + + // TODO: if there is not setter, then effectively this field/property is read-only + // and should not require the @ReadOnlyProperty + @ReadOnlyProperty + Object processId; + } + + @Getter @Setter + static class EntityWithTransientProperty { + + private String name; + + private transient Object valueOne; + + @Transient + private Object valueTwo; } } diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java index 55e94d1b..664feba1 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java @@ -17,36 +17,40 @@ package org.springframework.data.gemfire.mapping; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.isA; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; import java.util.Collections; +import java.util.Map; +import java.util.Optional; import org.apache.geode.pdx.PdxReader; import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.convert.EntityInstantiator; +import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.gemfire.repository.sample.Address; import org.springframework.data.gemfire.repository.sample.Person; import org.springframework.data.mapping.MappingException; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.ParameterValueProvider; /** @@ -72,12 +76,9 @@ public class MappingPdxSerializerUnitTests { ConversionService conversionService; - GemfireMappingContext context; + GemfireMappingContext mappingContext; - MappingPdxSerializer serializer; - - @Rule - public ExpectedException exception = ExpectedException.none(); + MappingPdxSerializer pdxSerializer; @Mock EntityInstantiator mockInstantiator; @@ -93,14 +94,70 @@ public class MappingPdxSerializerUnitTests { @Before public void setUp() { - context = new GemfireMappingContext(); - conversionService = new GenericConversionService(); - serializer = new MappingPdxSerializer(context, conversionService); - serializer.setCustomSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); + + this.conversionService = new GenericConversionService(); + this.mappingContext = new GemfireMappingContext(); + this.pdxSerializer = new MappingPdxSerializer(this.mappingContext, this.conversionService); + this.pdxSerializer.setCustomSerializers(Collections.singletonMap(Address.class, this.mockAddressSerializer)); } @Test - public void createFullyInitialized() { + public void constructDefaultMappingPdxSerializer() { + + MappingPdxSerializer pdxSerializer = new MappingPdxSerializer(); + + assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class); + assertThat(pdxSerializer.getCustomSerializers()).isEmpty(); + assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(pdxSerializer.getMappingContext()).isInstanceOf(GemfireMappingContext.class); + } + + @Test + public void constructMappingPdxSerializer() { + + ConversionService mockConversionService = mock(ConversionService.class); + GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class); + + MappingPdxSerializer pdxSerializer = new MappingPdxSerializer(mockMappingContext, mockConversionService); + + assertThat(pdxSerializer.getConversionService()).isEqualTo(mockConversionService); + assertThat(pdxSerializer.getCustomSerializers()).isEmpty(); + assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(pdxSerializer.getMappingContext()).isEqualTo(mockMappingContext); + } + + @Test(expected = IllegalArgumentException.class) + public void constructMappingPdxSerializerWithNullConversionService() { + + try { + new MappingPdxSerializer(this.mappingContext, null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("ConversionService is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void constructMappingPdxSerializerWithNullMappingContext() { + + try { + new MappingPdxSerializer(null, this.conversionService); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("MappingContext is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void createMappingPdxSerializer() { ConversionService mockConversionService = mock(ConversionService.class); GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class); @@ -147,39 +204,246 @@ public class MappingPdxSerializerUnitTests { } @Test - @SuppressWarnings("unchecked") - public void usesRegisteredInstantiator() { + public void setCustomSerializersWithMappingOfClassTypesToPdxSerializers() { - Address address = new Address(); + Map, PdxSerializer> customSerializers = + Collections.singletonMap(Person.class, mock(PdxSerializer.class)); - address.city = "London"; - address.zipCode = "01234"; + this.pdxSerializer.setCustomSerializers(customSerializers); - Person person = new Person(1L, "Oliver", "Gierke"); + assertThat(this.pdxSerializer.getCustomSerializers()).isEqualTo(customSerializers); + } - person.address = address; + @Test(expected = IllegalArgumentException.class) + public void setCustomSerializersToNull() { - when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) - .thenReturn(person); + try { + this.pdxSerializer.setCustomSerializers(null); + } + catch (IllegalArgumentException expected) { - serializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); + assertThat(expected).hasMessage("Custom PdxSerializers are required"); + assertThat(expected).hasNoCause(); - serializer.fromData(Person.class, mockReader); + throw expected; + } + } - GemfirePersistentEntity persistentEntity = context.getPersistentEntity(Person.class); + @Test + public void getCustomSerializerForMappedType() { - verify(mockInstantiator, times(1)).createInstance(eq(persistentEntity), - any(ParameterValueProvider.class)); + PdxSerializer mockPdxSerializer = mock(PdxSerializer.class); - verify(mockAddressSerializer, times(1)).fromData(eq(Address.class), any(PdxReader.class)); + Map, PdxSerializer> customSerializers = Collections.singletonMap(Person.class, mockPdxSerializer); + + this.pdxSerializer.setCustomSerializers(customSerializers); + + assertThat(this.pdxSerializer.getCustomSerializer(Person.class)).isEqualTo(mockPdxSerializer); + } + + @Test + public void getCustomSerializerForNonMappedType() { + + PdxSerializer mockPdxSerializer = mock(PdxSerializer.class); + + Map, PdxSerializer> customSerializers = Collections.singletonMap(Person.class, mockPdxSerializer); + + this.pdxSerializer.setCustomSerializers(customSerializers); + + assertThat(this.pdxSerializer.getCustomSerializer(Address.class)).isNull(); + } + + @Test + public void setGemfireInstantiatorsWithEntityInstantiators() { + + EntityInstantiators mockEntityInstantiators = mock(EntityInstantiators.class); + + this.pdxSerializer.setGemfireInstantiators(mockEntityInstantiators); + + assertThat(this.pdxSerializer.getGemfireInstantiators()).isSameAs(mockEntityInstantiators); + } + + @Test(expected = IllegalArgumentException.class) + public void setGemfireInstantiatorsWithNullEntityInstantiators() { + + try { + this.pdxSerializer.setGemfireInstantiators((EntityInstantiators) null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("EntityInstantiators are required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void setGemfireInstantiatorsWithMappingOfClassTypesToEntityInstantiators() { + + Map, EntityInstantiator> entityInstantiators = + Collections.singletonMap(Person.class, mock(EntityInstantiator.class)); + + this.pdxSerializer.setGemfireInstantiators(entityInstantiators); + + assertThat(this.pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + } + + @Test(expected = IllegalArgumentException.class) + public void setGemfireInstantiatorsWithNullMap() { + + try { + this.pdxSerializer.setGemfireInstantiators((Map, EntityInstantiator>) null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("CustomInstantiators must not be null!"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void getInstantiatorForManagedPersistentEntityWithInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + Map, EntityInstantiator> entityInstantiators = + Collections.singletonMap(Person.class, mockEntityInstantiator); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Person.class); + + this.pdxSerializer.setGemfireInstantiators(entityInstantiators); + + assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isEqualTo(mockEntityInstantiator); + + verify(mockEntity, atLeast(1)).getType(); + verifyZeroInteractions(mockEntityInstantiator); + } + + @Test + public void getInstantiatorForNonManagedPersistentEntityWithNoInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + Map, EntityInstantiator> entityInstantiators = + Collections.singletonMap(Person.class, mockEntityInstantiator); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Address.class); + + this.pdxSerializer.setGemfireInstantiators(entityInstantiators); + + assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isNotEqualTo(mockEntityInstantiator); + + verify(mockEntity, atLeast(1)).getType(); + verifyZeroInteractions(mockEntityInstantiator); + } + + @Test + public void isReadableWithNonTransientPropertyReturnsTrue() { + + GemfirePersistentProperty mockPersistentProperty = mock(GemfirePersistentProperty.class); + + when(mockPersistentProperty.isTransient()).thenReturn(false); + + assertThat(this.pdxSerializer.isReadable(mockPersistentProperty)).isTrue(); + + verify(mockPersistentProperty, times(1)).isTransient(); + } + + @Test + public void isReadableWithTransientPropertyReturnsFalse() { + + GemfirePersistentProperty mockPersistentProperty = mock(GemfirePersistentProperty.class); + + when(mockPersistentProperty.isTransient()).thenReturn(true); + + assertThat(this.pdxSerializer.isReadable(mockPersistentProperty)).isFalse(); + + verify(mockPersistentProperty, times(1)).isTransient(); + } + + @Test + public void isWritableWithWritablePropertyReturnsTrue() { + + GemfirePersistentEntity mockEntity = mock(GemfirePersistentEntity.class); + + GemfirePersistentProperty mockProperty = mock(GemfirePersistentProperty.class); + + when(mockEntity.isConstructorArgument(any(GemfirePersistentProperty.class))).thenReturn(false); + when(mockProperty.isTransient()).thenReturn(false); + when(mockProperty.isWritable()).thenReturn(true); + + assertThat(this.pdxSerializer.isWritable(mockEntity, mockProperty)).isTrue(); + + verify(mockEntity, times(1)).isConstructorArgument(eq(mockProperty)); + verify(mockProperty, times(1)).isWritable(); + verify(mockProperty, times(1)).isTransient(); + } + + @Test + public void isWritableWithConstructorArgumentPropertyReturnsFalse() { + + GemfirePersistentEntity mockEntity = mock(GemfirePersistentEntity.class); + + GemfirePersistentProperty mockProperty = mock(GemfirePersistentProperty.class); + + when(mockEntity.isConstructorArgument(any(GemfirePersistentProperty.class))).thenReturn(true); + + assertThat(this.pdxSerializer.isWritable(mockEntity, mockProperty)).isFalse(); + + verify(mockEntity, times(1)).isConstructorArgument(eq(mockProperty)); + verify(mockProperty, never()).isWritable(); + verify(mockProperty, never()).isTransient(); + } + + @Test + public void isWritableWithNonWritablePropertyReturnsFalse() { + + GemfirePersistentEntity mockEntity = mock(GemfirePersistentEntity.class); + + GemfirePersistentProperty mockProperty = mock(GemfirePersistentProperty.class); + + when(mockEntity.isConstructorArgument(any(GemfirePersistentProperty.class))).thenReturn(false); + when(mockProperty.isWritable()).thenReturn(false); + + assertThat(this.pdxSerializer.isWritable(mockEntity, mockProperty)).isFalse(); + + verify(mockEntity, times(1)).isConstructorArgument(eq(mockProperty)); + verify(mockProperty, times(1)).isWritable(); + verify(mockProperty, never()).isTransient(); + } + + @Test + public void isWritableWithTransientPropertyReturnsFalse() { + + GemfirePersistentEntity mockEntity = mock(GemfirePersistentEntity.class); + + GemfirePersistentProperty mockProperty = mock(GemfirePersistentProperty.class); + + when(mockEntity.isConstructorArgument(any(GemfirePersistentProperty.class))).thenReturn(false); + when(mockProperty.isTransient()).thenReturn(true); + when(mockProperty.isWritable()).thenReturn(true); + + assertThat(this.pdxSerializer.isWritable(mockEntity, mockProperty)).isFalse(); + + verify(mockEntity, times(1)).isConstructorArgument(eq(mockProperty)); + verify(mockProperty, times(1)).isWritable(); + verify(mockProperty, times(1)).isTransient(); } @Test @SuppressWarnings("unchecked") - public void fromDataMapsPdxDataToApplicationDomainObject() { + public void fromDataDeserializesPdxAndMapsToApplicationDomainObject() { Address expectedAddress = new Address(); + expectedAddress.street = "100 Main St."; expectedAddress.city = "Portland"; expectedAddress.zipCode = "12345"; @@ -190,9 +454,9 @@ public class MappingPdxSerializerUnitTests { when(mockReader.readField(eq("lastname"))).thenReturn("Doe"); when(mockAddressSerializer.fromData(eq(Address.class), eq(mockReader))).thenReturn(expectedAddress); - serializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); + this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); - Object obj = serializer.fromData(Person.class, mockReader); + Object obj = this.pdxSerializer.fromData(Person.class, mockReader); assertThat(obj).isInstanceOf(Person.class); @@ -203,14 +467,15 @@ public class MappingPdxSerializerUnitTests { assertThat(jonDoe.getFirstname()).isEqualTo("Jon"); assertThat(jonDoe.getLastname()).isEqualTo("Doe"); - verify(mockInstantiator, times(1)).createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); + verify(mockInstantiator, times(1)) + .createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); verify(mockReader, times(1)).readField(eq("id")); verify(mockReader, times(1)).readField(eq("firstname")); verify(mockReader, times(1)).readField(eq("lastname")); verify(mockAddressSerializer, times(1)).fromData(eq(Address.class), eq(mockReader)); } - @Test + @Test(expected = MappingException.class) @SuppressWarnings("unchecked") public void fromDataHandlesExceptionProperly() { @@ -219,15 +484,18 @@ public class MappingPdxSerializerUnitTests { when(mockReader.readField(eq("id"))).thenThrow(newIllegalArgumentException("test")); - serializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); - try { - exception.expect(MappingException.class); - exception.expectCause(isA(IllegalArgumentException.class)); - exception.expectMessage(String.format( - "While setting value [null] of property [id] for entity of type [%s] from PDX", Person.class)); + this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); + this.pdxSerializer.fromData(Person.class, mockReader); + } + catch (MappingException expected) { - serializer.fromData(Person.class, mockReader); + assertThat(expected).hasMessage("While setting value [null] of property [id] for entity of type [%s] from PDX", Person.class); + assertThat(expected).hasCauseInstanceOf(IllegalArgumentException.class); + assertThat(expected.getCause()).hasMessage("test"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; } finally { verify(mockInstantiator, times(1)) @@ -237,11 +505,42 @@ public class MappingPdxSerializerUnitTests { } } + @Test + @SuppressWarnings("unchecked") + public void fromDataUsesRegisteredInstantiator() { + + Address address = new Address(); + + address.street = "100 Main St."; + address.city = "London"; + address.zipCode = "01234"; + + Person person = new Person(1L, "Oliver", "Gierke"); + + person.address = address; + + when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + .thenReturn(person); + + this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator)); + this.pdxSerializer.fromData(Person.class, mockReader); + + GemfirePersistentEntity persistentEntity = + Optional.ofNullable(mappingContext.getPersistentEntity(Person.class)).orElse(null); + + verify(mockInstantiator, times(1)) + .createInstance(eq(persistentEntity), any(ParameterValueProvider.class)); + + verify(mockAddressSerializer, times(1)) + .fromData(eq(Address.class), any(PdxReader.class)); + } + @Test public void toDataSerializesApplicationDomainObjectToPdx() { Address address = new Address(); + address.street = "100 Main St."; address.city = "Portland"; address.zipCode = "12345"; @@ -249,9 +548,9 @@ public class MappingPdxSerializerUnitTests { jonDoe.address = address; - serializer.setCustomSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); + this.pdxSerializer.setCustomSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); - assertThat(serializer.toData(jonDoe, mockWriter)).isTrue(); + assertThat(this.pdxSerializer.toData(jonDoe, mockWriter)).isTrue(); verify(mockAddressSerializer, times(1)).toData(eq(address), eq(mockWriter)); verify(mockWriter, times(1)).writeField(eq("id"), eq(1L), eq(Long.class)); @@ -260,11 +559,12 @@ public class MappingPdxSerializerUnitTests { verify(mockWriter, times(1)).markIdentityField(eq("id")); } - @Test + @Test(expected = MappingException.class) public void toDataHandlesExceptionProperly() { Address address = new Address(); + address.street = "100 Main St."; address.city = "Portland"; address.zipCode = "12345"; @@ -276,13 +576,19 @@ public class MappingPdxSerializerUnitTests { .thenThrow(newIllegalArgumentException("test")); try { - exception.expect(MappingException.class); - exception.expectCause(isA(IllegalArgumentException.class)); - exception.expectMessage(String.format( - "While serializing entity property [address] value [Portland, 12345] of type [%s] to PDX", - Person.class)); + this.pdxSerializer.setCustomSerializers(Collections.emptyMap()); + this.pdxSerializer.toData(jonDoe, mockWriter); + } + catch (MappingException expected) { - new MappingPdxSerializer(context, conversionService).toData(jonDoe, mockWriter); + assertThat(expected).hasMessage("While serializing entity [%1$s] property [address]" + + " value [100 Main St. Portland, 12345] of type [%2$s] to PDX", + Person.class.getName(), Address.class.getName()); + assertThat(expected).hasCauseInstanceOf(IllegalArgumentException.class); + assertThat(expected.getCause()).hasMessage("test"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; } finally { verify(mockWriter, atMost(1)).writeField(eq("id"), eq(1L), eq(Long.class)); diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java index 366fbefc..7f69e6b4 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java @@ -17,6 +17,7 @@ package org.springframework.data.gemfire.repository.sample; import org.springframework.data.annotation.Id; import org.springframework.data.gemfire.mapping.annotation.Region; +import org.springframework.util.ObjectUtils; /** * @@ -25,14 +26,44 @@ import org.springframework.data.gemfire.mapping.annotation.Region; @Region("address") public class Address { + public String street; + public String city; + @Id public String zipCode; - public String city; + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof Address)) { + return false; + } + + Address that = (Address) obj; + + return ObjectUtils.nullSafeEquals(this.street, that.street) + && ObjectUtils.nullSafeEquals(this.city, that.city) + && ObjectUtils.nullSafeEquals(this.zipCode, that.zipCode); + } + + @Override + public int hashCode() { + + int hashValue = 17; + + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.street); + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.city); + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.zipCode); + + return hashValue; + } @Override public String toString() { - return String.format("%1$s, %2$s", city, zipCode); + return String.format("%1$s %2$s, %3$s", this.street, this.city, this.zipCode); } - } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java index ed2bd300..f5c8049b 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java @@ -119,6 +119,7 @@ public class Person implements Serializable { */ @Override public boolean equals(Object obj) { + if (this == obj) { return true; }