SGF-719 - Extend MappingPdxSerializer to allow registering custom PdxSerializers based on fully qualified property name.
This commit is contained in:
@@ -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<Class<?>, PdxSerializer> customSerializers;
|
||||
private Map<?, PdxSerializer> 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<Class<?>, PdxSerializer> customSerializers) {
|
||||
Assert.notNull(customSerializers);
|
||||
this.customSerializers = customSerializers;
|
||||
public void setCustomPdxSerializers(Map<?, PdxSerializer> 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<Class<?>, 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<?, PdxSerializer> getCustomPdxSerializers() {
|
||||
return Collections.unmodifiableMap(this.customPdxSerializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link #getCustomPdxSerializers()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<Class<?>, PdxSerializer> getCustomSerializers() {
|
||||
return Collections.unmodifiableMap(customSerializers);
|
||||
return (Map<Class<?>, 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<?, PdxSerializer> 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<Class<?>, 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<GemfirePersistentProperty>(entity,
|
||||
new GemfirePropertyValueProvider(reader), null));
|
||||
|
||||
final PersistentPropertyAccessor propertyAccessor =
|
||||
new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService());
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<GemfirePersistentProperty>() {
|
||||
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<GemfirePersistentProperty>() {
|
||||
@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<Object>) 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<GemfirePersistentProperty>(entity,
|
||||
new GemfirePropertyValueProvider(reader), null));
|
||||
|
||||
final PersistentPropertyAccessor propertyAccessor =
|
||||
new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), getConversionService());
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<GemfirePersistentProperty>() {
|
||||
|
||||
@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<GemfirePersistentProperty>() {
|
||||
|
||||
@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<Object>) 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object, Object> 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<Boolean>() {
|
||||
|
||||
@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<String>() {
|
||||
|
||||
@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<Boolean>() {
|
||||
|
||||
@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<CreditCard>() {
|
||||
|
||||
@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,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.<Class<?>, 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<?, PdxSerializer> customPdxSerializers =
|
||||
Collections.<Object, PdxSerializer>singletonMap(Person.class, mock(PdxSerializer.class));
|
||||
|
||||
when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)))
|
||||
.thenReturn(person);
|
||||
this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers);
|
||||
|
||||
serializer.setGemfireInstantiators(Collections.<Class<?>,
|
||||
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.<Object, PdxSerializer>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.<Object, PdxSerializer>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<Object, PdxSerializer> customPdxSerializers = new HashMap<Object, PdxSerializer>();
|
||||
|
||||
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<Class<?>, EntityInstantiator> entityInstantiators =
|
||||
Collections.<Class<?>, 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.<Class<?>, 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.<Class<?>, 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.<Class<?>, EntityInstantiator>singletonMap(
|
||||
Person.class, mockInstantiator));
|
||||
this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer));
|
||||
this.pdxSerializer.setGemfireInstantiators(
|
||||
Collections.<Class<?>, 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.<Class<?>, 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.<Class<?>, 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.<Class<?>, 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.<Class<?>, 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.<Object, PdxSerializer>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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<KEY, VALUE> {
|
||||
|
||||
public static <KEY, VALUE> MapBuilder<KEY, VALUE> newMapBuilder() {
|
||||
return new MapBuilder<KEY, VALUE>();
|
||||
}
|
||||
|
||||
private final Map<KEY, VALUE> map = new HashMap<KEY, VALUE>();
|
||||
|
||||
public MapBuilder<KEY, VALUE> put(KEY key, VALUE value) {
|
||||
this.map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapBuilder<KEY, VALUE> remove(KEY key) {
|
||||
this.map.remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<KEY, VALUE> build() {
|
||||
return this.map;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user