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 47265b7c..e02113b0 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; @@ -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 @@ -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,11 +290,8 @@ 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); } /** @@ -322,27 +339,11 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw 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()); + return this.pdxSerializerResolvers.stream() + .map(it -> it.resolve(customPdxSerializers, property)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); } /** @@ -454,19 +455,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 +500,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 @@ -517,8 +554,8 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw 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 +569,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); } } }); @@ -668,15 +705,71 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return obj != null ? obj.getClass() : null; } + @FunctionalInterface + public interface PdxSerializerResolver { + + @Nullable + PdxSerializer resolve(@NonNull Map customPdxSerializers, + @NonNull PersistentProperty property); + + } + + public enum PdxSerializerResolvers implements PdxSerializerResolver { + + PROPERTY { + + @Override + public PdxSerializer resolve(Map customPdxSerializers, PersistentProperty property) { + return customPdxSerializers.get(property); + } + }, + + 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_COM_GEMSTONE_GEMFIRE_TYPES { + 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(COM_GEMSTONE_GEMFIRE_PACKAGE_NAME)) + .filter(it -> !it.getPackage().getName().startsWith(JAVA_PACKAGE_NAME)) .isPresent(); } }, @@ -689,6 +782,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 +803,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 497dbb1b..f86fd083 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java @@ -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; @@ -109,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 @@ -228,24 +238,31 @@ public class MappingPdxSerializerUnitTests { assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEqualTo(customPdxSerializers); } - @Test(expected = IllegalArgumentException.class) - public void setCustomPdxSerializersToNull() { + @Test + public void setCustomPdxSerializersIsNullSafe() { - try { - this.pdxSerializer.setCustomPdxSerializers(null); - } - catch (IllegalArgumentException expected) { + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); - assertThat(expected).hasMessage("Custom PdxSerializers must not be null"); - assertThat(expected).hasNoCause(); + this.pdxSerializer.setCustomPdxSerializers(null); - throw expected; - } + assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); } @Test @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForProperty() { + public void getCustomPdxSerializerReturnsNull() { + + 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("all") + public void getCustomPdxSerializerReturnsPdxSerializerForProperty() { PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); PdxSerializer mockPropertySerializer = mock(PdxSerializer.class); @@ -266,7 +283,7 @@ public class MappingPdxSerializerUnitTests { @Test @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForPropertyName() { + public void getCustomPdxSerializerReturnsPdxSerializerForPropertyName() { PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); @@ -285,7 +302,7 @@ public class MappingPdxSerializerUnitTests { @Test @SuppressWarnings("all") - public void getCustomPdxSerializerForMappedPersistentPropertyReturnsPdxSerializerForPropertyType() { + public void getCustomPdxSerializerReturnsPdxSerializerForPropertyType() { PdxSerializer mockNamedSerializer = mock(PdxSerializer.class); PdxSerializer mockTypedSerializer = mock(PdxSerializer.class); @@ -305,18 +322,7 @@ public class MappingPdxSerializerUnitTests { } @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 + // TODO remove! @SuppressWarnings("deprecation") public void getCustomSerializerForMappedTypeReturnsPdxSerializer() { @@ -328,6 +334,7 @@ public class MappingPdxSerializerUnitTests { } @Test + // TODO remove! @SuppressWarnings("deprecation") public void getCustomSerializerForUnmappedTypeReturnsNull() { assertThat(this.pdxSerializer.getCustomPdxSerializers()).isEmpty(); @@ -344,7 +351,7 @@ public class MappingPdxSerializerUnitTests { when(mockProperty.getOwner()).thenReturn(mockEntity); when(mockEntity.getType()).thenReturn(Person.class); - assertThat(this.pdxSerializer.toFullyQualifiedPropertyName(mockProperty)) + assertThat(MappingPdxSerializer.PdxSerializerResolvers.toFullyQualifiedPropertyName(mockProperty)) .isEqualTo(Person.class.getName().concat(".mockProperty")); verify(mockEntity, times(1)).getType(); @@ -353,7 +360,7 @@ public class MappingPdxSerializerUnitTests { } @Test - public void setGemfireInstantiatorsWithEntityInstantiators() { + public void setGemfireInstantiatorsWithNonNullEntityInstantiators() { EntityInstantiators mockEntityInstantiators = mock(EntityInstantiators.class); @@ -378,7 +385,7 @@ public class MappingPdxSerializerUnitTests { } @Test - public void setGemfireInstantiatorsWithMappingOfClassTypesToEntityInstantiators() { + public void setGemfireInstantiatorsWithNonNullMappingOfClassTypesToEntityInstantiators() { Map, EntityInstantiator> entityInstantiators = Collections.singletonMap(Person.class, mock(EntityInstantiator.class)); @@ -404,7 +411,7 @@ public class MappingPdxSerializerUnitTests { } @Test - public void getInstantiatorForManagedPersistentEntityWithInstantiator() { + public void getInstantiatorForManagedPersistentEntityWithEntityInstantiator() { EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); @@ -421,7 +428,7 @@ public class MappingPdxSerializerUnitTests { } @Test - public void getInstantiatorForNonManagedPersistentEntityWithNoInstantiator() { + public void getInstantiatorForNonManagedPersistentEntityWithNoEntityInstantiator() { EntityInstantiator mockEntityInstantiator = mock(EntityInstantiator.class); @@ -561,6 +568,8 @@ public class MappingPdxSerializerUnitTests { this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + 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); @@ -593,6 +602,7 @@ public class MappingPdxSerializerUnitTests { try { this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.fromData(Person.class, this.mockReader); } catch (MappingException expected) { @@ -614,7 +624,7 @@ public class MappingPdxSerializerUnitTests { @Test @SuppressWarnings("unchecked") - public void fromDataUsesRegisteredInstantiator() { + public void fromDataUsesRegisteredEntityInstantiator() { Address address = new Address(); @@ -633,6 +643,7 @@ public class MappingPdxSerializerUnitTests { this.pdxSerializer.setCustomPdxSerializers(Collections.singletonMap(Address.class, mockAddressSerializer)); this.pdxSerializer.setGemfireInstantiators(Collections.singletonMap(Person.class, this.mockInstantiator)); + this.pdxSerializer.setIncludeTypeFilters(type -> Person.class.equals(type)); this.pdxSerializer.fromData(Person.class, this.mockReader); GemfirePersistentEntity persistentEntity = @@ -648,41 +659,75 @@ public class MappingPdxSerializerUnitTests { @Test 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 fromDataWithTypeFilterFiltersNullTypeReturnsNull() { + public void fromDataWithTypeFilterFiltersGemStoneTypesReturnsNull() { + assertThat(this.pdxSerializer.fromData(TestGemStoneGemFireType.class, this.mockReader)).isNull(); + } + + @Test + 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 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() { - - this.pdxSerializer.setTypeFilters(type -> !ApplicationDomainType.class.equals(type)); - assertThat(this.pdxSerializer.fromData(ApplicationDomainType.class, this.mockReader)).isNull(); } @@ -701,6 +746,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(); @@ -736,6 +783,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); } @@ -769,24 +818,6 @@ public class MappingPdxSerializerUnitTests { } } - @Test - public void toDataFiltersUndeclaredEntityTypeReturnsFalse() { - - this.pdxSerializer.setTypeFilters(type -> !ApplicationDomainType.class.equals(type)); - - assertThat(this.pdxSerializer.toData(new ApplicationDomainType(), this.mockWriter)).isFalse(); - } - - @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 toDataAcceptsDeclaredEntityTypeReturnsTrue() { @@ -794,25 +825,75 @@ public class MappingPdxSerializerUnitTests { 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 { } }