Refactor @EnablePdx annotation-based configuration allowing users to register explicit application domain types.

Explicit (Class) type registration conveniently allows java.*, javax.*, com.gemstone.gemfire.*, org.apache.geode.* and org.springframework.* types to be registered, which are excluded by SDG's MappingPdxSerializer by default.

Closes #619.
This commit is contained in:
John Blum
2022-08-19 16:29:51 -07:00
parent 968982701c
commit dbcf4ba58d
3 changed files with 58 additions and 6 deletions

View File

@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
/**
* The {@link EnablePdx} annotation marks a Spring {@link Configuration @Configuration} annotated {@link Class}
@@ -61,6 +62,18 @@ public @interface EnablePdx {
*/
boolean ignoreUnreadFields() default PdxConfiguration.DEFAULT_IGNORE_UNREAD_FIELDS;
/**
* When using the Spring Data Geode's (SDG) {@link MappingPdxSerializer} most application domain {@link Class types}
* are included for Apache Geode PDX serialization, by default. However, certain {@link Class types} are excluded
* by SDG's {@link MappingPdxSerializer}, such as {@literal java.*}, {@literal javax.*}, {@literal com.gemstone.*},
* {@literal org.apache.geode.*} and {@literal org.springframework.*} {@link Class types}. This allows the default
* behavior to be overridden when and where necessary.
*
* @return an array of {@link Class types} to be handled by the {@link MappingPdxSerializer}, possibly overriding
* the excluded {@link Class types} by default.
*/
Class<?>[] includeDomainTypes() default {};
/**
* Configures whether the type metadata for PDX objects is persisted to disk.
*

View File

@@ -16,7 +16,9 @@
package org.springframework.data.gemfire.config.annotation;
import java.lang.annotation.Annotation;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.pdx.PdxSerializer;
@@ -36,6 +38,7 @@ import org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFact
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.data.gemfire.support.NoOpBeanFactoryPostProcessor;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
@@ -57,6 +60,7 @@ import org.springframework.util.StringUtils;
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
* @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
* @see org.springframework.data.gemfire.support.NoOpBeanFactoryPostProcessor
* @since 2.1.0
@@ -76,6 +80,8 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
private Boolean persistent;
private Boolean readSerialized;
private Class<?>[] includeDomainTypes = {};
private String diskStoreName;
private String serializerBeanName;
@@ -106,6 +112,8 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
? enablePdxAttributes.getBoolean("ignoreUnreadFields")
: DEFAULT_IGNORE_UNREAD_FIELDS));
setIncludeDomainTypes(enablePdxAttributes.getClassArray("includeDomainTypes"));
setPersistent(resolveProperty(pdxProperty("persistent"),
enablePdxAttributes.containsKey("persistent")
? enablePdxAttributes.getBoolean("persistent")
@@ -139,6 +147,14 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
return Boolean.TRUE.equals(this.ignoreUnreadFields);
}
void setIncludeDomainTypes(Class<?>[] includeDomainTypes) {
this.includeDomainTypes = includeDomainTypes;
}
protected Class<?>[] getIncludeDomainTypes() {
return ArrayUtils.nullSafeArray(this.includeDomainTypes, Class.class);
}
void setPersistent(Boolean persistent) {
this.persistent = persistent;
}
@@ -189,7 +205,7 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see <a href="https://geode.apache.org/docs/guide/113/developing/data_serialization/gemfire_pdx_serialization.html">Geode PDX Serialization</a>
*/
protected void configurePdx(CacheFactoryBean cacheFactoryBean) {
protected void configurePdx(@NonNull CacheFactoryBean cacheFactoryBean) {
getDiskStoreName().ifPresent(cacheFactoryBean::setPdxDiskStoreName);
@@ -240,15 +256,20 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
* @see org.apache.geode.pdx.PdxSerializer
* @see #getBeanFactory()
*/
@NonNull
protected PdxSerializer resolvePdxSerializer() {
protected @NonNull PdxSerializer resolvePdxSerializer() {
BeanFactory beanFactory = getBeanFactory();
return getSerializerBeanName()
PdxSerializer serializer = getSerializerBeanName()
.filter(beanFactory::containsBean)
.map(beanName -> beanFactory.getBean(beanName, PdxSerializer.class))
.orElseGet(this::newPdxSerializer);
if (serializer instanceof MappingPdxSerializer mappingSerializer) {
mappingSerializer.setIncludeTypeFilters(buildIncludeTypeFilters());
}
return serializer;
}
/**
@@ -258,11 +279,23 @@ public class PdxConfiguration extends AbstractAnnotationConfigSupport implements
* @return a new instance of {@link PdxSerializer}.
* @see org.apache.geode.pdx.PdxSerializer
*/
@NonNull
@SuppressWarnings("unchecked")
protected <T extends PdxSerializer> T newPdxSerializer() {
protected @NonNull <T extends PdxSerializer> T newPdxSerializer() {
return (T) MappingPdxSerializer.create(resolveMappingContext().orElse(null),
resolveConversionService().orElse(null));
}
private @NonNull Predicate<Class<?>> buildIncludeTypeFilters() {
Predicate<Class<?>> includeTypeFilter = type -> false;
for (Class<?> domainType : getIncludeDomainTypes()) {
if (Objects.nonNull(domainType)) {
includeTypeFilter = includeTypeFilter.or(type -> domainType.isAssignableFrom(type));
}
}
return includeTypeFilter;
}
}

View File

@@ -33,6 +33,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.gemstone.gemfire.TestGemStoneGemFireType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
@@ -92,6 +94,7 @@ public class EnablePdxConfigurationUnitTests {
annotationAttributes.put("diskStoreName", "MockDiskStore");
annotationAttributes.put("ignoreUnreadFields", Boolean.TRUE);
annotationAttributes.put("includeDomainTypes", new Class[] { TestGemStoneGemFireType.class });
annotationAttributes.put("persistent", Boolean.TRUE);
annotationAttributes.put("readSerialized", Boolean.TRUE);
annotationAttributes.put("serializerBeanName", "MockPdxSerializer");
@@ -106,6 +109,7 @@ public class EnablePdxConfigurationUnitTests {
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.getDiskStoreName().orElse(null)).isEqualTo("MockDiskStore");
assertThat(this.pdxConfiguration.isIgnoreUnreadFields()).isTrue();
assertThat(this.pdxConfiguration.getIncludeDomainTypes()).containsExactly(TestGemStoneGemFireType.class);
assertThat(this.pdxConfiguration.isPersistent()).isTrue();
assertThat(this.pdxConfiguration.isReadSerialized()).isTrue();
assertThat(this.pdxConfiguration.getSerializerBeanName().orElse(null)).isEqualTo("MockPdxSerializer");
@@ -126,6 +130,7 @@ public class EnablePdxConfigurationUnitTests {
assertThat(this.pdxConfiguration.getDiskStoreName().isPresent()).isFalse();
assertThat(this.pdxConfiguration.isIgnoreUnreadFields()).isFalse();
assertThat(this.pdxConfiguration.getIncludeDomainTypes()).isEmpty();
assertThat(this.pdxConfiguration.isPersistent()).isFalse();
assertThat(this.pdxConfiguration.isReadSerialized()).isFalse();
assertThat(this.pdxConfiguration.getSerializerBeanName().isPresent()).isFalse();
@@ -168,6 +173,7 @@ public class EnablePdxConfigurationUnitTests {
verify(this.pdxConfiguration, times(1)).getDiskStoreName();
verify(this.pdxConfiguration, times(1)).isIgnoreUnreadFields();
verify(this.pdxConfiguration, never()).getIncludeDomainTypes();
verify(this.pdxConfiguration, times(1)).isPersistent();
verify(this.pdxConfiguration, times(1)).isReadSerialized();
verify(this.pdxConfiguration, times(1)).getSerializerBeanName();