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