SGF-756 - Add ability to declare include filters in addition to exclude filters on o.s.d.g.mapping.MappingPdxSerializer.

Additionally, o.s.d.g.mapping.MappingPdxSerializer will filter Null types, Apache Geode types, GemStone GemFire types, Java types and Spring Framework types by default.
This commit is contained in:
John Blum
2018-06-08 13:14:03 -07:00
parent eff11f566d
commit bca6ca8a7a
3 changed files with 334 additions and 107 deletions

View File

@@ -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<PdxSerializerResolver> pdxSerializerResolvers = new CopyOnWriteArrayList<>();
private final Logger logger = LoggerFactory.getLogger(getClass());
private Map<?, PdxSerializer> customPdxSerializers;
private final Map<Object, PdxSerializer> customPdxSerializers = new ConcurrentHashMap<>();
private Predicate<Class<?>> typeFilters = TypeFilters.EXCLUDE_NULL_TYPES
private Predicate<Class<?>> 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<Class<?>> 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<?, PdxSerializer> customPdxSerializers) {
Assert.notNull(customPdxSerializers, "Custom PdxSerializers must not be null");
this.customPdxSerializers = customPdxSerializers;
public void setCustomPdxSerializers(Map<?, PdxSerializer> customPdxSerializers) {
Optional.ofNullable(customPdxSerializers).ifPresent(this.customPdxSerializers::putAll);
}
/**
@@ -322,27 +339,11 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw
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());
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<Class<?>> typeFilters) {
this.typeFilters = typeFilters != null ? this.typeFilters.and(typeFilters) : this.typeFilters;
public void setExcludeTypeFilters(@Nullable Predicate<Class<?>> 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<Class<?>> 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<Class<?>> 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<?, PdxSerializer> customPdxSerializers,
@NonNull PersistentProperty<?> property);
}
public enum PdxSerializerResolvers implements PdxSerializerResolver {
PROPERTY {
@Override
public PdxSerializer resolve(Map<?, PdxSerializer> customPdxSerializers, PersistentProperty<?> property) {
return customPdxSerializers.get(property);
}
},
PROPERTY_NAME {
@Override
public PdxSerializer resolve(Map<?, PdxSerializer> customPdxSerializers, PersistentProperty<?> property) {
return customPdxSerializers.get(toFullyQualifiedPropertyName(property));
}
},
PROPERTY_TYPE {
@Override
public PdxSerializer resolve(Map<?, PdxSerializer> 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<Class<?>> {
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();
}
},
}
}