From 154d0c5f254667a421d9119d1372225169628d42 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 12 Feb 2018 18:41:01 -0800 Subject: [PATCH] SGF-719 - Extend MappingPdxSerializer to allow registering custom PdxSerializers based on fully qualified property name. --- .../gemfire/mapping/MappingPdxSerializer.java | 508 ++++++++++----- .../MappingPdxSerializerIntegrationTest.java | 59 +- .../MappingPdxSerializerIntegrationTests.java | 466 ++++++++++++++ .../MappingPdxSerializerUnitTests.java | 607 +++++++++++++++--- .../cdi/CdiExtensionIntegrationTest.java | 15 +- .../gemfire/repository/sample/Address.java | 39 +- .../data/gemfire/test/support/MapBuilder.java | 50 ++ 7 files changed, 1449 insertions(+), 295 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java 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 a4854343..3c9adb57 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -32,6 +32,7 @@ import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; @@ -39,50 +40,67 @@ import org.springframework.data.mapping.model.MappingException; 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} - * to read and write entities. + * GemFire {@link PdxSerializer} implementation using the Spring Data GemFire {@link GemfireMappingContext} + * to read and write entities from/to GemFire PDX bytes. * * @author Oliver Gierke * @author David Turanski * @author John Blum + * @see com.gemstone.gemfire.pdx.PdxReader + * @see com.gemstone.gemfire.pdx.PdxSerializer + * @see com.gemstone.gemfire.pdx.PdxWriter * @see org.springframework.context.ApplicationContext * @see org.springframework.context.ApplicationContextAware * @see org.springframework.core.convert.ConversionService * @see org.springframework.data.convert.EntityInstantiator * @see org.springframework.data.mapping.PersistentEntity + * @see org.springframework.data.mapping.PersistentProperty * @see org.springframework.data.mapping.PersistentPropertyAccessor + * @see org.springframework.data.mapping.model.ConvertingPropertyAccessor * @see org.springframework.data.mapping.model.PersistentEntityParameterValueProvider - * @see org.springframework.data.mapping.model.SpELContext - * @see com.gemstone.gemfire.pdx.PdxReader - * @see com.gemstone.gemfire.pdx.PdxSerializer - * @see com.gemstone.gemfire.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 Map, PdxSerializer> customSerializers; + private Map customPdxSerializers; + // TODO: decide what to do with this; SpELContext is not used private SpELContext context; + public static MappingPdxSerializer newMappingPdxSerializer() { + return create(newMappingContext(), newConversionService()); + } + + public static MappingPdxSerializer create(ConversionService conversionService) { + return create(newMappingContext(), conversionService); + } + + public static MappingPdxSerializer create(GemfireMappingContext mappingContext) { + return create(mappingContext, newConversionService()); + } + /** - * Factory method to construct a new instance of the {@link MappingPdxSerializer} initialized with the given - * {@link GemfireMappingContext} and Spring {@link ConversionService}. If either the {@link GemfireMappingContext} - * or Spring {@link ConversionService} are {@literal null}, then this factory method will construct default - * instances of each. + * Factory method used to construct a new instance of the {@link MappingPdxSerializer} initialized with + * the given {@link GemfireMappingContext mapping context} and {@link ConversionService conversion service}. * - * @param mappingContext {@link GemfireMappingContext} used by this {@link PdxSerializer} to handle mappings - * between application domain object types and PDX Serialization meta-data/data. - * @param conversionService Spring's {@link ConversionService} used to convert PDX deserialized data to application - * object property types. + * If either the {@link GemfireMappingContext mapping context} or the {@link ConversionService conversion service} + * are {@literal null}, then this factory method will provide default instances for each. + * + * @param mappingContext {@link GemfireMappingContext} used by the {@link MappingPdxSerializer} to map + * between application domain object types and PDX serialized bytes based on the entity mapping meta-data. + * @param conversionService {@link ConversionService} used by the {@link MappingPdxSerializer} to convert + * PDX serialized data to application object property types. * @return an initialized instance of the {@link MappingPdxSerializer}. * @see org.springframework.core.convert.ConversionService * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer @@ -90,39 +108,90 @@ 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()); - - return new MappingPdxSerializer(mappingContext, conversionService); + return new MappingPdxSerializer( + resolveMappingContext(mappingContext), + resolveConversionService(conversionService) + ); } /** - * Creates a new {@link MappingPdxSerializer} using the default {@link GemfireMappingContext} + * Constructs a new {@link ConversionService}. + * + * @return a new {@link ConversionService}. + * @see org.springframework.core.convert.ConversionService + */ + private static ConversionService newConversionService() { + return new DefaultConversionService(); + } + + /** + * Resolves the {@link ConversionService} used for conversions. + * + * @param conversionService {@link ConversionService} to evaluate. + * @return the given {@link ConversionService} if not {@literal null} or a new {@link ConversionService}. + * @see org.springframework.core.convert.ConversionService + * @see #newConversionService() + */ + private static ConversionService resolveConversionService(ConversionService conversionService) { + return conversionService != null ? conversionService : newConversionService(); + } + + /** + * Constructs a new {@link GemfireMappingContext}. + * + * @return a new {@link GemfireMappingContext}. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + */ + private static GemfireMappingContext newMappingContext() { + return new GemfireMappingContext(); + } + + /** + * Resolves the {@link GemfireMappingContext mapping context} used to provide mapping meta-data. + * + * @param mappingContext {@link GemfireMappingContext} to evaluate. + * @return the given {@link GemfireMappingContext mapping context} if not {@literal null} + * or a new {@link GemfireMappingContext mapping context}. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + * @see #newMappingContext() + */ + private static GemfireMappingContext resolveMappingContext(GemfireMappingContext mappingContext) { + return mappingContext != null ? mappingContext : newMappingContext(); + } + + /** + * Constructs a new instance of {@link MappingPdxSerializer} using a default {@link GemfireMappingContext} * and {@link DefaultConversionService}. * + * @see #newConversionService() + * @see #newMappingContext() * @see org.springframework.core.convert.support.DefaultConversionService * @see org.springframework.data.gemfire.mapping.GemfireMappingContext */ public MappingPdxSerializer() { - this(new GemfireMappingContext(), new DefaultConversionService()); + this(newMappingContext(), newConversionService()); } /** - * Creates a new {@link MappingPdxSerializer} using the given + * Constructs a new instance of {@link MappingPdxSerializer} initialized with the given * {@link GemfireMappingContext} and {@link ConversionService}. * - * @param mappingContext must not be {@literal null}. - * @param conversionService must not be {@literal null}. + * @param mappingContext {@link GemfireMappingContext} used by the {@link MappingPdxSerializer} to map + * between application domain object types and PDX serialized bytes based on the entity mapping meta-data. + * @param conversionService {@link ConversionService} used by the {@link MappingPdxSerializer} to convert + * PDX serialized data to application object property types. + * @throws IllegalArgumentException if either the {@link GemfireMappingContext} or the {@link ConversionService} + * is {@literal null}. */ public MappingPdxSerializer(GemfireMappingContext mappingContext, ConversionService conversionService) { - Assert.notNull(mappingContext); - Assert.notNull(conversionService); + Assert.notNull(mappingContext, "MappingContext is required"); + Assert.notNull(conversionService, "ConversionService is required"); this.mappingContext = mappingContext; this.conversionService = conversionService; - this.instantiators = new EntityInstantiators(); - this.customSerializers = Collections.emptyMap(); + this.entityInstantiators = new EntityInstantiators(); + this.customPdxSerializers = Collections.emptyMap(); this.context = new SpELContext(PdxReaderPropertyAccessor.INSTANCE); } @@ -134,24 +203,105 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw this.context = new SpELContext(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 reference to the configured {@link ConversionService}. + * @see org.springframework.core.convert.ConversionService + */ protected ConversionService getConversionService() { return conversionService; } /** - * Configures custom PDX serializers to use for specific class types. + * Configures custom {@link PdxSerializer PDX serializers} used to customize the serialization for specific + * application {@link Class domain types}. * - * @param customSerializers a mapping of domain object class types and their corresponding PDX serializer. + * @param customPdxSerializers {@link Map mapping} containing custom {@link PdxSerializer PDX serializers} + * used to customize the serialization of specific application {@link Class domain types}. + * @throws IllegalArgumentException if the {@link Map custom PDX serializer mapping} is {@literal null}. + * @see com.gemstone.gemfire.pdx.PdxSerializer + * @see java.util.Map */ - public void setCustomSerializers(Map, PdxSerializer> customSerializers) { - Assert.notNull(customSerializers); - this.customSerializers = customSerializers; + public void setCustomPdxSerializers(Map customPdxSerializers) { + + Assert.notNull(customPdxSerializers, "Custom PdxSerializers are required"); + + this.customPdxSerializers = customPdxSerializers; } - /* (non-Javadoc) */ + /** + * @deprecated please use ({@link #setCustomPdxSerializers(Map)} instead. + */ + @Deprecated + public void setCustomSerializers(Map, PdxSerializer> customSerializers) { + setCustomPdxSerializers(customSerializers); + } + + /** + * Returns a {@link Map mapping} of application {@link Class domain types} to custom + * {@link PdxSerializer PDX serializers} used to customize the serialization + * for specific application {@link Class domain types}. + * + * @return a {@link Map mapping} of application {@link Class domain types} + * to custom {@link PdxSerializer PDX serializers}. + * @see com.gemstone.gemfire.pdx.PdxSerializer + * @see java.util.Map + */ + protected Map getCustomPdxSerializers() { + return Collections.unmodifiableMap(this.customPdxSerializers); + } + + /** + * @deprecated please use {@link #getCustomPdxSerializers()} instead. + */ + @Deprecated + @SuppressWarnings("unchecked") protected Map, PdxSerializer> getCustomSerializers() { - return Collections.unmodifiableMap(customSerializers); + return (Map, PdxSerializer>) getCustomPdxSerializers(); + } + + /** + * Returns a custom PDX serializer for the given {@link PersistentProperty entity persistent property}. + * + * @param property {@link PersistentProperty} of the entity used to lookup the custom PDX serializer. + * @return a custom {@link PdxSerializer} for the given entity {@link PersistentProperty}, + * or {@literal null} if no custom {@link PdxSerializer} could be found. + * @see com.gemstone.gemfire.pdx.PdxSerializer + */ + protected PdxSerializer getCustomPdxSerializer(PersistentProperty property) { + + Map customPdxSerializers = getCustomPdxSerializers(); + + PdxSerializer customPdxSerializer = customPdxSerializers.get(property); + + customPdxSerializer = customPdxSerializer != null ? customPdxSerializer + : customPdxSerializers.get(toFullyQualifiedPropertyName(property)); + + customPdxSerializer = customPdxSerializer != null ? customPdxSerializer + : customPdxSerializers.get(property.getType()); + + return customPdxSerializer; + } + + /** + * Converts the entity {@link PersistentProperty} to a {@link String fully-qualified property name}. + * + * @param property {@link PersistentProperty} of the entity. + * @return the {@link String fully-qualified property name of the entity {@link PersistentProperty}. + * @see org.springframework.data.mapping.PersistentProperty + */ + String toFullyQualifiedPropertyName(PersistentProperty property) { + return property.getOwner().getType().getName().concat(".").concat(property.getName()); + } + + /** + * @deprecated please use {@link #getCustomPdxSerializer(PersistentProperty)} instead. + */ + @Deprecated + protected PdxSerializer getCustomSerializer(Class type) { + return getCustomPdxSerializers().get(type); } /** @@ -160,137 +310,13 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @param gemfireInstantiators must not be {@literal null}. */ public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { - Assert.notNull(gemfireInstantiators); - this.instantiators = new EntityInstantiators(gemfireInstantiators); + Assert.notNull(gemfireInstantiators, "GemFire EntityInstantiators are required"); + this.entityInstantiators = new EntityInstantiators(gemfireInstantiators); } /* (non-Javadoc) */ protected EntityInstantiators getGemfireInstantiators() { - return instantiators; - } - - /* (non-Javadoc) */ - protected GemfireMappingContext getMappingContext() { - return mappingContext; - } - - /** - * {@inheritDoc} - */ - @Override - public Object fromData(final Class type, final PdxReader reader) { - - final GemfirePersistentEntity entity = getPersistentEntity(type); - - final Object instance = getInstantiatorFor(entity).createInstance(entity, - new PersistentEntityParameterValueProvider(entity, - new GemfirePropertyValueProvider(reader), null)); - - final PersistentPropertyAccessor propertyAccessor = - new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService()); - - entity.doWithProperties(new PropertyHandler() { - public void doWithPersistentProperty(GemfirePersistentProperty 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 [%1$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 - public boolean toData(final Object value, final PdxWriter writer) { - - GemfirePersistentEntity entity = getPersistentEntity(value); - - final PersistentPropertyAccessor propertyAccessor = - new ConvertingPropertyAccessor(entity.getPropertyAccessor(value), getConversionService()); - - entity.doWithProperties(new PropertyHandler() { - @Override @SuppressWarnings("unchecked") - public void doWithPersistentProperty(GemfirePersistentProperty 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( - "Error 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; - } - - /** - * Looks up and returns a custom PdxSerializer based on the class type of the object to (de)serialize. - * - * @param type the Class type of the object to (de)serialize. - * @return a "custom" PdxSerializer for the given class type or null if no custom PdxSerializer - * for the given class type was registered. - * @see #getCustomSerializers() - * @see com.gemstone.gemfire.pdx.PdxSerializer - */ - protected PdxSerializer getCustomSerializer(Class type) { - return getCustomSerializers().get(type); + return this.entityInstantiators; } /** @@ -306,6 +332,28 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return getGemfireInstantiators().getInstantiatorFor(entity); } + /** + * Returns a reference to the configured {@link Log} used to log {@link String messages} + * about the functions of this {@link PdxSerializer}. + * + * @return a reference to the configured {@link Log}. + * @see org.apache.commons.logging.Log + */ + protected Log getLogger() { + return this.log; + } + + /** + * 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. * @@ -329,4 +377,136 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw protected GemfirePersistentEntity getPersistentEntity(Class entityType) { return getMappingContext().getPersistentEntity(entityType); } + + @Override + public Object fromData(final Class type, final PdxReader reader) { + + final GemfirePersistentEntity entity = getPersistentEntity(type); + + final Object instance = getInstantiatorFor(entity) + .createInstance(entity, new PersistentEntityParameterValueProvider(entity, + new GemfirePropertyValueProvider(reader), null)); + + final PersistentPropertyAccessor propertyAccessor = + new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService()); + + entity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { + + if (isWritable(entity, persistentProperty)) { + + PdxSerializer customPdxSerializer = getCustomPdxSerializer(persistentProperty); + + 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, (customPdxSerializer != null ? + String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : ""))); + } + + value = (customPdxSerializer != null + ? customPdxSerializer.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, (customPdxSerializer != null ? + String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : "")), 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, final PdxWriter writer) { + + final GemfirePersistentEntity entity = getPersistentEntity(value); + + // Entity will be null for simple types + if (entity != null) { + + final PersistentPropertyAccessor propertyAccessor = + new ConvertingPropertyAccessor(entity.getPropertyAccessor(value), getConversionService()); + + entity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { + + if (isReadable(persistentProperty)) { + + PdxSerializer customPdxSerializer = getCustomPdxSerializer(persistentProperty); + + 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), (customPdxSerializer != null + ? String.format(" using custom PdxSerializer [%s]", customPdxSerializer) : ""))); + } + + if (customPdxSerializer != null) { + customPdxSerializer.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), (customPdxSerializer != null + ? String.format(" using custom PdxSerializer [%1$s].", + customPdxSerializer.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/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java index f3007704..61f159d5 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java @@ -24,13 +24,6 @@ import java.io.File; import java.io.FilenameFilter; import java.io.IOException; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.gemfire.repository.sample.Address; -import org.springframework.data.gemfire.repository.sample.Person; - import com.gemstone.gemfire.DataSerializable; import com.gemstone.gemfire.Instantiator; import com.gemstone.gemfire.cache.Cache; @@ -38,9 +31,16 @@ import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.cache.DataPolicy; import com.gemstone.gemfire.cache.Region; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.gemfire.repository.sample.Address; +import org.springframework.data.gemfire.repository.sample.Person; + /** * Integration tests for {@link MappingPdxSerializer}. - * + * * @author Oliver Gierke * @author John Blum */ @@ -141,47 +141,56 @@ public class MappingPdxSerializerIntegrationTest { super(id, firstname, lastname); this.dsProperty = dsProperty; } - + public DataSerializableProperty getDataSerializableProperty() { return this.dsProperty; } - + } - + @SuppressWarnings("serial") public static class DataSerializableProperty implements DataSerializable { - + static { - Instantiator.register(new Instantiator(DataSerializableProperty.class,101) { - public DataSerializable newInstance() { - return new DataSerializableProperty(""); - } - }); + registerInstantiator(); } - + + private static void registerInstantiator() { + + try { + Instantiator.register(new Instantiator(DataSerializableProperty.class,101) { + public DataSerializable newInstance() { + return new DataSerializableProperty(""); + } + }); + } + catch (IllegalStateException ignore) { + // thrown when already registered + } + } + private String value; - + public DataSerializableProperty(String value) { this.value = value; } - - + + @Override public void fromData(DataInput dataInput) throws IOException, ClassNotFoundException { value = dataInput.readUTF(); - + } @Override public void toData(DataOutput dataOutput) throws IOException { dataOutput.writeUTF(value); } - + public String getValue() { return this.value; } - - } + } } diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java new file mode 100644 index 00000000..6b580c08 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTests.java @@ -0,0 +1,466 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.mapping; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Collections; + +import com.gemstone.gemfire.DataSerializable; +import com.gemstone.gemfire.Instantiator; +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.pdx.PdxReader; +import com.gemstone.gemfire.pdx.PdxSerializer; +import com.gemstone.gemfire.pdx.PdxWriter; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.data.annotation.ReadOnlyProperty; +import org.springframework.data.annotation.Transient; +import org.springframework.data.gemfire.repository.sample.Address; +import org.springframework.data.gemfire.repository.sample.Person; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import sun.misc.BASE64Decoder; +import sun.misc.BASE64Encoder; + +/** + * Integration tests for {@link MappingPdxSerializer}. + * + * @author Oliver Gierke + * @author John Blum + */ +public class MappingPdxSerializerIntegrationTests { + + static Cache cache; + + static Region region; + + @BeforeClass + public static void setUp() { + + MappingPdxSerializer serializer = MappingPdxSerializer.newMappingPdxSerializer(); + + cache = new CacheFactory() + .set("name", MappingPdxSerializerIntegrationTests.class.getSimpleName()) + .set("log-level", "error") + .setPdxSerializer(serializer) + .setPdxPersistent(true) + .create(); + + region = cache.createRegionFactory() + .setDataPolicy(DataPolicy.PARTITION) + .create("TemporaryRegion"); + } + + @AfterClass + @SuppressWarnings("all") + public static void tearDown() { + + if (cache != null) { + cache.close(); + } + } + + @After + public void clearRegion() { + region.removeAll(region.keySet()); + } + + @Test + public void handlesEntityWithReadOnlyProperty() { + + EntityWithReadOnlyProperty entity = new EntityWithReadOnlyProperty(); + + entity.setName("ReadOnlyEntity"); + entity.setTimestamp(System.currentTimeMillis()); + 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.setValue("test"); + + 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.getValue()).isNull(); + } + + @Test + @SuppressWarnings("unchecked") + public void serializesAndDeserializesEntity() { + + 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; + + region.put(1L, person); + + Object result = region.get(1L); + + assertThat(result).isInstanceOf(Person.class); + assertThat(result).isNotSameAs(person); + + 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 serializesAndDeserializesEntityWithDataSerializableProperty() { + + Address address = new Address(); + + address.street = "100 Main St."; + address.city = "London"; + address.zipCode = "01234"; + + PersonWithDataSerializableProperty person = + new PersonWithDataSerializableProperty(2L, "Oliver", "Gierke", + new DataSerializableProperty("foo")); + + person.address = address; + + region.put(2L, person); + + Object result = region.get(2L); + + assertThat(result).isInstanceOf(PersonWithDataSerializableProperty.class); + assertThat(result).isNotSameAs(person); + + 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"); + } + + @Test + public void serializationUsesCustomPropertyNameBasedPdxSerializer() throws IOException { + + PdxSerializer mockPasswordSerializer = mock(PdxSerializer.class); + + when(mockPasswordSerializer.toData(any(), any(PdxWriter.class))).thenAnswer(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + + String password = invocation.getArgumentAt(0, String.class); + + PdxWriter pdxWriter = invocation.getArgumentAt(1, PdxWriter.class); + + pdxWriter.writeString("password", new BASE64Encoder().encode(password.getBytes())); + + return true; + } + }); + + when(mockPasswordSerializer.fromData(any(Class.class), any(PdxReader.class))).thenAnswer(new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + + PdxReader pdxReader = invocation.getArgumentAt(1, PdxReader.class); + + return pdxReader.readString("password"); + } + }); + + User jonDoe = User.newUser("jdoe", "p@55w0rd!"); + + assertThat(jonDoe).isNotNull(); + assertThat(jonDoe.getName()).isEqualTo("jdoe"); + assertThat(jonDoe.getPassword()).isEqualTo("p@55w0rd!"); + + String passwordPropertyName = User.class.getName().concat(".password"); + + ((MappingPdxSerializer) ((Cache) region.getRegionService()).getPdxSerializer()) + .setCustomPdxSerializers(Collections.singletonMap(passwordPropertyName, mockPasswordSerializer)); + + region.put(4L, jonDoe); + + Object result = region.get(4L); + + assertThat(result).isInstanceOf(User.class); + assertThat(result).isNotSameAs(jonDoe); + + User jonDoeLoaded = (User) result; + + assertThat(jonDoeLoaded.getName()).isEqualTo(jonDoe.getName()); + assertThat(jonDoeLoaded.getPassword()).describedAs("Password was [%s]", jonDoeLoaded.getPassword()) + .isNotEqualTo(jonDoe.getPassword()); + assertThat(new String(new BASE64Decoder().decodeBuffer(jonDoeLoaded.getPassword()))).isEqualTo(jonDoe.getPassword()); + + verify(mockPasswordSerializer, atLeastOnce()).toData(eq("p@55w0rd!"), isA(PdxWriter.class)); + + verify(mockPasswordSerializer, times(1)) + .fromData(eq(String.class), isA(PdxReader.class)); + } + + @Test + public void serializationUsesCustomPropertyTypeBasedPdxSerializer() { + + PdxSerializer mockCreditCardSerializer = mock(PdxSerializer.class); + + when(mockCreditCardSerializer.toData(any(), any(PdxWriter.class))).thenAnswer(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + + CreditCard creditCard = invocation.getArgumentAt(0, CreditCard.class); + + PdxWriter pdxWriter = invocation.getArgumentAt(1, PdxWriter.class); + + pdxWriter.writeLong("creditCard.expirationDate", creditCard.getExpirationDate()); + pdxWriter.writeString("creditCard.number", + new BASE64Encoder().encode(creditCard.getNumber().getBytes())); + pdxWriter.writeString("creditCard.type", creditCard.getType().name()); + + return true; + } + }); + + when(mockCreditCardSerializer.fromData(any(Class.class), any(PdxReader.class))).thenAnswer(new Answer() { + + @Override + public CreditCard answer(InvocationOnMock invocation) throws Throwable { + + PdxReader pdxReader = invocation.getArgumentAt(1, PdxReader.class); + + Long creditCardExpirationDate = pdxReader.readLong("creditCard.expirationDate"); + + String creditCardNumber = + new String(new BASE64Decoder().decodeBuffer(pdxReader.readString("creditCard.number"))); + + creditCardNumber = "xxxx-".concat(creditCardNumber.substring(creditCardNumber.length() - 4)); + + CreditCard.Type creditCardType = CreditCard.Type.valueOf(pdxReader.readString("creditCard.type")); + + return CreditCard.of(creditCardExpirationDate, creditCardNumber, creditCardType); + } + }); + + ((MappingPdxSerializer) ((Cache) region.getRegionService()).getPdxSerializer()) + .setCustomPdxSerializers(Collections.singletonMap(CreditCard.class, mockCreditCardSerializer)); + + CreditCard creditCard = CreditCard.of(System.currentTimeMillis(), + "8842-6789-4186-7981", CreditCard.Type.VISA); + + Customer jonDoe = Customer.newCustomer(creditCard, "Jon Doe"); + + region.put(8L, jonDoe); + + Object result = region.get(8L); + + assertThat(result).isInstanceOf(Customer.class); + assertThat(result).isNotSameAs(jonDoe); + + Customer jonDoeLoaded = (Customer) result; + + assertThat(jonDoeLoaded.getName()).isEqualTo(jonDoe.getName()); + assertThat(jonDoeLoaded.getCreditCard()).isNotEqualTo(jonDoe.getCreditCard()); + assertThat(jonDoeLoaded.getCreditCard().getExpirationDate()) + .isEqualTo(jonDoe.getCreditCard().getExpirationDate()); + assertThat(jonDoeLoaded.getCreditCard().getNumber()).isEqualTo("xxxx-7981"); + assertThat(jonDoeLoaded.getCreditCard().getType()).isEqualTo(jonDoe.getCreditCard().getType()); + + verify(mockCreditCardSerializer, atLeastOnce()).toData(eq(creditCard), isA(PdxWriter.class)); + + verify(mockCreditCardSerializer, times(1)) + .fromData(eq(CreditCard.class), isA(PdxReader.class)); + } + + @SuppressWarnings({ "serial", "unused" }) + public static class PersonWithDataSerializableProperty extends Person { + + private DataSerializableProperty property; + + public PersonWithDataSerializableProperty(Long id, String firstname, + String lastname, DataSerializableProperty property) { + + super(id, firstname, lastname); + + this.property = property; + } + + public DataSerializableProperty getDataSerializableProperty() { + return this.property; + } + + public void setDataSerializableProperty(DataSerializableProperty property) { + this.property = property; + } + } + + @SuppressWarnings("serial") + public static class DataSerializableProperty implements DataSerializable { + + static { + Instantiator.register(new Instantiator(DataSerializableProperty.class,101) { + public DataSerializable newInstance() { + return new DataSerializableProperty(""); + } + }); + } + + private String value; + + public DataSerializableProperty(String value) { + this.value = value; + } + + + @Override + public void fromData(DataInput dataInput) throws IOException, ClassNotFoundException { + this.value = dataInput.readUTF(); + + } + + @Override + public void toData(DataOutput dataOutput) throws IOException { + dataOutput.writeUTF(this.value); + } + + public String getValue() { + return this.value; + } + } + + @Getter + static class EntityWithReadOnlyProperty { + + @Setter + Long timestamp; + + @Setter + String name; + + // TODO: if there is no 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; + + @Transient + private Object value; + + } + + @Data + @AllArgsConstructor(staticName = "newUser") + static class User { + + String name; + String password; + + @SuppressWarnings("unused") + User() {} + + } + + @Data + @AllArgsConstructor(staticName = "newCustomer") + static class Customer { + + CreditCard creditCard; + String name; + + @SuppressWarnings("unused") + Customer() {} + + } + + @Data + @RequiredArgsConstructor(staticName = "of") + static class CreditCard { + + @NonNull Long expirationDate; + @NonNull String number; + @NonNull Type type; + + enum Type { + + AMERICAN_EXPRESS, + MASTER_CARD, + VISA, + + } + } +} 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 c235b455..bad4fe3b 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,38 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + 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 java.util.Collections; +import java.util.HashMap; +import java.util.Map; import com.gemstone.gemfire.pdx.PdxReader; import com.gemstone.gemfire.pdx.PdxSerializer; import com.gemstone.gemfire.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.runners.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.gemfire.test.support.MapBuilder; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.ParameterValueProvider; @@ -53,15 +60,16 @@ import org.springframework.data.mapping.model.ParameterValueProvider; * * @author Oliver Gierke * @author John Blum - * @see org.junit.Rule * @see org.junit.Test - * @see org.junit.rules.ExpectedException * @see org.junit.runner.RunWith * @see org.mockito.Mock * @see org.mockito.Mockito * @see org.mockito.runners.MockitoJUnitRunner * @see org.springframework.core.convert.ConversionService + * @see org.springframework.data.convert.EntityInstantiator * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer + * @see org.springframework.data.mapping.PersistentEntity + * @see org.springframework.data.mapping.PersistentProperty * @see com.gemstone.gemfire.pdx.PdxReader * @see com.gemstone.gemfire.pdx.PdxSerializer * @see com.gemstone.gemfire.pdx.PdxWriter @@ -71,12 +79,9 @@ public class MappingPdxSerializerUnitTests { ConversionService conversionService; - GemfireMappingContext context; + GemfireMappingContext mappingContext; - MappingPdxSerializer serializer; - - @Rule - public ExpectedException expectedException = ExpectedException.none(); + MappingPdxSerializer pdxSerializer; @Mock EntityInstantiator mockInstantiator; @@ -84,24 +89,82 @@ public class MappingPdxSerializerUnitTests { @Mock PdxReader mockReader; - @Mock - PdxSerializer mockAddressSerializer; - @Mock PdxWriter mockWriter; @Before public void setUp() { - context = new GemfireMappingContext(); - conversionService = new GenericConversionService(); - serializer = new MappingPdxSerializer(context, conversionService); - serializer.setCustomSerializers(Collections., PdxSerializer>singletonMap( - Address.class, mockAddressSerializer)); + + this.conversionService = new GenericConversionService(); + this.mappingContext = new GemfireMappingContext(); + this.pdxSerializer = new MappingPdxSerializer(this.mappingContext, this.conversionService); + } + + private String toFullyQualifiedPropertyName(PersistentProperty persistentProperty) { + return this.pdxSerializer.toFullyQualifiedPropertyName(persistentProperty); } @Test - public void createFullyInitialized() { + public void constructDefaultMappingPdxSerializer() { + + MappingPdxSerializer pdxSerializer = new MappingPdxSerializer(); + + assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class); + assertThat(pdxSerializer.getCustomPdxSerializers()).isEmpty(); + assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(pdxSerializer.getMappingContext()).isInstanceOf(GemfireMappingContext.class); + } + + @Test + public void constructMappingPdxSerializerWithProvidedMappingContextAndConversionService() { + ConversionService mockConversionService = mock(ConversionService.class); + + GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class); + + MappingPdxSerializer pdxSerializer = new MappingPdxSerializer(mockMappingContext, mockConversionService); + + assertThat(pdxSerializer.getConversionService()).isEqualTo(mockConversionService); + assertThat(pdxSerializer.getCustomPdxSerializers()).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); MappingPdxSerializer pdxSerializer = MappingPdxSerializer.create(mockMappingContext, mockConversionService); @@ -112,18 +175,20 @@ public class MappingPdxSerializerUnitTests { } @Test - public void createWithNullConversionService() { + public void createMappingPdxSerializerWithNullConversionService() { + GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class); MappingPdxSerializer pdxSerializer = MappingPdxSerializer.create(mockMappingContext, null); assertThat(pdxSerializer).isNotNull(); - assertThat(pdxSerializer.getConversionService()).isInstanceOf(ConversionService.class); + assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class); assertThat(pdxSerializer.getMappingContext()).isEqualTo(mockMappingContext); } @Test - public void createWithNullMappingContext() { + public void createMappingPdxSerializerWithNullMappingContext() { + ConversionService mockConversionService = mock(ConversionService.class); MappingPdxSerializer pdxSerializer = MappingPdxSerializer.create(null, mockConversionService); @@ -134,142 +199,492 @@ public class MappingPdxSerializerUnitTests { } @Test - public void createWithNullConversionServiceAndNullMappingContext() { + public void createMappingPdxSerializerWithNullConversionServiceAndNullMappingContext() { + MappingPdxSerializer pdxSerializer = MappingPdxSerializer.create(null, null); assertThat(pdxSerializer).isNotNull(); - assertThat(pdxSerializer.getConversionService()).isInstanceOf(ConversionService.class); + assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class); assertThat(pdxSerializer.getMappingContext()).isInstanceOf(GemfireMappingContext.class); } @Test - @SuppressWarnings("unchecked") - public void usesRegisteredInstantiator() { - Address address = new Address(); - address.city = "London"; - address.zipCode = "01234"; + public void setCustomPdxSerializersWithMappingOfClassTypesToPdxSerializers() { - Person person = new Person(1L, "Oliver", "Gierke"); - person.address = address; + Map customPdxSerializers = + Collections.singletonMap(Person.class, mock(PdxSerializer.class)); - when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) - .thenReturn(person); + this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers); - serializer.setGemfireInstantiators(Collections., - EntityInstantiator>singletonMap(Person.class, mockInstantiator)); + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEqualTo(customPdxSerializers); + } - serializer.fromData(Person.class, mockReader); + @Test(expected = IllegalArgumentException.class) + public void setCustomPdxSerializersToNull() { - verify(mockInstantiator, times(1)).createInstance(eq(context.getPersistentEntity(Person.class)), - any(ParameterValueProvider.class)); - verify(mockAddressSerializer, times(1)).fromData(eq(Address.class), any(PdxReader.class)); + try { + this.pdxSerializer.setCustomPdxSerializers(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Custom PdxSerializers are required"); + assertThat(expected).hasNoCause(); + + throw expected; + } } @Test - public void fromDataMapsPdxDataToApplicationDomainObject() { + @SuppressWarnings("all") + public void getCustomPdxSerializerForMappedPersistentPropertyReturnsSerializerForProperty() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockPropertySerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() + .put(addressProperty, mockPropertySerializer) + .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) + .put(Address.class, mockTypedSerializer) + .build()); + + assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockPropertySerializer); + } + + @Test + @SuppressWarnings("all") + public void getCustomPdxSerializerForMappedPersistentPropertyReturnsSerializerForPropertyName() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() + .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) + .put(Address.class, mockTypedSerializer) + .build()); + + assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockNamedSerializer); + } + + @Test + @SuppressWarnings("all") + public void getCustomPdxSerializerForMappedPersistentPropertyReturnsSerializerForPropertyType() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + Map customPdxSerializers = new HashMap(); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + customPdxSerializers.put("example.Type.address", mockNamedSerializer); + customPdxSerializers.put(Address.class, mockTypedSerializer); + + this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers); + + assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockTypedSerializer); + } + + @Test + @SuppressWarnings("all") + public void getCustomPdxSerializerForUnmappedPersistentPropertyReturnsNull() { + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); + assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isNull(); + } + + @Test + @SuppressWarnings("deprecation") + public void getCustomSerializerForMappedType() { + + PdxSerializer mockPdxSerializer = mock(PdxSerializer.class); + + this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Person.class, mockPdxSerializer)); + + assertThat(this.pdxSerializer.getCustomSerializer(Person.class)).isEqualTo(mockPdxSerializer); + } + + @Test + @SuppressWarnings("deprecation") + public void getCustomSerializerForUnmappedTypeReturnsNull() { + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); + assertThat(this.pdxSerializer.getCustomSerializer(Address.class)).isNull(); + } + + @Test + public void toFullyQualifiedPropertyName() { + + PersistentEntity mockEntity = mock(PersistentEntity.class); + PersistentProperty mockProperty = mock(PersistentProperty.class); + + when(mockProperty.getName()).thenReturn("mockProperty"); + when(mockProperty.getOwner()).thenReturn(mockEntity); + when(mockEntity.getType()).thenReturn(Person.class); + + assertThat(this.pdxSerializer.toFullyQualifiedPropertyName(mockProperty)) + .isEqualTo(Person.class.getName().concat(".mockProperty")); + + verify(mockEntity, times(1)).getType(); + verify(mockProperty, times(1)).getName(); + verify(mockProperty, times(1)).getOwner(); + } + + @Test + public void setGemfireInstantiatorsWithMappingOfClassTypesToEntityInstantiators() { + + Map, EntityInstantiator> entityInstantiators = + Collections., EntityInstantiator>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(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("GemFire EntityInstantiators are required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void getInstantiatorForManagedPersistentEntityWithInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Person.class); + + this.pdxSerializer.setGemfireInstantiators( + Collections., EntityInstantiator>singletonMap(Person.class, mockEntityInstantiator)); + + assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isEqualTo(mockEntityInstantiator); + + verify(mockEntity, atLeast(1)).getType(); + verifyZeroInteractions(mockEntityInstantiator); + } + + @Test + public void getInstantiatorForNonManagedPersistentEntityWithNoInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Address.class); + + this.pdxSerializer.setGemfireInstantiators( + Collections., EntityInstantiator>singletonMap(Person.class, mockEntityInstantiator)); + + 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 fromDataDeserializesPdxBytesAndMapsToApplicationDomainObject() { + Address expectedAddress = new Address(); + + expectedAddress.street = "100 Main St."; expectedAddress.city = "Portland"; expectedAddress.zipCode = "12345"; - when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + PdxSerializer mockAddressSerializer = mock(PdxSerializer.class); + + when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) .thenReturn(new Person(null, null, null)); - when(mockReader.readField(eq("id"))).thenReturn(1l); - when(mockReader.readField(eq("firstname"))).thenReturn("Jon"); - when(mockReader.readField(eq("lastname"))).thenReturn("Doe"); - when(mockAddressSerializer.fromData(eq(Address.class), eq(mockReader))).thenReturn(expectedAddress); + when(this.mockReader.readField(eq("id"))).thenReturn(1L); + when(this.mockReader.readField(eq("firstname"))).thenReturn("Jon"); + when(this.mockReader.readField(eq("lastname"))).thenReturn("Doe"); + when(mockAddressSerializer.fromData(eq(Address.class), eq(this.mockReader))).thenReturn(expectedAddress); - serializer.setGemfireInstantiators(Collections., EntityInstantiator>singletonMap( - Person.class, mockInstantiator)); + this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); + this.pdxSerializer.setGemfireInstantiators( + Collections., EntityInstantiator>singletonMap(Person.class, this.mockInstantiator)); - Object obj = serializer.fromData(Person.class, mockReader); + Object obj = this.pdxSerializer.fromData(Person.class, this.mockReader); assertThat(obj).isInstanceOf(Person.class); Person jonDoe = (Person) obj; assertThat(jonDoe.getAddress()).isEqualTo(expectedAddress); - assertThat(jonDoe.getId()).isEqualTo(1l); + assertThat(jonDoe.getId()).isEqualTo(1L); assertThat(jonDoe.getFirstname()).isEqualTo("Jon"); assertThat(jonDoe.getLastname()).isEqualTo("Doe"); - 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)); + verify(this.mockInstantiator, times(1)) + .createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); + verify(this.mockReader, times(1)).readField(eq("id")); + verify(this.mockReader, times(1)).readField(eq("firstname")); + verify(this.mockReader, times(1)).readField(eq("lastname")); + verify(mockAddressSerializer, times(1)) + .fromData(eq(Address.class), eq(this.mockReader)); + } + + @Test(expected = MappingException.class) + @SuppressWarnings("unchecked") + public void fromDataHandlesException() { + + when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + .thenReturn(new Person(null, null, null)); + + when(this.mockReader.readField(eq("id"))).thenThrow(new IllegalArgumentException("test")); + + try { + this.pdxSerializer.setGemfireInstantiators( + Collections., EntityInstantiator>singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.fromData(Person.class, this.mockReader); + } + catch (MappingException expected) { + + 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(this.mockInstantiator, times(1)) + .createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); + + verify(this.mockReader, times(1)).readField(eq("id")); + } } @Test - public void fromDataHandlesExceptionProperly() { - when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) - .thenReturn(new Person(null, null, null)); - when(mockReader.readField(eq("id"))).thenThrow(new IllegalArgumentException("test")); + @SuppressWarnings("unchecked") + public void fromDataUsesRegisteredInstantiator() { - serializer.setGemfireInstantiators(Collections., EntityInstantiator>singletonMap( - Person.class, mockInstantiator)); + Address address = new Address(); - try { - expectedException.expect(MappingException.class); - expectedException.expectCause(isA(IllegalArgumentException.class)); - expectedException.expectMessage(String.format( - "while setting value [null] of property [id] for entity of type [%1$s] from PDX", Person.class)); + address.street = "100 Main St."; + address.city = "London"; + address.zipCode = "01234"; - serializer.fromData(Person.class, mockReader); - } - finally { - verify(mockInstantiator, times(1)).createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); - verify(mockReader, times(1)).readField(eq("id")); - } + PdxSerializer mockAddressSerializer = mock(PdxSerializer.class); + + Person person = new Person(1L, "Oliver", "Gierke"); + + person.address = address; + + when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + .thenReturn(person); + + this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); + this.pdxSerializer.setGemfireInstantiators( + Collections., EntityInstantiator>singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.fromData(Person.class, this.mockReader); + + GemfirePersistentEntity persistentEntity = this.mappingContext.getPersistentEntity(Person.class); + + verify(this.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"; - Person jonDoe = new Person(1l, "Jon", "Doe"); + PdxSerializer mockAddressSerializer = mock(PdxSerializer.class); + + Person jonDoe = new Person(1L, "Jon", "Doe"); + jonDoe.address = address; - serializer.setCustomSerializers(Collections., PdxSerializer>singletonMap( - Address.class, mockAddressSerializer)); + this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); - assertThat(serializer.toData(jonDoe, mockWriter)).isTrue(); + assertThat(this.pdxSerializer.toData(jonDoe, this.mockWriter)).isTrue(); - verify(mockAddressSerializer, times(1)).toData(eq(address), eq(mockWriter)); - verify(mockWriter, times(1)).writeField(eq("id"), eq(1l), eq(Long.class)); - verify(mockWriter, times(1)).writeField(eq("firstname"), eq("Jon"), eq(String.class)); - verify(mockWriter, times(1)).writeField(eq("lastname"), eq("Doe"), eq(String.class)); - verify(mockWriter, times(1)).markIdentityField(eq("id")); + verify(mockAddressSerializer, times(1)).toData(eq(address), eq(this.mockWriter)); + + verify(this.mockWriter, times(1)) + .writeField(eq("id"), eq(1L), eq(Long.class)); + + verify(this.mockWriter, times(1)) + .writeField(eq("firstname"), eq("Jon"), eq(String.class)); + + verify(this.mockWriter, times(1)) + .writeField(eq("lastname"), eq("Doe"), eq(String.class)); + + verify(this.mockWriter, times(1)).markIdentityField(eq("id")); } - @Test - public void toDataHandlesExceptionProperly() { + @Test(expected = MappingException.class) + public void toDataHandlesException() { + Address address = new Address(); + + address.street = "100 Main St."; address.city = "Portland"; address.zipCode = "12345"; - Person jonDoe = new Person(1l, "Jon", "Doe"); + Person jonDoe = new Person(1L, "Jon", "Doe"); + jonDoe.address = address; - when(mockWriter.writeField(eq("address"), eq(address), eq(Address.class))) + when(this.mockWriter.writeField(eq("address"), eq(address), eq(Address.class))) .thenThrow(new IllegalArgumentException("test")); try { - expectedException.expect(MappingException.class); - expectedException.expectCause(isA(IllegalArgumentException.class)); - expectedException.expectMessage(String.format( - "Error while serializing entity property [address] value [Portland, 12345] of type [%s] to PDX", - Person.class)); + this.pdxSerializer.setCustomPdxSerializers(Collections.emptyMap()); + this.pdxSerializer.toData(jonDoe, this.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)); - verify(mockWriter, atMost(1)).writeField(eq("firstname"), eq("Jon"), eq(String.class)); - verify(mockWriter, atMost(1)).writeField(eq("lastname"), eq("Doe"), eq(String.class)); - verify(mockWriter, times(1)).writeField(eq("address"), eq(address), eq(Address.class)); - verify(mockWriter, never()).markIdentityField(anyString()); + + verify(this.mockWriter, atMost(1)) + .writeField(eq("id"), eq(1L), eq(Long.class)); + + verify(this.mockWriter, atMost(1)) + .writeField(eq("firstname"), eq("Jon"), eq(String.class)); + + verify(this.mockWriter, atMost(1)) + .writeField(eq("lastname"), eq("Doe"), eq(String.class)); + + verify(this.mockWriter, times(1)) + .writeField(eq("address"), eq(address), eq(Address.class)); + + verify(this.mockWriter, never()).markIdentityField(anyString()); } } } diff --git a/src/test/java/org/springframework/data/gemfire/repository/cdi/CdiExtensionIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/repository/cdi/CdiExtensionIntegrationTest.java index a8889dc9..13b03c99 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/cdi/CdiExtensionIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/cdi/CdiExtensionIntegrationTest.java @@ -24,6 +24,9 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import com.gemstone.gemfire.cache.CacheClosedException; +import com.gemstone.gemfire.cache.CacheFactory; + import org.apache.webbeans.cditest.CdiTestContainer; import org.apache.webbeans.cditest.CdiTestContainerLoader; import org.junit.AfterClass; @@ -31,9 +34,6 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.data.gemfire.repository.sample.Person; -import com.gemstone.gemfire.cache.CacheClosedException; -import com.gemstone.gemfire.cache.CacheFactory; - /** * The CdiExtensionIntegrationTest class... * @@ -62,6 +62,7 @@ public class CdiExtensionIntegrationTest { } private static void closeGemfireCache() { + try { CacheFactory.getAnyInstance().close(); } @@ -69,7 +70,8 @@ public class CdiExtensionIntegrationTest { } } - protected void assertIsExpectedPerson(Person actual, Person expected) { + private void assertIsExpectedPerson(Person actual, Person expected) { + assertThat(actual.getId(), is(equalTo(expected.getId()))); assertThat(actual.getFirstname(), is(equalTo(expected.getFirstname()))); assertThat(actual.getLastname(), is(equalTo(expected.getLastname()))); @@ -77,6 +79,7 @@ public class CdiExtensionIntegrationTest { @Test public void bootstrapsRepositoryCorrectly() { + RepositoryClient repositoryClient = container.getInstance(RepositoryClient.class); assertThat(repositoryClient.getPersonRepository(), is(notNullValue())); @@ -84,7 +87,7 @@ public class CdiExtensionIntegrationTest { Person expectedJonDoe = repositoryClient.newPerson("Jon", "Doe"); assertThat(expectedJonDoe, is(notNullValue())); - assertThat(expectedJonDoe.getId(), is(greaterThan(0l))); + assertThat(expectedJonDoe.getId(), is(greaterThan(0L))); assertThat(expectedJonDoe.getName(), is(equalTo("Jon Doe"))); Person savedJonDoe = repositoryClient.save(expectedJonDoe); @@ -101,9 +104,9 @@ public class CdiExtensionIntegrationTest { @Test public void returnOneFromCustomImplementation() { + RepositoryClient repositoryClient = container.getInstance(RepositoryClient.class); assertThat(repositoryClient.getPersonRepository().returnOne(), is(equalTo(1))); } - } 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 60016e0f..dd8b67e1 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,22 +17,53 @@ package org.springframework.data.gemfire.repository.sample; import org.springframework.data.annotation.Id; import org.springframework.data.gemfire.mapping.Region; +import org.springframework.util.ObjectUtils; /** - * + * * @author Oliver Gierke */ @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/test/support/MapBuilder.java b/src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java new file mode 100644 index 00000000..42fc7500 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.test.support; + +import java.util.HashMap; +import java.util.Map; + +/** + * The {@link MapBuilder} class employs the Builder Software Design Pattern to build a {@link Map}. + * + * @author John Blum + * @see java.util.Map + * @since 2.0.0 + */ +public class MapBuilder { + + public static MapBuilder newMapBuilder() { + return new MapBuilder(); + } + + private final Map map = new HashMap(); + + public MapBuilder put(KEY key, VALUE value) { + this.map.put(key, value); + return this; + } + + public MapBuilder remove(KEY key) { + this.map.remove(key); + return this; + } + + public Map build() { + return this.map; + } +}