From d6dc23a440177033e0ccccad138d8d63c10e2e49 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 2 Jun 2015 14:41:31 -0700 Subject: [PATCH] SGF-407 - Modify the MappingPdxSerializer to allow for extensibility. --- gradle.properties | 18 +-- .../gemfire/mapping/MappingPdxSerializer.java | 143 +++++++++++++----- .../MappingPdxSerializerIntegrationTest.java | 45 +++--- 3 files changed, 136 insertions(+), 70 deletions(-) diff --git a/gradle.properties b/gradle.properties index d8231ac8..3c006587 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,14 @@ antlrVersion=2.7.7 -slf4jVersion=1.7.12 -junitVersion=4.12 -gemfireVersion=8.1.0 -spring.range="[4.0.0, 5.0.0)" aspectjVersion=1.8.5 -springDataBuildVersion=1.7.0.BUILD-SNAPSHOT -springVersion=4.1.6.RELEASE -springDataCommonsVersion=1.11.0.BUILD-SNAPSHOT -log4jVersion=1.2.17 +gemfireVersion=8.1.0 hamcrestVersion=1.3 -version=1.7.0.BUILD-SNAPSHOT jacksonVersion=2.5.1 +junitVersion=4.12 +log4jVersion=1.2.17 mockitoVersion=1.10.19 +slf4jVersion=1.7.12 +spring.range="[4.0.0, 5.0.0)" +springVersion=4.1.6.RELEASE +springDataBuildVersion=1.7.0.BUILD-SNAPSHOT +springDataCommonsVersion=1.11.0.BUILD-SNAPSHOT +version=1.7.0.BUILD-SNAPSHOT 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 49b822d7..c48e3926 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -15,6 +15,7 @@ */ package org.springframework.data.gemfire.mapping; +import java.util.Collections; import java.util.Map; import org.springframework.beans.BeansException; @@ -42,16 +43,17 @@ import com.gemstone.gemfire.pdx.PdxWriter; * * @author Oliver Gierke * @author David Turanski + * @author John Blum */ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAware { - private final GemfireMappingContext mappingContext; - private final ConversionService conversionService; private EntityInstantiators instantiators; - - private Map,PdxSerializer> customSerializers; + + private final GemfireMappingContext mappingContext; + + private Map, PdxSerializer> customSerializers; private SpELContext context; @@ -70,6 +72,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw this.mappingContext = mappingContext; this.conversionService = conversionService; this.instantiators = new EntityInstantiators(); + this.customSerializers = Collections.emptyMap(); this.context = new SpELContext(PdxReaderPropertyAccessor.INSTANCE); } @@ -81,15 +84,20 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw this(new GemfireMappingContext(), new DefaultConversionService()); } - /** - * Configures the {@link EntityInstantiator}s to be used to create the - * instances to be read. - * - * @param gemfireInstantiators must not be {@literal null}. + /* + * (non-Javadoc) + * + * @see org.springframework.context.ApplicationContextAware#setApplicationContext( + * org.springframework.context.ApplicationContext) */ - public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { - Assert.notNull(gemfireInstantiators); - this.instantiators = new EntityInstantiators(gemfireInstantiators); + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = new SpELContext(context, applicationContext); + } + + /* (non-Javadoc) */ + protected ConversionService getConversionService() { + return conversionService; } /** @@ -98,19 +106,33 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw * @param customSerializers a mapping of domain object class types and their corresponding PDX serializer. */ public void setCustomSerializers(Map, PdxSerializer> customSerializers) { + Assert.notNull(customSerializers); this.customSerializers = customSerializers; } - /* - * (non-Javadoc) - * - * @see - * org.springframework.context.ApplicationContextAware#setApplicationContext - * (org.springframework.context.ApplicationContext) + /* (non-Javadoc) */ + protected Map, PdxSerializer> getCustomSerializers() { + return Collections.unmodifiableMap(customSerializers); + } + + /** + * Configures the {@link EntityInstantiator}s used to create the instances read by this PdxSerializer. + * + * @param gemfireInstantiators must not be {@literal null}. */ - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.context = new SpELContext(context, applicationContext); + public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { + Assert.notNull(gemfireInstantiators); + this.instantiators = new EntityInstantiators(gemfireInstantiators); + } + + /* (non-Javadoc) */ + protected EntityInstantiators getGemfireInstantiators() { + return instantiators; + } + + /* (non-Javadoc) */ + protected GemfireMappingContext getMappingContext() { + return mappingContext; } /* @@ -121,32 +143,31 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw */ @Override public Object fromData(Class type, final PdxReader reader) { - final GemfirePersistentEntity entity = mappingContext.getPersistentEntity(type); - EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); - GemfirePropertyValueProvider propertyValueProvider = new GemfirePropertyValueProvider(reader); + final GemfirePersistentEntity entity = getPersistentEntity(type); - PersistentEntityParameterValueProvider provider = new PersistentEntityParameterValueProvider( - entity, propertyValueProvider, null); - - Object instance = instantiator.createInstance(entity, provider); + Object instance = getInstantiatorFor(entity).createInstance(entity, + new PersistentEntityParameterValueProvider(entity, + new GemfirePropertyValueProvider(reader), null)); - final BeanWrapper wrapper = BeanWrapper.create(instance, conversionService); + final BeanWrapper wrapper = BeanWrapper.create(instance, getConversionService()); entity.doWithProperties(new PropertyHandler() { @Override public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { - if (entity.isConstructorArgument(persistentProperty)) { return; } - + PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); - Object value = null; + Object value; + if (customSerializer != null) { value = customSerializer.fromData(persistentProperty.getType(), reader); - } else { + } + else { value = reader.readField(persistentProperty.getName()); } + try { wrapper.setProperty(persistentProperty, value); } @@ -167,25 +188,30 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw */ @Override public boolean toData(Object value, final PdxWriter writer) { - GemfirePersistentEntity entity = mappingContext.getPersistentEntity(value.getClass()); - final BeanWrapper wrapper = BeanWrapper.create(value, conversionService); + GemfirePersistentEntity entity = getPersistentEntity(value.getClass()); + + final BeanWrapper wrapper = BeanWrapper.create(value, getConversionService()); entity.doWithProperties(new PropertyHandler() { - @SuppressWarnings({ "unchecked", "rawtypes" }) @Override + @SuppressWarnings("unchecked") public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { try { Object propertyValue = wrapper.getProperty(persistentProperty); + PdxSerializer customSerializer = getCustomSerializer(persistentProperty.getType()); + if (customSerializer != null) { customSerializer.toData(propertyValue, writer); - } else { + } + else { writer.writeField(persistentProperty.getName(), propertyValue, (Class) persistentProperty.getType()); } } catch (Exception e) { - throw new MappingException("Could not write value for property " + persistentProperty.toString(), e); + throw new MappingException(String.format("Could not write value for property %1$s", + persistentProperty), e); } } }); @@ -198,8 +224,43 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw return true; } - - private PdxSerializer getCustomSerializer(Class clazz) { - return customSerializers == null ? null : customSerializers.get(clazz); + + /** + * Looks up and returns a custom PdxSerializer based on the class type of the object to (de)serialize. + * + * @param type the Class type of the object to (de)serialize. + * @return a "custom" PdxSerializer for the given class type or null if no custom PdxSerializer + * for the given class type was registered. + * @see #getCustomSerializers() + * @see com.gemstone.gemfire.pdx.PdxSerializer + */ + protected PdxSerializer getCustomSerializer(Class type) { + return getCustomSerializers().get(type); } + + /** + * 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 getInstantiatorFor(PersistentEntity entity) { + return getGemfireInstantiators().getInstantiatorFor(entity); + } + + /** + * Looks up and returns the PersistentEntity meta-data for the given entity class type. + * + * @param entityType the Class type of the actual persistent entity, application domain object class. + * @return the PersistentEntity meta-data for the given entity class type. + * @see #getMappingContext() + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + */ + protected GemfirePersistentEntity getPersistentEntity(Class entityType) { + return getMappingContext().getPersistentEntity(entityType); + } + } diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java index 68b30090..6c1bd113 100644 --- a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java @@ -43,6 +43,7 @@ import com.gemstone.gemfire.cache.RegionFactory; * Integration tests for {@link MappingPdxSerializer}. * * @author Oliver Gierke + * @author John Blum */ public class MappingPdxSerializerIntegrationTest { @@ -57,6 +58,9 @@ public class MappingPdxSerializerIntegrationTest { new DefaultConversionService()); CacheFactory factory = new CacheFactory(); + factory.set("name", MappingPdxSerializer.class.getSimpleName()); + factory.set("mcast-port", "0"); + factory.set("log-level", "warning"); factory.setPdxSerializer(serializer); factory.setPdxPersistent(true); cache = factory.create(); @@ -66,6 +70,25 @@ public class MappingPdxSerializerIntegrationTest { region = regionFactory.create("foo"); } + @AfterClass + public static void tearDown() { + try { + cache.close(); + } + catch (Exception ignore) { + } + finally { + for (String name : new File(".").list(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith("BACKUP"); + } + })) { + new File(name).delete(); + } + } + } + @Test public void serializeAndDeserializeCorrectly() { @@ -86,7 +109,8 @@ public class MappingPdxSerializerIntegrationTest { assertThat(reference.getLastname(), is(person.getLastname())); assertThat(reference.address, is(person.address)); } - + + @Test public void serializeAndDeserializeCorrectlyWithDataSerializable() { @@ -108,26 +132,7 @@ public class MappingPdxSerializerIntegrationTest { assertThat(reference.address, is(person.address)); assertThat(reference.dsProperty.getValue(),is("foo")); } - - @AfterClass - public static void tearDown() { - try { - cache.close(); - } - catch (Exception e) { - } - for (String name : new File(".").list(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - return name.startsWith("BACKUP"); - } - })) { - new File(name).delete(); - } - } - @SuppressWarnings("serial") public static class PersonWithDataSerializableProperty extends Person {