SGF-719 - Extend MappingPdxSerializer to allow registering custom PdxSerializers based on fully qualified property name.

This commit is contained in:
John Blum
2018-02-12 18:41:01 -08:00
parent 73a4ae5979
commit 30e443bb55
3 changed files with 584 additions and 153 deletions

View File

@@ -32,32 +32,37 @@ import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mapping.MappingException;
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;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
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 org.apache.geode.pdx.PdxReader
* @see org.apache.geode.pdx.PdxSerializer
* @see org.apache.geode.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.convert.EntityInstantiators
* @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.apache.geode.pdx.PdxReader
* @see org.apache.geode.pdx.PdxSerializer
* @see org.apache.geode.pdx.PdxWriter
* @see org.springframework.data.mapping.model.PersistentEntityParameterValueProvider
* @since 1.2.0
*/
public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAware {
@@ -70,51 +75,115 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
private final Logger logger = LoggerFactory.getLogger(getClass());
private Map<Class<?>, PdxSerializer> customSerializers;
private Map<?, PdxSerializer> customPdxSerializers;
// TODO: decide what to do with this; the SpELContext is not used
// 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(@Nullable ConversionService conversionService) {
return create(newMappingContext(), conversionService);
}
public static MappingPdxSerializer create(@Nullable 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
*/
public static MappingPdxSerializer create(GemfireMappingContext mappingContext,
ConversionService conversionService) {
public static MappingPdxSerializer create(@Nullable GemfireMappingContext mappingContext,
@Nullable 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) {
@@ -124,7 +193,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
this.mappingContext = mappingContext;
this.conversionService = conversionService;
this.entityInstantiators = new EntityInstantiators();
this.customSerializers = Collections.emptyMap();
this.customPdxSerializers = Collections.emptyMap();
this.context = new SpELContext(PdxReaderPropertyAccessor.INSTANCE);
}
@@ -143,56 +212,106 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* 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}.
* @return a reference to the configured {@link ConversionService}.
* @see org.springframework.core.convert.ConversionService
*/
@NonNull
protected ConversionService getConversionService() {
return this.conversionService;
}
/**
* Configures custom {@link PdxSerializer PDX serializers} used to serialize
* specific application {@link Class class types}.
* Configures custom {@link PdxSerializer PDX serializers} used to customize the serialization for specific
* application {@link Class domain types}.
*
* @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}.
* @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 org.apache.geode.pdx.PdxSerializer
* @see java.util.Map
*/
public void setCustomPdxSerializers(@NonNull Map<?, PdxSerializer> customPdxSerializers) {
Assert.notNull(customPdxSerializers, "Custom PdxSerializers are required");
this.customPdxSerializers = customPdxSerializers;
}
/**
* @deprecated please use ({@link #setCustomPdxSerializers(Map)} instead.
*/
@Deprecated
public void setCustomSerializers(Map<Class<?>, PdxSerializer> customSerializers) {
Assert.notNull(customSerializers, "Custom PdxSerializers are required");
this.customSerializers = customSerializers;
setCustomPdxSerializers(customSerializers);
}
/**
* 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.
* 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 domain object {@link Class types}
* to custom {@link PdxSerializer PdxSerializers}.
* @return a {@link Map mapping} of application {@link Class domain types}
* to custom {@link PdxSerializer PDX serializers}.
* @see org.apache.geode.pdx.PdxSerializer
* @see java.util.Map
*/
protected Map<Class<?>, PdxSerializer> getCustomSerializers() {
return Collections.unmodifiableMap(this.customSerializers);
@NonNull
protected Map<?, PdxSerializer> getCustomPdxSerializers() {
return Collections.unmodifiableMap(this.customPdxSerializers);
}
/**
* 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 org.apache.geode.pdx.PdxSerializer
* @see java.lang.Class
* @deprecated please use {@link #getCustomPdxSerializers()} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
protected Map<Class<?>, PdxSerializer> getCustomSerializers() {
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 org.apache.geode.pdx.PdxSerializer
*/
@Nullable
protected PdxSerializer getCustomPdxSerializer(@NonNull 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
*/
@NonNull
String toFullyQualifiedPropertyName(@NonNull PersistentProperty<?> property) {
return property.getOwner().getType().getName().concat(".").concat(property.getName());
}
/**
* @deprecated please use {@link #getCustomPdxSerializer(PersistentProperty)} instead.
*/
@Nullable
@Deprecated
protected PdxSerializer getCustomSerializer(Class<?> type) {
return getCustomSerializers().get(type);
return getCustomPdxSerializers().get(type);
}
/**
@@ -203,7 +322,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* read by this {@link PdxSerializer}; must not be {@literal null}.
* @see org.springframework.data.convert.EntityInstantiator
*/
public void setGemfireInstantiators(EntityInstantiators entityInstantiators) {
public void setGemfireInstantiators(@NonNull EntityInstantiators entityInstantiators) {
Assert.notNull(entityInstantiators, "EntityInstantiators are required");
@@ -219,7 +338,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* @see org.springframework.data.convert.EntityInstantiator
* @see java.util.Map
*/
public void setGemfireInstantiators(Map<Class<?>, EntityInstantiator> gemfireInstantiators) {
public void setGemfireInstantiators(@NonNull Map<Class<?>, EntityInstantiator> gemfireInstantiators) {
setGemfireInstantiators(new EntityInstantiators(gemfireInstantiators));
}
@@ -253,6 +372,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* @return a reference to the configured {@link Logger}.
* @see org.slf4j.Logger
*/
@NonNull
protected Logger getLogger() {
return this.logger;
}
@@ -264,6 +384,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* @return a reference to the configured {@link GemfireMappingContext mapping context} for Pivotal GemFire.
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
*/
@NonNull
protected GemfireMappingContext getMappingContext() {
return this.mappingContext;
}
@@ -276,7 +397,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
* @see #getPersistentEntity(Class)
*/
protected GemfirePersistentEntity<?> getPersistentEntity(Object entity) {
protected GemfirePersistentEntity<?> getPersistentEntity(@NonNull Object entity) {
return getPersistentEntity(entity.getClass());
}
@@ -308,19 +429,19 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
if (isWritable(entity, persistentProperty)) {
PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType());
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, (customSerializer != null ?
String.format(" using custom PdxSerializer [%1$s]", customSerializer) : "")));
persistentProperty.getName(), instance, type, (customPdxSerializer != null ?
String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : "")));
}
value = (customSerializer != null
? customSerializer.fromData(persistentProperty.getType(), reader)
value = (customPdxSerializer != null
? customPdxSerializer.fromData(persistentProperty.getType(), reader)
: reader.readField(persistentProperty.getName()));
if (getLogger().isDebugEnabled()) {
@@ -332,8 +453,8 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
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);
value, persistentProperty.getName(), type, (customPdxSerializer != null ?
String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : "")), cause);
}
}
});
@@ -365,7 +486,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
if (isReadable(persistentProperty)) {
PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType());
PdxSerializer customPdxSerializer = getCustomPdxSerializer(persistentProperty);
Object propertyValue = null;
@@ -376,12 +497,12 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
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) : "")));
ObjectUtils.nullSafeClassName(propertyValue), (customPdxSerializer != null
? String.format(" using custom PdxSerializer [%s]", customPdxSerializer) : "")));
}
if (customSerializer != null) {
customSerializer.toData(propertyValue, writer);
if (customPdxSerializer != null) {
customPdxSerializer.toData(propertyValue, writer);
}
else {
writer.writeField(persistentProperty.getName(), propertyValue,
@@ -392,9 +513,9 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
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
ObjectUtils.nullSafeClassName(propertyValue), (customPdxSerializer != null
? String.format(" using custom PdxSerializer [%1$s].",
customSerializer.getClass().getName()) : "")), cause);
customPdxSerializer.getClass().getName()) : "")), cause);
}
}
});

View File

@@ -17,11 +17,24 @@
package org.springframework.data.gemfire.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.Base64;
import java.util.Collections;
import java.util.Optional;
import org.apache.geode.DataSerializable;
import org.apache.geode.Instantiator;
@@ -29,17 +42,24 @@ import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.pdx.PdxReader;
import org.apache.geode.pdx.PdxSerializer;
import org.apache.geode.pdx.PdxWriter;
import org.junit.After;
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.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
/**
@@ -57,8 +77,7 @@ public class MappingPdxSerializerIntegrationTests {
@BeforeClass
public static void setUp() {
MappingPdxSerializer serializer =
new MappingPdxSerializer(new GemfireMappingContext(), new DefaultConversionService());
MappingPdxSerializer serializer = MappingPdxSerializer.newMappingPdxSerializer();
cache = new CacheFactory()
.set("name", MappingPdxSerializerIntegrationTests.class.getSimpleName())
@@ -78,6 +97,11 @@ public class MappingPdxSerializerIntegrationTests {
GemfireUtils.close(cache);
}
@After
public void clearRegion() {
region.removeAll(region.keySet());
}
@Test
public void handlesEntityWithReadOnlyProperty() {
@@ -126,7 +150,7 @@ public class MappingPdxSerializerIntegrationTests {
@Test
@SuppressWarnings("unchecked")
public void serializesAndDeserializesEntityCorrectly() {
public void serializesAndDeserializesEntity() {
Address address = new Address();
@@ -137,6 +161,7 @@ public class MappingPdxSerializerIntegrationTests {
Person person = new Person(1L, "Oliver", "Gierke");
person.address = address;
region.put(1L, person);
Object result = region.get(1L);
@@ -181,6 +206,138 @@ public class MappingPdxSerializerIntegrationTests {
assertThat(reference.property.getValue()).isEqualTo("foo");
}
@Test
public void serializationUsesCustomPropertyNameBasedPdxSerializer() {
PdxSerializer mockPasswordSerializer = mock(PdxSerializer.class);
when(mockPasswordSerializer.toData(any(), any(PdxWriter.class))).thenAnswer(invocation -> {
String password = invocation.getArgument(0);
PdxWriter pdxWriter = invocation.getArgument(1);
pdxWriter.writeByteArray("password", Base64.getEncoder().encode(password.getBytes()));
return true;
});
when(mockPasswordSerializer.fromData(any(Class.class), any(PdxReader.class))).thenAnswer(invocation -> {
PdxReader pdxReader = invocation.getArgument(1);
return new String(pdxReader.readByteArray("password"));
});
User jonDoe = User.newUser("jdoe", "p@55w0rd!");
assertThat(jonDoe).isNotNull();
assertThat(jonDoe.getName()).isEqualTo("jdoe");
assertThat(jonDoe.getPassword()).isEqualTo("p@55w0rd!");
Optional.of(region.getRegionService())
.filter(regionService -> regionService instanceof Cache)
.map(regionService -> ((Cache) regionService).getPdxSerializer())
.filter(pdxSerializer -> pdxSerializer instanceof MappingPdxSerializer)
.ifPresent(pdxSerializer -> {
String passwordPropertyName = User.class.getName().concat(".password");
((MappingPdxSerializer) pdxSerializer)
.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(Base64.getDecoder().decode(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(invocation -> {
CreditCard creditCard = invocation.getArgument(0);
PdxWriter pdxWriter = invocation.getArgument(1);
pdxWriter.writeLong("creditCard.expirationDate", creditCard.getExpirationDate().toEpochDay());
pdxWriter.writeByteArray("creditCard.number",
Base64.getEncoder().encode(creditCard.getNumber().getBytes()));
pdxWriter.writeString("creditCard.type", creditCard.getType().name());
return true;
});
when(mockCreditCardSerializer.fromData(any(Class.class), any(PdxReader.class))).thenAnswer(invocation -> {
PdxReader pdxReader = invocation.getArgument(1);
LocalDate creditCardExpirationDate =
LocalDate.ofEpochDay(pdxReader.readLong("creditCard.expirationDate"));
String creditCardNumber =
new String(Base64.getDecoder().decode(pdxReader.readByteArray("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);
});
Optional.of(region.getRegionService())
.filter(regionService -> regionService instanceof Cache)
.map(regionService -> ((Cache) regionService).getPdxSerializer())
.filter(pdxSerializer -> pdxSerializer instanceof MappingPdxSerializer)
.ifPresent(pdxSerializer -> ((MappingPdxSerializer) pdxSerializer)
.setCustomPdxSerializers(Collections.singletonMap(CreditCard.class, mockCreditCardSerializer)));
CreditCard creditCard = CreditCard.of(LocalDate.of(2020, Month.FEBRUARY, 12),
"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 {
@@ -246,10 +403,11 @@ public class MappingPdxSerializerIntegrationTests {
@Setter
String name;
// TODO: if there is not setter, then effectively this field/property is read-only
// 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
@@ -261,5 +419,47 @@ public class MappingPdxSerializerIntegrationTests {
@Transient
private Object valueTwo;
}
@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 LocalDate expirationDate;
@NonNull String number;
@NonNull Type type;
enum Type {
AMERICAN_EXPRESS,
MASTER_CARD,
VISA,
}
}
}

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@@ -49,8 +50,10 @@ 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.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.ParameterValueProvider;
/**
@@ -86,9 +89,6 @@ public class MappingPdxSerializerUnitTests {
@Mock
PdxReader mockReader;
@Mock
PdxSerializer mockAddressSerializer;
@Mock
PdxWriter mockWriter;
@@ -98,7 +98,10 @@ public class MappingPdxSerializerUnitTests {
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));
}
private String toFullyQualifiedPropertyName(PersistentProperty<?> persistentProperty) {
return this.pdxSerializer.toFullyQualifiedPropertyName(persistentProperty);
}
@Test
@@ -107,21 +110,22 @@ public class MappingPdxSerializerUnitTests {
MappingPdxSerializer pdxSerializer = new MappingPdxSerializer();
assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class);
assertThat(pdxSerializer.getCustomSerializers()).isEmpty();
assertThat(pdxSerializer.getCustomPdxSerializers()).isEmpty();
assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class);
assertThat(pdxSerializer.getMappingContext()).isInstanceOf(GemfireMappingContext.class);
}
@Test
public void constructMappingPdxSerializer() {
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.getCustomSerializers()).isEmpty();
assertThat(pdxSerializer.getCustomPdxSerializers()).isEmpty();
assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class);
assertThat(pdxSerializer.getMappingContext()).isEqualTo(mockMappingContext);
}
@@ -160,6 +164,7 @@ public class MappingPdxSerializerUnitTests {
public void createMappingPdxSerializer() {
ConversionService mockConversionService = mock(ConversionService.class);
GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class);
MappingPdxSerializer pdxSerializer = MappingPdxSerializer.create(mockMappingContext, mockConversionService);
@@ -170,19 +175,19 @@ 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);
@@ -194,31 +199,31 @@ 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
public void setCustomSerializersWithMappingOfClassTypesToPdxSerializers() {
public void setCustomPdxSerializersWithMappingOfClassTypesToPdxSerializers() {
Map<Class<?>, PdxSerializer> customSerializers =
Map<Class<?>, PdxSerializer> customPdxSerializers =
Collections.singletonMap(Person.class, mock(PdxSerializer.class));
this.pdxSerializer.setCustomSerializers(customSerializers);
this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers);
assertThat(this.pdxSerializer.getCustomSerializers()).isEqualTo(customSerializers);
assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEqualTo(customPdxSerializers);
}
@Test(expected = IllegalArgumentException.class)
public void setCustomSerializersToNull() {
public void setCustomPdxSerializersToNull() {
try {
this.pdxSerializer.setCustomSerializers(null);
this.pdxSerializer.setCustomPdxSerializers(null);
}
catch (IllegalArgumentException expected) {
@@ -230,29 +235,114 @@ public class MappingPdxSerializerUnitTests {
}
@Test
@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<>();
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);
Map<Class<?>, PdxSerializer> customSerializers = Collections.singletonMap(Person.class, mockPdxSerializer);
this.pdxSerializer.setCustomSerializers(customSerializers);
this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Person.class, mockPdxSerializer));
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);
@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 setGemfireInstantiatorsWithEntityInstantiators() {
@@ -309,14 +399,11 @@ public class MappingPdxSerializerUnitTests {
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);
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator));
assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isEqualTo(mockEntityInstantiator);
@@ -329,14 +416,11 @@ public class MappingPdxSerializerUnitTests {
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);
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator));
assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isNotEqualTo(mockEntityInstantiator);
@@ -439,7 +523,7 @@ public class MappingPdxSerializerUnitTests {
@Test
@SuppressWarnings("unchecked")
public void fromDataDeserializesPdxAndMapsToApplicationDomainObject() {
public void fromDataDeserializesPdxBytesAndMapsToApplicationDomainObject() {
Address expectedAddress = new Address();
@@ -447,16 +531,19 @@ public class MappingPdxSerializerUnitTests {
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);
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator));
this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer));
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator));
Object obj = this.pdxSerializer.fromData(Person.class, mockReader);
Object obj = this.pdxSerializer.fromData(Person.class, this.mockReader);
assertThat(obj).isInstanceOf(Person.class);
@@ -467,26 +554,27 @@ public class MappingPdxSerializerUnitTests {
assertThat(jonDoe.getFirstname()).isEqualTo("Jon");
assertThat(jonDoe.getLastname()).isEqualTo("Doe");
verify(mockInstantiator, times(1))
verify(this.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.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 fromDataHandlesExceptionProperly() {
public void fromDataHandlesException() {
when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)))
when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)))
.thenReturn(new Person(null, null, null));
when(mockReader.readField(eq("id"))).thenThrow(newIllegalArgumentException("test"));
when(this.mockReader.readField(eq("id"))).thenThrow(newIllegalArgumentException("test"));
try {
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator));
this.pdxSerializer.fromData(Person.class, mockReader);
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator));
this.pdxSerializer.fromData(Person.class, this.mockReader);
}
catch (MappingException expected) {
@@ -498,10 +586,10 @@ public class MappingPdxSerializerUnitTests {
throw expected;
}
finally {
verify(mockInstantiator, times(1))
verify(this.mockInstantiator, times(1))
.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class));
verify(mockReader, times(1)).readField(eq("id"));
verify(this.mockReader, times(1)).readField(eq("id"));
}
}
@@ -515,20 +603,23 @@ public class MappingPdxSerializerUnitTests {
address.city = "London";
address.zipCode = "01234";
PdxSerializer mockAddressSerializer = mock(PdxSerializer.class);
Person person = new Person(1L, "Oliver", "Gierke");
person.address = address;
when(mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)))
when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)))
.thenReturn(person);
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockInstantiator));
this.pdxSerializer.fromData(Person.class, mockReader);
this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer));
this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator));
this.pdxSerializer.fromData(Person.class, this.mockReader);
GemfirePersistentEntity<?> persistentEntity =
Optional.ofNullable(mappingContext.getPersistentEntity(Person.class)).orElse(null);
Optional.ofNullable(this.mappingContext.getPersistentEntity(Person.class)).orElse(null);
verify(mockInstantiator, times(1))
verify(this.mockInstantiator, times(1))
.createInstance(eq(persistentEntity), any(ParameterValueProvider.class));
verify(mockAddressSerializer, times(1))
@@ -544,23 +635,32 @@ public class MappingPdxSerializerUnitTests {
address.city = "Portland";
address.zipCode = "12345";
PdxSerializer mockAddressSerializer = mock(PdxSerializer.class);
Person jonDoe = new Person(1L, "Jon", "Doe");
jonDoe.address = address;
this.pdxSerializer.setCustomSerializers(Collections.singletonMap(Address.class, mockAddressSerializer));
this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer));
assertThat(this.pdxSerializer.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(expected = MappingException.class)
public void toDataHandlesExceptionProperly() {
public void toDataHandlesException() {
Address address = new Address();
@@ -572,18 +672,19 @@ public class MappingPdxSerializerUnitTests {
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(newIllegalArgumentException("test"));
try {
this.pdxSerializer.setCustomSerializers(Collections.emptyMap());
this.pdxSerializer.toData(jonDoe, mockWriter);
this.pdxSerializer.setCustomPdxSerializers(Collections.emptyMap());
this.pdxSerializer.toData(jonDoe, this.mockWriter);
}
catch (MappingException expected) {
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();
@@ -591,11 +692,20 @@ public class MappingPdxSerializerUnitTests {
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());
}
}
}