diff --git a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java index cdc7c2f3..5d68e1b0 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -15,9 +15,14 @@ */ package org.springframework.data.gemfire.mapping; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; import org.apache.geode.pdx.PdxReader; @@ -47,8 +52,8 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** - * Pivotal GemFire {@link PdxSerializer} implementation using the SDG {@link GemfireMappingContext} to read and write entities - * from/to Pivotal GemFire PDX bytes. + * 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 @@ -61,6 +66,7 @@ import org.springframework.util.ObjectUtils; * @see org.springframework.core.convert.ConversionService * @see org.springframework.data.convert.EntityInstantiator * @see org.springframework.data.convert.EntityInstantiators + * @see org.springframework.data.gemfire.util.Filter * @see org.springframework.data.mapping.PersistentEntity * @see org.springframework.data.mapping.PersistentProperty * @see org.springframework.data.mapping.PersistentPropertyAccessor @@ -70,8 +76,10 @@ import org.springframework.util.ObjectUtils; */ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAware { + protected static final String JAVA_PACKAGE_NAME = "java"; protected static final String COM_GEMSTONE_GEMFIRE_PACKAGE_NAME = "com.gemstone.gemfire"; protected static final String ORG_APACHE_GEODE_PACKAGE_NAME = "org.apache.geode"; + protected static final String ORG_SPRINGFRAMEWORK_PACKAGE_NAME = "org.springframework"; /** * Factory method used to construct a new instance of {@link MappingPdxSerializer} initialized with @@ -131,7 +139,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer */ public static MappingPdxSerializer create(@Nullable GemfireMappingContext mappingContext, - @Nullable ConversionService conversionService) { + @Nullable ConversionService conversionService) { return new MappingPdxSerializer( resolveMappingContext(mappingContext), @@ -190,15 +198,21 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw private final GemfireMappingContext mappingContext; + private final List pdxSerializerResolvers = new CopyOnWriteArrayList<>(); + private final Logger logger = LoggerFactory.getLogger(getClass()); - private Map customPdxSerializers; + private final Map customPdxSerializers = new ConcurrentHashMap<>(); - private Predicate> typeFilters = TypeFilters.EXCLUDE_NULL_TYPES + private Predicate> excludeTypeFilters = TypeFilters.EXCLUDE_NULL_TYPES + .and(TypeFilters.EXCLUDE_JAVA_TYPES) .and(TypeFilters.EXCLUDE_COM_GEMSTONE_GEMFIRE_TYPES) - .and(TypeFilters.EXCLUDE_ORG_APACHE_GEODE_TYPES); + .and(TypeFilters.EXCLUDE_ORG_APACHE_GEODE_TYPES) + .and(TypeFilters.EXCLUDE_ORG_SPRINGFRAMEWORK_TYPES); - // TODO: decide what to do with this; SpELContext is not used + private Predicate> includeTypeFilters = TypeFilters.EXCLUDE_ALL_TYPES; + + // TODO remove? SpELContext is not used private SpELContext spelContext; /** @@ -233,7 +247,13 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw this.mappingContext = mappingContext; this.conversionService = conversionService; this.entityInstantiators = new EntityInstantiators(); - this.customPdxSerializers = Collections.emptyMap(); + + this.pdxSerializerResolvers.addAll(Arrays.asList( + PdxSerializerResolvers.PROPERTY, + PdxSerializerResolvers.PROPERTY_NAME, + PdxSerializerResolvers.PROPERTY_TYPE + )); + this.spelContext = new SpELContext(PdxReaderPropertyAccessor.INSTANCE); } @@ -270,18 +290,15 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @see org.apache.geode.pdx.PdxSerializer * @see java.util.Map */ - public void setCustomPdxSerializers(@NonNull Map customPdxSerializers) { - - Assert.notNull(customPdxSerializers, "Custom PdxSerializers must not be null"); - - this.customPdxSerializers = customPdxSerializers; + public void setCustomPdxSerializers(Map customPdxSerializers) { + Optional.ofNullable(customPdxSerializers).ifPresent(this.customPdxSerializers::putAll); } /** - * @deprecated please use ({@link #setCustomPdxSerializers(Map)} instead. + * @deprecated use ({@link #setCustomPdxSerializers(Map)}. */ @Deprecated - public void setCustomSerializers(Map, PdxSerializer> customSerializers) { + public final void setCustomSerializers(Map, PdxSerializer> customSerializers) { setCustomPdxSerializers(customSerializers); } @@ -301,56 +318,29 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw } /** - * @deprecated please use {@link #getCustomPdxSerializers()} instead. + * @deprecated use {@link #getCustomPdxSerializers()}. */ @Deprecated @SuppressWarnings("unchecked") - protected Map, PdxSerializer> getCustomSerializers() { + protected final Map, PdxSerializer> getCustomSerializers() { return (Map, PdxSerializer>) getCustomPdxSerializers(); } /** - * Returns a custom PDX serializer for the given {@link PersistentProperty entity persistent property}. - * - * @param property {@link PersistentProperty} of the entity used to lookup the custom PDX serializer. - * @return a custom {@link PdxSerializer} for the given entity {@link PersistentProperty}, - * or {@literal null} if no custom {@link PdxSerializer} could be found. - * @see org.apache.geode.pdx.PdxSerializer - */ - @Nullable - protected PdxSerializer getCustomPdxSerializer(@NonNull PersistentProperty property) { - - Map customPdxSerializers = getCustomPdxSerializers(); - - PdxSerializer customPdxSerializer = customPdxSerializers.get(property); - - customPdxSerializer = customPdxSerializer != null ? customPdxSerializer - : customPdxSerializers.get(toFullyQualifiedPropertyName(property)); - - customPdxSerializer = customPdxSerializer != null ? customPdxSerializer - : customPdxSerializers.get(property.getType()); - - return customPdxSerializer; - } - - /** - * Converts the entity {@link PersistentProperty} to a {@link String fully-qualified property name}. - * - * @param property {@link PersistentProperty} of the entity. - * @return the {@link String fully-qualified property name of the entity {@link PersistentProperty}. - * @see org.springframework.data.mapping.PersistentProperty - */ - @NonNull - String toFullyQualifiedPropertyName(@NonNull PersistentProperty property) { - return property.getOwner().getType().getName().concat(".").concat(property.getName()); - } - - /** - * @deprecated please use {@link #getCustomPdxSerializer(PersistentProperty)} instead. + * @deprecated use {@link #resolveCustomPdxSerializer(PersistentProperty)}. */ @Nullable @Deprecated - protected PdxSerializer getCustomSerializer(Class type) { + protected final PdxSerializer getCustomPdxSerializer(@NonNull PersistentProperty property) { + return resolveCustomPdxSerializer(property); + } + + /** + * @deprecated use {@link #resolveCustomPdxSerializer(PersistentProperty)}. + */ + @Nullable + @Deprecated + protected final PdxSerializer getCustomSerializer(Class type) { return getCustomPdxSerializers().get(type); } @@ -362,13 +352,21 @@ 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(@NonNull EntityInstantiators entityInstantiators) { + public void setEntityInstantiators(@NonNull EntityInstantiators entityInstantiators) { Assert.notNull(entityInstantiators, "EntityInstantiators must not be null"); this.entityInstantiators = entityInstantiators; } + /** + * @deprecated use {@link #setEntityInstantiators(EntityInstantiators)}. + */ + @Deprecated + public final void setGemfireInstantiators(@NonNull EntityInstantiators entityInstantiators) { + setEntityInstantiators(entityInstantiators); + } + /** * Configures the {@link EntityInstantiator EntityInstantiators} used to create the instances * read by this {@link PdxSerializer}. @@ -378,31 +376,42 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @see org.springframework.data.convert.EntityInstantiator * @see java.util.Map */ - public void setGemfireInstantiators(@NonNull Map, EntityInstantiator> gemfireInstantiators) { - setGemfireInstantiators(new EntityInstantiators(gemfireInstantiators)); + public void setEntityInstantiators(@NonNull Map, EntityInstantiator> gemfireInstantiators) { + setEntityInstantiators(new EntityInstantiators(gemfireInstantiators)); } /** - * Returns the configured {@link EntityInstantiators} handling instantiation for Pivotal GemFire persistent entities. + * @deprecated use {@link #setEntityInstantiators(Map)}. + */ + @Deprecated + public final void setGemfireInstantiators(@NonNull Map, EntityInstantiator> gemfireInstantiators) { + setEntityInstantiators(new EntityInstantiators(gemfireInstantiators)); + } + + /** + * Returns the configured {@link EntityInstantiators} handling instantiation for GemFire persistent entities. * - * @return the configured {@link EntityInstantiators} handling instantiation for Pivotal GemFire persistent entities. + * @return the configured {@link EntityInstantiators} handling instantiation for GemFire persistent entities. * @see org.springframework.data.convert.EntityInstantiators */ - protected EntityInstantiators getGemfireInstantiators() { + protected EntityInstantiators getEntityInstantiators() { 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). - * - * @param entity the PersistentEntity object used to lookup the custom EntityInstantiator. - * @return an EntityInstantiator for the given PersistentEntity. - * @see org.springframework.data.convert.EntityInstantiator - * @see org.springframework.data.mapping.PersistentEntity + * @deprecated use {@link #getEntityInstantiators()}. */ - protected EntityInstantiator getInstantiatorFor(PersistentEntity entity) { - return getGemfireInstantiators().getInstantiatorFor(entity); + @Deprecated + protected final EntityInstantiators getGemfireInstantiators() { + return this.entityInstantiators; + } + + /** + * @deprecated use {@link #resolveEntityInstantiator(PersistentEntity)} + */ + @Deprecated + protected final EntityInstantiator getInstantiatorFor(PersistentEntity entity) { + return resolveEntityInstantiator(entity); } /** @@ -419,7 +428,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw /** * Returns a reference to the configured {@link GemfireMappingContext mapping context} used to handling mapping - * logic between Pivotal GemFire persistent entities and application domain object {@link Class types}. + * 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 @@ -454,19 +463,41 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw } /** - * Sets the {@link Predicate type filters} used to filter {@link Class types} serializable + * Sets the {@link Predicate type filters} used to exclude (a.k.a. filter) {@link Class types} serializable * by this {@link MappingPdxSerializer PDX serializer}. * - * This operation is null-safe and rather than overriding the existing {@link Predicate type filters}, - * this set operation combines the given {@link Predicate type filters} with - * the exiting {@link Predicate type filters} joined by {@literal and}. + * This operation is null-safe and rather than overriding the existing {@link Predicate excluded type filters}, + * this set operation combines the given {@link Predicate exclude type filters} with + * the exiting {@link Predicate excluded type filters} joined by {@literal and}. * - * @param typeFilters {@link Predicate type filters} used to to filter {@link Class type} serializable + * @param excludeTypeFilters {@link Predicate type filters} used to exclude/filter {@link Class types} serializable * by this {@link MappingPdxSerializer PDX serializer}. * @see java.util.function.Predicate */ - public void setTypeFilters(@Nullable Predicate> typeFilters) { - this.typeFilters = typeFilters != null ? this.typeFilters.and(typeFilters) : this.typeFilters; + public void setExcludeTypeFilters(@Nullable Predicate> excludeTypeFilters) { + + this.excludeTypeFilters = excludeTypeFilters != null + ? this.excludeTypeFilters.and(excludeTypeFilters) + : this.excludeTypeFilters; + } + + /** + * Sets the {@link Predicate type filters} used to include {@link Class types} serializable + * by this {@link MappingPdxSerializer PDX serializer}. + * + * This operation is null-safe and rather than overriding the existing {@link Predicate included type filters}, + * this set operation combines the given {@link Predicate include type filters} with + * the exiting {@link Predicate included type filters} joined by {@literal or}. + * + * @param includeTypeFilters {@link Predicate type filters} used to include {@link Class types} serializable + * by this {@link MappingPdxSerializer PDX serializer}. + * @see java.util.function.Predicate + */ + public void setIncludeTypeFilters(@Nullable Predicate> includeTypeFilters) { + + this.includeTypeFilters = includeTypeFilters != null + ? this.includeTypeFilters.or(includeTypeFilters) + : this.includeTypeFilters; } /** @@ -477,7 +508,21 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @see java.util.function.Predicate */ protected Predicate> getTypeFilters() { - return this.typeFilters; + return this.excludeTypeFilters.or(TypeFilters.EXCLUDE_NULL_TYPES.and(this.includeTypeFilters)); + } + + /** + * Registers the given {@link PdxSerializerResolver}, which will be used to resolve a custom {@link PdxSerializer} + * for a entity property. + * + * The strategy, or criteria used to resolve the custom {@link PdxSerializer} is up to the individual resolve + * and can be based on things like the property type, or fully-qualified property name, etc. + * + * @param pdxSerializerResolver {@link PdxSerializerResolver} used to resolve a custom {@link PdxSerializer} + * for a entity property. + */ + public void register(PdxSerializerResolver pdxSerializerResolver) { + Optional.ofNullable(pdxSerializerResolver).ifPresent(it -> this.pdxSerializerResolvers.add(0, it)); } @Override @@ -499,7 +544,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw GemfirePersistentEntity entity = getPersistentEntity(type); - Object instance = getInstantiatorFor(entity) + Object instance = resolveEntityInstantiator(entity) .createInstance(entity, new PersistentEntityParameterValueProvider<>(entity, new GemfirePropertyValueProvider(reader), null)); @@ -510,15 +555,15 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw if (isWritable(entity, persistentProperty)) { - PdxSerializer customPdxSerializer = getCustomPdxSerializer(persistentProperty); + PdxSerializer customPdxSerializer = resolveCustomPdxSerializer(persistentProperty); Object value = null; try { if (getLogger().isDebugEnabled()) { getLogger().debug(String.format("Setting property [%1$s] for entity [%2$s] of type [%3$s] from PDX%4$s", - persistentProperty.getName(), instance, type, (customPdxSerializer != null ? - String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : ""))); + persistentProperty.getName(), instance, type, (customPdxSerializer != null + ? String.format(" using custom PdxSerializer [%s]", customPdxSerializer) : ""))); } value = (customPdxSerializer != null @@ -532,10 +577,10 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw propertyAccessor.setProperty(persistentProperty, value); } catch (Exception cause) { - throw new MappingException(String.format( - "While setting value [%1$s] of property [%2$s] for entity of type [%3$s] from PDX%4$s", - value, persistentProperty.getName(), type, (customPdxSerializer != null ? - String.format(" using custom PdxSerializer [%1$s]", customPdxSerializer) : "")), cause); + throw new MappingException( + String.format("While setting value [%1$s] of property [%2$s] for entity of type [%3$s] from PDX%4$s", + value, persistentProperty.getName(), type, (customPdxSerializer != null + ? String.format(" using custom PdxSerializer [%s]", customPdxSerializer) : "")), cause); } } }); @@ -595,7 +640,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw if (isReadable(persistentProperty)) { - PdxSerializer customPdxSerializer = getCustomPdxSerializer(persistentProperty); + PdxSerializer customPdxSerializer = resolveCustomPdxSerializer(persistentProperty); Object propertyValue = null; @@ -624,7 +669,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw entity.getType().getName(), persistentProperty.getName(), propertyValue, ObjectUtils.nullSafeClassName(propertyValue), (customPdxSerializer != null ? String.format(" using custom PdxSerializer [%1$s].", - customPdxSerializer.getClass().getName()) : "")), cause); + customPdxSerializer.getClass().getName()) : "")), cause); } } }); @@ -655,6 +700,39 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return !persistentProperty.isTransient(); } + /** + * 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 resolveCustomPdxSerializer(@NonNull PersistentProperty property) { + + Map customPdxSerializers = getCustomPdxSerializers(); + + return this.pdxSerializerResolvers.stream() + .map(it -> it.resolve(customPdxSerializers, property)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + /** + * Looks up and returns an EntityInstantiator to construct and initialize an instance of the object defined + * by the given PersistentEntity (meta-data). + * + * @param entity the PersistentEntity object used to lookup the custom EntityInstantiator. + * @return an EntityInstantiator for the given PersistentEntity. + * @see org.springframework.data.convert.EntityInstantiator + * @see org.springframework.data.mapping.PersistentEntity + */ + protected EntityInstantiator resolveEntityInstantiator(PersistentEntity entity) { + return getEntityInstantiators().getInstantiatorFor(entity); + } + /** * Resolves the {@link Class type} of the given {@link Object}. * @@ -668,15 +746,71 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return obj != null ? obj.getClass() : null; } - public enum TypeFilters implements Filter> { + @FunctionalInterface + public interface PdxSerializerResolver { - EXCLUDE_COM_GEMSTONE_GEMFIRE_TYPES { + @Nullable + PdxSerializer resolve(@NonNull Map customPdxSerializers, + @NonNull PersistentProperty property); + + } + + public enum PdxSerializerResolvers implements PdxSerializerResolver { + + PROPERTY { @Override - public boolean accept(@Nullable Class obj) { + public PdxSerializer resolve(Map customPdxSerializers, PersistentProperty property) { + return customPdxSerializers.get(property); + } + }, - return Optional.ofNullable(obj) - .filter(type -> !type.getPackage().getName().startsWith(COM_GEMSTONE_GEMFIRE_PACKAGE_NAME)) + PROPERTY_NAME { + + @Override + public PdxSerializer resolve(Map customPdxSerializers, PersistentProperty property) { + return customPdxSerializers.get(toFullyQualifiedPropertyName(property)); + } + }, + + PROPERTY_TYPE { + + @Override + public PdxSerializer resolve(Map customPdxSerializers, PersistentProperty property) { + return customPdxSerializers.get(property.getType()); + } + }; + + /** + * 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 + static String toFullyQualifiedPropertyName(@NonNull PersistentProperty property) { + return property.getOwner().getType().getName().concat(".").concat(property.getName()); + } + } + + public enum TypeFilters implements Filter> { + + EXCLUDE_ALL_TYPES { + + @Override + public boolean accept(@Nullable Class type) { + return false; + } + }, + + EXCLUDE_JAVA_TYPES { + + @Override + public boolean accept(@Nullable Class type) { + + return Optional.ofNullable(type) + .filter(it -> !it.getPackage().getName().startsWith(JAVA_PACKAGE_NAME)) .isPresent(); } }, @@ -689,6 +823,17 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw } }, + EXCLUDE_COM_GEMSTONE_GEMFIRE_TYPES { + + @Override + public boolean accept(@Nullable Class type) { + + return Optional.ofNullable(type) + .filter(it -> !it.getPackage().getName().startsWith(COM_GEMSTONE_GEMFIRE_PACKAGE_NAME)) + .isPresent(); + } + }, + EXCLUDE_ORG_APACHE_GEODE_TYPES { @Override @@ -699,5 +844,16 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw .isPresent(); } }, + + EXCLUDE_ORG_SPRINGFRAMEWORK_TYPES { + + @Override + public boolean accept(@Nullable Class type) { + + return Optional.ofNullable(type) + .filter(it -> !it.getPackage().getName().startsWith(ORG_SPRINGFRAMEWORK_PACKAGE_NAME)) + .isPresent(); + } + }, } } diff --git a/src/test/java/com/gemstone/gemfire/TestGemStoneGemFireType.java b/src/test/java/com/gemstone/gemfire/TestGemStoneGemFireType.java new file mode 100644 index 00000000..04bae604 --- /dev/null +++ b/src/test/java/com/gemstone/gemfire/TestGemStoneGemFireType.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gemstone.gemfire; + +import org.springframework.data.gemfire.mapping.MappingPdxSerializerUnitTests; + +/** + * The {@link TestGemStoneGemFireType} class is a test {@link Class type} used by + * the {@link MappingPdxSerializerUnitTests}. + * + * @author John Blum + * @see org.springframework.data.gemfire.mapping.MappingPdxSerializerUnitTests + * @since 1.0.0 + */ +public class TestGemStoneGemFireType { + +} diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java index b99f86aa..fa3baf62 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,11 +34,14 @@ import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; +import java.security.Principal; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import com.gemstone.gemfire.TestGemStoneGemFireType; + import org.apache.geode.pdx.PdxReader; import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; @@ -47,15 +50,22 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.core.SpringVersion; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.core.type.ClassMetadata; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.convert.EntityInstantiators; +import org.springframework.data.domain.Page; +import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.repository.sample.Account; import org.springframework.data.gemfire.repository.sample.Address; +import org.springframework.data.gemfire.repository.sample.Algorithm; +import org.springframework.data.gemfire.repository.sample.Animal; import org.springframework.data.gemfire.repository.sample.Customer; import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.gemfire.repository.sample.Plant; import org.springframework.data.gemfire.repository.sample.Programmer; import org.springframework.data.gemfire.repository.sample.RootUser; import org.springframework.data.gemfire.repository.sample.User; @@ -73,11 +83,10 @@ import org.springframework.data.mapping.model.ParameterValueProvider; * @author John Blum * @see org.junit.Rule * @see org.junit.Test - * @see org.junit.rules.ExpectedException * @see org.junit.runner.RunWith * @see org.mockito.Mock * @see org.mockito.Mockito - * @see org.mockito.runners.MockitoJUnitRunner + * @see org.mockito.junit.MockitoJUnitRunner * @see org.springframework.core.convert.ConversionService * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer * @see org.apache.geode.pdx.PdxReader @@ -87,20 +96,20 @@ import org.springframework.data.mapping.model.ParameterValueProvider; @RunWith(MockitoJUnitRunner.class) public class MappingPdxSerializerUnitTests { - ConversionService conversionService; + private ConversionService conversionService; @Mock - EntityInstantiator mockInstantiator; + private EntityInstantiator mockEntityInstantiator; - GemfireMappingContext mappingContext; + private GemfireMappingContext mappingContext; - MappingPdxSerializer pdxSerializer; + private MappingPdxSerializer pdxSerializer; @Mock - PdxReader mockReader; + private PdxReader mockReader; @Mock - PdxWriter mockWriter; + private PdxWriter mockWriter; @Before public void setUp() { @@ -110,8 +119,8 @@ public class MappingPdxSerializerUnitTests { this.pdxSerializer = spy(new MappingPdxSerializer(this.mappingContext, this.conversionService)); } - private String toFullyQualifiedPropertyName(PersistentProperty persistentProperty) { - return this.pdxSerializer.toFullyQualifiedPropertyName(persistentProperty); + private String toFullyQualifiedPropertyName(PersistentProperty property) { + return MappingPdxSerializer.PdxSerializerResolvers.toFullyQualifiedPropertyName(property); } @Test @@ -121,7 +130,7 @@ public class MappingPdxSerializerUnitTests { assertThat(pdxSerializer.getConversionService()).isInstanceOf(DefaultConversionService.class); assertThat(pdxSerializer.getCustomPdxSerializers()).isEmpty(); - assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(pdxSerializer.getEntityInstantiators()).isInstanceOf(EntityInstantiators.class); assertThat(pdxSerializer.getMappingContext()).isInstanceOf(GemfireMappingContext.class); } @@ -136,7 +145,7 @@ public class MappingPdxSerializerUnitTests { assertThat(pdxSerializer.getConversionService()).isEqualTo(mockConversionService); assertThat(pdxSerializer.getCustomPdxSerializers()).isEmpty(); - assertThat(pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(pdxSerializer.getEntityInstantiators()).isInstanceOf(EntityInstantiators.class); assertThat(pdxSerializer.getMappingContext()).isEqualTo(mockMappingContext); } @@ -229,145 +238,31 @@ public class MappingPdxSerializerUnitTests { assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEqualTo(customPdxSerializers); } - @Test(expected = IllegalArgumentException.class) - public void setCustomPdxSerializersToNull() { - - try { - this.pdxSerializer.setCustomPdxSerializers(null); - } - catch (IllegalArgumentException expected) { - - assertThat(expected).hasMessage("Custom PdxSerializers must not be null"); - assertThat(expected).hasNoCause(); - - throw expected; - } - } - @Test - @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForProperty() { - - PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); - PdxSerializer mockPropertySerializer = mock(PdxSerializer.class); - PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); - - PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); - - PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); - - this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() - .put(addressProperty, mockPropertySerializer) - .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) - .put(Address.class, mockTypedSerializer) - .build()); - - assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockPropertySerializer); - } - - @Test - @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForPropertyName() { - - PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); - PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); - - PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); - - PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); - - this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() - .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) - .put(Address.class, mockTypedSerializer) - .build()); - - assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockNamedSerializer); - } - - @Test - @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForPropertyType() { - - PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); - PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); - - Map customPdxSerializers = new HashMap<>(); - - PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); - - PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); - - customPdxSerializers.put("example.Type.address", mockNamedSerializer); - customPdxSerializers.put(Address.class, mockTypedSerializer); - - this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers); - - assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isEqualTo(mockTypedSerializer); - } - - @Test - @SuppressWarnings("all") - public void getCustomPdxSerializerForUnmappedPersistentPropertyReturnsNull() { - - PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); - - PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + public void setCustomPdxSerializersIsNullSafe() { assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); - assertThat(this.pdxSerializer.getCustomPdxSerializer(addressProperty)).isNull(); - } - @Test - @SuppressWarnings("deprecation") - public void getCustomSerializerForMappedTypeReturnsPdxSerializer() { + this.pdxSerializer.setCustomPdxSerializers(null); - PdxSerializer mockPdxSerializer = mock(PdxSerializer.class); - - this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Person.class, mockPdxSerializer)); - - assertThat(this.pdxSerializer.getCustomSerializer(Person.class)).isEqualTo(mockPdxSerializer); - } - - @Test - @SuppressWarnings("deprecation") - public void getCustomSerializerForUnmappedTypeReturnsNull() { assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); - assertThat(this.pdxSerializer.getCustomSerializer(Address.class)).isNull(); } @Test - public void toFullyQualifiedPropertyName() { - - PersistentEntity mockEntity = mock(PersistentEntity.class); - PersistentProperty mockProperty = mock(PersistentProperty.class); - - when(mockProperty.getName()).thenReturn("mockProperty"); - when(mockProperty.getOwner()).thenReturn(mockEntity); - when(mockEntity.getType()).thenReturn(Person.class); - - assertThat(this.pdxSerializer.toFullyQualifiedPropertyName(mockProperty)) - .isEqualTo(Person.class.getName().concat(".mockProperty")); - - verify(mockEntity, times(1)).getType(); - verify(mockProperty, times(1)).getName(); - verify(mockProperty, times(1)).getOwner(); - } - - @Test - public void setGemfireInstantiatorsWithEntityInstantiators() { + public void setEntityInstantiatorsWithNonNullEntityInstantiators() { EntityInstantiators mockEntityInstantiators = mock(EntityInstantiators.class); - this.pdxSerializer.setGemfireInstantiators(mockEntityInstantiators); + this.pdxSerializer.setEntityInstantiators(mockEntityInstantiators); - assertThat(this.pdxSerializer.getGemfireInstantiators()).isSameAs(mockEntityInstantiators); + assertThat(this.pdxSerializer.getEntityInstantiators()).isSameAs(mockEntityInstantiators); } @Test(expected = IllegalArgumentException.class) - public void setGemfireInstantiatorsWithNullEntityInstantiators() { + public void setEntityInstantiatorsWithNullEntityInstantiators() { try { - this.pdxSerializer.setGemfireInstantiators((EntityInstantiators) null); + this.pdxSerializer.setEntityInstantiators((EntityInstantiators) null); } catch (IllegalArgumentException expected) { @@ -379,21 +274,21 @@ public class MappingPdxSerializerUnitTests { } @Test - public void setGemfireInstantiatorsWithMappingOfClassTypesToEntityInstantiators() { + public void setEntityInstantiatorsWithNonNullMappingOfClassTypesToEntityInstantiators() { Map, EntityInstantiator> entityInstantiators = Collections.singletonMap(Person.class, mock(EntityInstantiator.class)); - this.pdxSerializer.setGemfireInstantiators(entityInstantiators); + this.pdxSerializer.setEntityInstantiators(entityInstantiators); - assertThat(this.pdxSerializer.getGemfireInstantiators()).isInstanceOf(EntityInstantiators.class); + assertThat(this.pdxSerializer.getEntityInstantiators()).isInstanceOf(EntityInstantiators.class); } @Test(expected = IllegalArgumentException.class) - public void setGemfireInstantiatorsWithNullMap() { + public void setEntityInstantiatorsWithNullMap() { try { - this.pdxSerializer.setGemfireInstantiators((Map, EntityInstantiator>) null); + this.pdxSerializer.setEntityInstantiators((Map, EntityInstantiator>) null); } catch (IllegalArgumentException expected) { @@ -404,40 +299,6 @@ public class MappingPdxSerializerUnitTests { } } - @Test - public void getInstantiatorForManagedPersistentEntityWithInstantiator() { - - EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); - - PersistentEntity mockEntity = mock(PersistentEntity.class); - - when(mockEntity.getType()).thenReturn(Person.class); - - this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator)); - - assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isEqualTo(mockEntityInstantiator); - - verify(mockEntity, atLeast(1)).getType(); - verifyZeroInteractions(mockEntityInstantiator); - } - - @Test - public void getInstantiatorForNonManagedPersistentEntityWithNoInstantiator() { - - EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); - - PersistentEntity mockEntity = mock(PersistentEntity.class); - - when(mockEntity.getType()).thenReturn(Address.class); - - this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator)); - - assertThat(this.pdxSerializer.getInstantiatorFor(mockEntity)).isNotEqualTo(mockEntityInstantiator); - - verify(mockEntity, atLeast(1)).getType(); - verifyZeroInteractions(mockEntityInstantiator); - } - @Test public void isReadableWithNonTransientPropertyReturnsTrue() { @@ -531,9 +392,144 @@ public class MappingPdxSerializerUnitTests { verify(mockProperty, times(1)).isTransient(); } + @Test + @SuppressWarnings("all") + public void resolveCustomPdxSerializerReturnsNull() { + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); + assertThat(this.pdxSerializer.resolveCustomPdxSerializer(addressProperty)).isNull(); + } + + @Test + @SuppressWarnings("all") + public void resolveCustomPdxSerializerReturnsPdxSerializerForProperty() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockPropertySerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() + .put(addressProperty, mockPropertySerializer) + .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) + .put(Address.class, mockTypedSerializer) + .build()); + + assertThat(this.pdxSerializer.resolveCustomPdxSerializer(addressProperty)).isEqualTo(mockPropertySerializer); + } + + @Test + @SuppressWarnings("all") + public void resolveCustomPdxSerializerReturnsPdxSerializerForPropertyName() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + this.pdxSerializer.setCustomPdxSerializers(MapBuilder.newMapBuilder() + .put(toFullyQualifiedPropertyName(addressProperty), mockNamedSerializer) + .put(Address.class, mockTypedSerializer) + .build()); + + assertThat(this.pdxSerializer.resolveCustomPdxSerializer(addressProperty)).isEqualTo(mockNamedSerializer); + } + + @Test + @SuppressWarnings("all") + public void resolveCustomPdxSerializerReturnsPdxSerializerForPropertyType() { + + PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); + PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); + + Map customPdxSerializers = new HashMap<>(); + + PersistentEntity personEntity = this.mappingContext.getPersistentEntity(Person.class); + + PersistentProperty addressProperty = personEntity.getPersistentProperty("address"); + + customPdxSerializers.put("example.Type.address", mockNamedSerializer); + customPdxSerializers.put(Address.class, mockTypedSerializer); + + this.pdxSerializer.setCustomPdxSerializers(customPdxSerializers); + + assertThat(this.pdxSerializer.resolveCustomPdxSerializer(addressProperty)).isEqualTo(mockTypedSerializer); + } + + @Test + public void resolveEntityInstantiatorForManagedPersistentEntityWithEntityInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Person.class); + + this.pdxSerializer.setEntityInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator)); + + assertThat(this.pdxSerializer.resolveEntityInstantiator(mockEntity)).isEqualTo(mockEntityInstantiator); + + verify(mockEntity, atLeast(1)).getType(); + verifyZeroInteractions(mockEntityInstantiator); + } + + @Test + public void resolveEntityInstantiatorForNonManagedPersistentEntityWithNoEntityInstantiator() { + + EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); + + PersistentEntity mockEntity = mock(PersistentEntity.class); + + when(mockEntity.getType()).thenReturn(Address.class); + + this.pdxSerializer.setEntityInstantiators(Collections.singletonMap(Person.class, mockEntityInstantiator)); + + assertThat(this.pdxSerializer.resolveEntityInstantiator(mockEntity)).isNotEqualTo(mockEntityInstantiator); + + verify(mockEntity, atLeast(1)).getType(); + verifyZeroInteractions(mockEntityInstantiator); + } + + @Test + public void resolveTypeWithNonNullType() { + assertThat(this.pdxSerializer.resolveType("test")).isEqualTo(String.class); + } + + @Test + public void resolveTypeWithNullType() { + assertThat(this.pdxSerializer.resolveType(null)).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(MappingPdxSerializer.PdxSerializerResolvers.toFullyQualifiedPropertyName(mockProperty)) + .isEqualTo(Person.class.getName().concat(".mockProperty")); + + verify(mockEntity, times(1)).getType(); + verify(mockProperty, times(1)).getName(); + verify(mockProperty, times(1)).getOwner(); + } + @Test @SuppressWarnings("unchecked") - public void fromDataDeserializesPdxBytesAndMapsToApplicationDomainObject() { + public void fromDataDeserializesPdxBytesAndMapsToEntity() { Address expectedAddress = new Address(); @@ -543,7 +539,7 @@ public class MappingPdxSerializerUnitTests { PdxSerializer mockAddressSerializer = mock(PdxSerializer.class); - when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + when(this.mockEntityInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) .thenReturn(new Person(null, null, null)); when(this.mockReader.readField(eq("id"))).thenReturn(1L); when(this.mockReader.readField(eq("firstname"))).thenReturn("Jon"); @@ -551,7 +547,9 @@ public class MappingPdxSerializerUnitTests { when(mockAddressSerializer.fromData(eq(Address.class), eq(this.mockReader))).thenReturn(expectedAddress); this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); - this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.setEntityInstantiators(Collections.singletonMap(Person.class, this.mockEntityInstantiator)); + this.pdxSerializer.setIncludeTypeFilters(type -> Address.class.isAssignableFrom(type)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.isAssignableFrom(type)); Object obj = this.pdxSerializer.fromData(Person.class, this.mockReader); @@ -564,7 +562,7 @@ public class MappingPdxSerializerUnitTests { assertThat(jonDoe.getFirstname()).isEqualTo("Jon"); assertThat(jonDoe.getLastname()).isEqualTo("Doe"); - verify(this.mockInstantiator, times(1)) + verify(this.mockEntityInstantiator, times(1)) .createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); verify(this.mockReader, times(1)).readField(eq("id")); verify(this.mockReader, times(1)).readField(eq("firstname")); @@ -577,13 +575,14 @@ public class MappingPdxSerializerUnitTests { @SuppressWarnings("unchecked") public void fromDataHandlesException() { - when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + when(this.mockEntityInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) .thenReturn(new Person(null, null, null)); when(this.mockReader.readField(eq("id"))).thenThrow(newIllegalArgumentException("test")); try { - this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.setEntityInstantiators(Collections.singletonMap(Person.class, this.mockEntityInstantiator)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.fromData(Person.class, this.mockReader); } catch (MappingException expected) { @@ -596,7 +595,7 @@ public class MappingPdxSerializerUnitTests { throw expected; } finally { - verify(this.mockInstantiator, times(1)) + verify(this.mockEntityInstantiator, times(1)) .createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class)); verify(this.mockReader, times(1)).readField(eq("id")); @@ -605,7 +604,7 @@ public class MappingPdxSerializerUnitTests { @Test @SuppressWarnings("unchecked") - public void fromDataUsesRegisteredInstantiator() { + public void fromDataUsesRegisteredEntityInstantiator() { Address address = new Address(); @@ -619,17 +618,18 @@ public class MappingPdxSerializerUnitTests { person.address = address; - when(this.mockInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) + when(this.mockEntityInstantiator.createInstance(any(GemfirePersistentEntity.class), any(ParameterValueProvider.class))) .thenReturn(person); this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); - this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.setEntityInstantiators(Collections.singletonMap(Person.class, this.mockEntityInstantiator)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.fromData(Person.class, this.mockReader); GemfirePersistentEntity persistentEntity = Optional.ofNullable(this.mappingContext.getPersistentEntity(Person.class)).orElse(null); - verify(this.mockInstantiator, times(1)) + verify(this.mockEntityInstantiator, times(1)) .createInstance(eq(persistentEntity), any(ParameterValueProvider.class)); verify(mockAddressSerializer, times(1)) @@ -637,48 +637,82 @@ public class MappingPdxSerializerUnitTests { } @Test - public void fromDataWithTypeFilterAcceptsApplicationDomainTypes() { + public void fromDataWithTypeFilterAcceptsDeclaredEntityTypes() { - this.pdxSerializer.setTypeFilters(type -> User.class.getPackage().equals(type.getPackage())); + this.pdxSerializer.setIncludeTypeFilters(type -> User.class.getPackage().equals(type.getPackage())); doReturn("test").when(this.pdxSerializer).doFromData(any(Class.class), any(PdxReader.class)); assertThat(this.pdxSerializer.fromData(Account.class, this.mockReader)).isEqualTo("test"); + assertThat(this.pdxSerializer.fromData(Algorithm.class, this.mockReader)).isEqualTo("test"); + assertThat(this.pdxSerializer.fromData(Animal.class, this.mockReader)).isEqualTo("test"); assertThat(this.pdxSerializer.fromData(Customer.class, this.mockReader)).isEqualTo("test"); + assertThat(this.pdxSerializer.fromData(Plant.class, this.mockReader)).isEqualTo("test"); assertThat(this.pdxSerializer.fromData(Programmer.class, this.mockReader)).isEqualTo("test"); assertThat(this.pdxSerializer.fromData(User.class, this.mockReader)).isEqualTo("test"); assertThat(this.pdxSerializer.fromData(org.springframework.data.gemfire.test.model.Person.class, this.mockReader)).isNull(); assertThat(this.pdxSerializer.fromData(null, this.mockReader)).isNull(); verify(this.pdxSerializer, times(1)).doFromData(eq(Account.class), eq(this.mockReader)); + verify(this.pdxSerializer, times(1)).doFromData(eq(Algorithm.class), eq(this.mockReader)); + verify(this.pdxSerializer, times(1)).doFromData(eq(Animal.class), eq(this.mockReader)); verify(this.pdxSerializer, times(1)).doFromData(eq(Customer.class), eq(this.mockReader)); + verify(this.pdxSerializer, times(1)).doFromData(eq(Plant.class), eq(this.mockReader)); verify(this.pdxSerializer, times(1)).doFromData(eq(Programmer.class), eq(this.mockReader)); verify(this.pdxSerializer, times(1)).doFromData(eq(User.class), eq(this.mockReader)); verify(this.pdxSerializer, never()).doFromData(isNull(), eq(this.mockReader)); - verify(this.pdxSerializer, never()) - .doFromData(eq(org.springframework.data.gemfire.test.model.Person.class), eq(this.mockReader)); + verify(this.pdxSerializer, never()).doFromData(eq(org.springframework.data.gemfire.test.model.Person.class), + eq(this.mockReader)); } @Test - public void fromDataWithTypeFilterFiltersApacheGeodeTypeReturnsNull() { + public void fromDataWithTypeFilterAcceptsIncludedTypesOverridingExcludedTypes() { + + this.pdxSerializer.setIncludeTypeFilters(type -> type.getPackage().getName().startsWith("java.security")); + + doReturn("test").when(this.pdxSerializer).doFromData(any(Class.class), any(PdxReader.class)); + + assertThat(this.pdxSerializer.fromData(Principal.class, this.mockReader)).isEqualTo("test"); + + verify(this.pdxSerializer, times(1)) + .doFromData(eq(Principal.class), eq(this.mockReader)); + } + + @Test + public void fromDataWithTypeFilterFiltersApacheGeodeTypesReturnsNull() { assertThat(this.pdxSerializer.fromData(org.apache.geode.cache.EntryEvent.class, this.mockReader)).isNull(); } @Test - public void fromDataWithTypeFilterFiltersApplicationDomainTypeReturnsNull() { - - this.pdxSerializer.setTypeFilters(type -> !ApplicationDomainType.class.equals(type)); - - assertThat(this.pdxSerializer.fromData(ApplicationDomainType.class, this.mockReader)).isNull(); + public void fromDataWithTypeFilterFiltersGemStoneTypesReturnsNull() { + assertThat(this.pdxSerializer.fromData(TestGemStoneGemFireType.class, this.mockReader)).isNull(); } @Test - public void fromDataWithTypeFilterFiltersNullTypeReturnsNull() { + public void fromDataWithTypeFilterFiltersJavaTypesReturnsNull() { + assertThat(this.pdxSerializer.fromData(java.security.Principal.class, this.mockReader)).isNull(); + } + + @Test + public void fromDataWithTypeFilterFiltersNullTypesReturnsNull() { assertThat(this.pdxSerializer.fromData(null, this.mockReader)).isNull(); } @Test - public void toDataSerializesApplicationDomainObjectToPdxBytes() { + public void fromDataWithTypeFilterFiltersSpringFrameworkTypesReturnsNull() { + + assertThat(this.pdxSerializer.fromData(ClassMetadata.class, this.mockReader)).isNull(); + assertThat(this.pdxSerializer.fromData(Page.class, this.mockReader)).isNull(); + assertThat(this.pdxSerializer.fromData(GemfireTemplate.class, this.mockReader)).isNull(); + } + + @Test + public void fromDataWithTypeFilterFiltersUndeclaredEntityTypesReturnsNull() { + assertThat(this.pdxSerializer.fromData(ApplicationDomainType.class, this.mockReader)).isNull(); + } + + @Test + public void toDataSerializesEntityToPdxBytes() { Address address = new Address(); @@ -692,6 +726,8 @@ public class MappingPdxSerializerUnitTests { jonDoe.address = address; + this.pdxSerializer.setIncludeTypeFilters(type -> Address.class.equals(type)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); assertThat(this.pdxSerializer.toData(jonDoe, this.mockWriter)).isTrue(); @@ -727,6 +763,8 @@ public class MappingPdxSerializerUnitTests { .thenThrow(newIllegalArgumentException("test")); try { + this.pdxSerializer.setIncludeTypeFilters(type -> Address.class.equals(type)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.setCustomPdxSerializers(Collections.emptyMap()); this.pdxSerializer.toData(jonDoe, this.mockWriter); } @@ -761,49 +799,81 @@ public class MappingPdxSerializerUnitTests { } @Test - public void toDataFiltersNullTypeReturnsFalse() { - assertThat(this.pdxSerializer.toData(null, this.mockWriter)).isFalse(); - } - - @Test - public void toDataFiltersApacheGeodeTypeReturnsFalse() { - assertThat(this.pdxSerializer.toData(mock(org.apache.geode.cache.EntryEvent.class), this.mockWriter)).isFalse(); - } - - @Test - public void toDataFiltersApplicationDomainTypeReturnsFalse() { - - this.pdxSerializer.setTypeFilters(type -> !ApplicationDomainType.class.equals(type)); - - assertThat(this.pdxSerializer.toData(new ApplicationDomainType(), this.mockWriter)).isFalse(); - } - - @Test - public void toDataAcceptsApplicationDomainObjectTypeReturnsTrue() { + public void toDataAcceptsDeclaredEntityTypeReturnsTrue() { org.springframework.data.gemfire.test.model.Person jonDoe = new org.springframework.data.gemfire.test.model.Person("Jon", "Doe", null, Gender.MALE); - this.pdxSerializer.setTypeFilters(type -> User.class.getPackage().equals(type.getPackage())); + this.pdxSerializer.setIncludeTypeFilters(type -> User.class.getPackage().equals(type.getPackage())); doReturn(true).when(this.pdxSerializer).doToData(any(), any(PdxWriter.class)); assertThat(this.pdxSerializer.toData(new Account(1L), this.mockWriter)).isTrue(); + assertThat(this.pdxSerializer.toData(new Animal(), this.mockWriter)).isTrue(); assertThat(this.pdxSerializer.toData(new Customer(1L), this.mockWriter)).isTrue(); assertThat(this.pdxSerializer.toData(new Programmer("jxblum"), this.mockWriter)).isTrue(); + assertThat(this.pdxSerializer.toData(new Plant(), this.mockWriter)).isTrue(); assertThat(this.pdxSerializer.toData(new RootUser("jxblum"), this.mockWriter)).isTrue(); assertThat(this.pdxSerializer.toData(null, this.mockWriter)).isFalse(); assertThat(this.pdxSerializer.toData(jonDoe, this.mockWriter)).isFalse(); verify(this.pdxSerializer, times(1)).doToData(isA(Account.class), eq(this.mockWriter)); + verify(this.pdxSerializer, times(1)).doToData(isA(Animal.class), eq(this.mockWriter)); verify(this.pdxSerializer, times(1)).doToData(isA(Customer.class), eq(this.mockWriter)); + verify(this.pdxSerializer, times(1)).doToData(isA(Plant.class), eq(this.mockWriter)); verify(this.pdxSerializer, times(1)).doToData(isA(Programmer.class), eq(this.mockWriter)); verify(this.pdxSerializer, times(1)).doToData(isA(RootUser.class), eq(this.mockWriter)); verify(this.pdxSerializer, never()).doToData(isNull(), eq(this.mockWriter)); verify(this.pdxSerializer, never()).doToData(isA(jonDoe.getClass()), eq(this.mockWriter)); } + @Test + public void toDataAcceptsIncludedEntityTypesOverridingExcludedEntityTypes() { + + this.pdxSerializer.setIncludeTypeFilters(type -> type.getPackage().getName().startsWith("org.springframework")); + + doReturn(true).when(this.pdxSerializer).doToData(any(), any(PdxWriter.class)); + + assertThat(this.pdxSerializer.toData(new SpringVersion(), this.mockWriter)).isTrue(); + + verify(this.pdxSerializer, times(1)) + .doToData(isA(SpringVersion.class), eq(this.mockWriter)); + } + + @Test + public void toDataFiltersApacheGeodeTypesReturnsFalse() { + assertThat(this.pdxSerializer.toData(mock(org.apache.geode.cache.EntryEvent.class), this.mockWriter)).isFalse(); + } + + @Test + public void toDataFiltersGemStoneTypesReturnsFalse() { + assertThat(this.pdxSerializer.toData(TestGemStoneGemFireType.class, this.mockWriter)).isFalse(); + } + + @Test + public void toDataFiltersJavaTypesReturnsFalse() { + assertThat(this.pdxSerializer.toData(java.security.Principal.class, this.mockWriter)).isFalse(); + } + + @Test + public void toDataFiltersNullTypesReturnsFalse() { + assertThat(this.pdxSerializer.toData(null, this.mockWriter)).isFalse(); + } + + @Test + public void toDataFiltersSpringFrameworkTypesReturnsFalse() { + + assertThat(this.pdxSerializer.toData(ClassMetadata.class, this. mockWriter)).isFalse(); + assertThat(this.pdxSerializer.toData(Page.class, this.mockWriter)).isFalse(); + assertThat(this.pdxSerializer.toData(GemfireTemplate.class, this.mockWriter)).isFalse(); + } + + @Test + public void toDataFiltersUndeclaredEntityTypeReturnsFalse() { + assertThat(this.pdxSerializer.toData(new ApplicationDomainType(), this.mockWriter)).isFalse(); + } + private static class ApplicationDomainType { } }