diff --git a/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java b/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java index 0fa04288..d12e584a 100644 --- a/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java @@ -22,6 +22,7 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -29,6 +30,7 @@ import org.springframework.beans.factory.support.ManagedArray; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; @@ -81,6 +83,8 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser { protected void doParseCommonRegionConfiguration(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinitionBuilder regionAttributesBuilder, boolean subRegion) { + mergeTemplateRegionAttributes(element, parserContext, builder, regionAttributesBuilder); + String resolvedCacheRef = ParsingUtils.resolveCacheReference(element.getAttribute("cache-ref")); if (!subRegion) { @@ -159,6 +163,45 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser { } } + void mergeTemplateRegionAttributes(Element element, ParserContext parserContext, + BeanDefinitionBuilder regionBuilder, BeanDefinitionBuilder regionAttributesBuilder) { + String regionTemplate = getParentName(element); + + if (StringUtils.hasText(regionTemplate)) { + if (parserContext.getRegistry().containsBeanDefinition(regionTemplate)) { + BeanDefinition regionTemplateDefinition = parserContext.getRegistry() + .getBeanDefinition(regionTemplate); + + BeanDefinition regionTemplateAttributesDefinition = getRegionAttributesBeanDefinition( + regionTemplateDefinition); + + if (regionTemplateAttributesDefinition != null) { + // NOTE we only need to merge the parent RegionAttributes with this since the parent will have + // already merged it's parent's RegionAttributes and so on... + regionAttributesBuilder.getRawBeanDefinition().overrideFrom(regionTemplateAttributesDefinition); + } + } + else { + parserContext.getReaderContext().error(String.format( + "The Region template [%1$s] must be defined in the Spring context configuration meta-data 'before' the Region [%2$s] using the template!", + regionTemplate, resolveId(element, regionBuilder.getRawBeanDefinition(), parserContext)), element); + } + } + } + + BeanDefinition getRegionAttributesBeanDefinition(final BeanDefinition region) { + Assert.notNull(region, "The 'Region' BeanDefinition must not be null!"); + + Object regionAttributesDefinition = null; + + if (region.getPropertyValues().contains("attributes")) { + PropertyValue regionAttributes = region.getPropertyValues().getPropertyValue("attributes"); + regionAttributesDefinition = regionAttributes.getValue(); + } + + return (regionAttributesDefinition instanceof BeanDefinition ? (BeanDefinition) regionAttributesDefinition : null); + } + private void parseCollectionOfCustomSubElements(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, String className, String subElementName, String propertyName) { List subElements = DomUtils.getChildElementsByTagName(element, subElementName, diff --git a/src/main/java/org/springframework/data/gemfire/config/PartitionedRegionParser.java b/src/main/java/org/springframework/data/gemfire/config/PartitionedRegionParser.java index 65615840..324b591f 100644 --- a/src/main/java/org/springframework/data/gemfire/config/PartitionedRegionParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/PartitionedRegionParser.java @@ -18,6 +18,8 @@ package org.springframework.data.gemfire.config; import java.util.List; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; @@ -53,8 +55,6 @@ class PartitionedRegionParser extends AbstractRegionParser { validateDataPolicyShortcutAttributesMutualExclusion(element, parserContext); - super.doParse(element, regionBuilder); - BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition( RegionAttributesFactoryBean.class); @@ -65,6 +65,8 @@ class PartitionedRegionParser extends AbstractRegionParser { BeanDefinitionBuilder partitionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition( PartitionAttributesFactoryBean.class); + mergeTemplateRegionPartitionAttributes(element, parserContext, regionBuilder, partitionAttributesBuilder); + parseColocatedWith(element, regionBuilder, partitionAttributesBuilder, "colocated-with"); ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "copies", "redundantCopies"); ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "local-max-memory"); @@ -108,6 +110,40 @@ class PartitionedRegionParser extends AbstractRegionParser { regionAttributesBuilder.addPropertyValue("partitionAttributes", partitionAttributesBuilder.getBeanDefinition()); } + void mergeTemplateRegionPartitionAttributes(Element element, ParserContext parserContext, + BeanDefinitionBuilder regionBuilder, BeanDefinitionBuilder partitionAttributesBuilder) { + String regionTemplate = getParentName(element); + + if (StringUtils.hasText(regionTemplate)) { + if (parserContext.getRegistry().containsBeanDefinition(regionTemplate)) { + BeanDefinition regionTemplateDefinition = parserContext.getRegistry() + .getBeanDefinition(regionTemplate); + + BeanDefinition regionTemplateAttributesDefinition = getRegionAttributesBeanDefinition( + regionTemplateDefinition); + + if (regionTemplateAttributesDefinition != null) { + if (regionTemplateAttributesDefinition.getPropertyValues().contains("partitionAttributes")) { + PropertyValue partitionAttributes = regionTemplateAttributesDefinition.getPropertyValues() + .getPropertyValue("partitionAttributes"); + + Object partitionAttributesDefinition = partitionAttributes.getValue(); + + if (partitionAttributesDefinition instanceof BeanDefinition) { + partitionAttributesBuilder.getRawBeanDefinition().overrideFrom( + (BeanDefinition) partitionAttributesDefinition); + } + } + } + } + else { + parserContext.getReaderContext().error(String.format( + "The Region template [%1$s] must be defined in the Spring context configuration meta-data 'before' the Region [%2$s] using the template!", + regionTemplate, resolveId(element, regionBuilder.getRawBeanDefinition(), parserContext)), element); + } + } + } + private void parseColocatedWith(Element element, BeanDefinitionBuilder regionBuilder, BeanDefinitionBuilder partitionAttributesBuilder, String attributeName) { // NOTE rather than using a dependency (with depends-on) we could also set the colocatedWith property of the diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.5.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.5.xsd index 5d5f4149..3dc5b584 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.5.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.5.xsd @@ -123,8 +123,7 @@ Configures a data source to be bound to a JNDI context for use with Gemfire tran The name of the cache definition (by default "gemfireCache").]]> - + - + - + - + - + - + - + - + - + simpleReplicateRegion; + @Resource(name = "InheritedReplicateRegion") + private Region inheritedReplicateRegion; + + @Resource(name = "ComplexReplicateRegion") + private Region complexReplicateRegion; + + protected static void assertRegionMetaData(final Region region, final String expectedRegionName) { + assertRegionMetaData(region, expectedRegionName, Region.SEPARATOR + expectedRegionName); + } + + protected static void assertRegionMetaData(final Region region, final String expectedRegionName, final String expectedRegionPath) { + assertNotNull(String.format("The '%1$s' Region was not properly configured and initialized!", + expectedRegionName), region); + assertEquals(expectedRegionName, region.getName()); + assertEquals(expectedRegionPath, region.getFullPath()); + assertNotNull(String.format("The '%1$s' Region must have RegionAttributes defined!", + expectedRegionName), region.getAttributes()); + } + @SuppressWarnings("deprecation") protected static void assertBaseRegionAttributes(final Region region) { assertNotNull("The Region must not be null!", region); @@ -100,6 +125,25 @@ public class TemplateRegionsNamespaceTests { assertEquals("Y", regionAttributes.getCacheWriter().toString()); } + protected static void assertBaseReplicateRegionAttributes(final Region replicateRegion) { + assertBaseRegionAttributes(replicateRegion); + assertEquals(2, replicateRegion.getAttributes().getConcurrencyLevel()); + assertTrue(replicateRegion.getAttributes().getEnableAsyncConflation()); + assertTrue(replicateRegion.getAttributes().getEnableSubscriptionConflation()); + + EvictionAttributes evictionAttributes = replicateRegion.getAttributes().getEvictionAttributes(); + + assertNotNull(evictionAttributes); + assertEquals(EvictionAction.OVERFLOW_TO_DISK, evictionAttributes.getAction()); + assertEquals(EvictionAlgorithm.LRU_ENTRY, evictionAttributes.getAlgorithm()); + assertEquals(1000, evictionAttributes.getMaximum()); + + SubscriptionAttributes subscriptionAttributes = replicateRegion.getAttributes().getSubscriptionAttributes(); + + assertNotNull(subscriptionAttributes); + assertEquals(InterestPolicy.CACHE_CONTENT, subscriptionAttributes.getInterestPolicy()); + } + @Test public void testNoAbstractRegionTemplateBeans() { String[] beanNames = { @@ -124,7 +168,66 @@ public class TemplateRegionsNamespaceTests { @Test public void testSimpleReplicateRegion() { - fail("Not Implemented!"); + assertRegionMetaData(simpleReplicateRegion, "SimpleReplicateRegion"); + assertEquals(DataPolicy.PERSISTENT_REPLICATE, simpleReplicateRegion.getAttributes().getDataPolicy()); + assertEquals(8, simpleReplicateRegion.getAttributes().getConcurrencyLevel()); + assertEquals(Integer.class, simpleReplicateRegion.getAttributes().getKeyConstraint()); + assertTrue(simpleReplicateRegion.getAttributes().isLockGrantor()); + assertEquals(Scope.GLOBAL, simpleReplicateRegion.getAttributes().getScope()); + assertNull(simpleReplicateRegion.getAttributes().getValueConstraint()); + assertNotNull(simpleReplicateRegion.getAttributes().getCacheListeners()); + assertEquals(1, simpleReplicateRegion.getAttributes().getCacheListeners().length); + assertTrue(simpleReplicateRegion.getAttributes().getCacheListeners()[0] instanceof TestCacheListener); + assertEquals("Simple", simpleReplicateRegion.getAttributes().getCacheListeners()[0].toString()); + assertNull(simpleReplicateRegion.getAttributes().getCacheLoader()); + assertNull(simpleReplicateRegion.getAttributes().getCacheWriter()); + } + + @Test + public void testInheritedReplicateRegion() { + assertRegionMetaData(inheritedReplicateRegion, "InheritedReplicateRegion"); + assertEquals(Scope.DISTRIBUTED_ACK, inheritedReplicateRegion.getAttributes().getScope()); + assertBaseReplicateRegionAttributes(inheritedReplicateRegion); + } + + @Test + @SuppressWarnings("deprecation") + public void testComplexReplicateRegion() { + assertRegionMetaData(complexReplicateRegion, "ComplexReplicateRegion"); + assertEquals(DataPolicy.PERSISTENT_REPLICATE, complexReplicateRegion.getAttributes().getDataPolicy()); + assertEquals(Scope.DISTRIBUTED_ACK, complexReplicateRegion.getAttributes().getScope()); + assertFalse(complexReplicateRegion.getAttributes().getCloningEnabled()); + assertEquals(2, complexReplicateRegion.getAttributes().getConcurrencyLevel()); + assertTrue(complexReplicateRegion.getAttributes().isDiskSynchronous()); + assertFalse(complexReplicateRegion.getAttributes().getEnableAsyncConflation()); + assertTrue(complexReplicateRegion.getAttributes().getEnableSubscriptionConflation()); + assertFalse(complexReplicateRegion.getAttributes().getIgnoreJTA()); + assertEquals(1000, complexReplicateRegion.getAttributes().getInitialCapacity()); + assertEquals(Integer.class, complexReplicateRegion.getAttributes().getKeyConstraint()); + assertEquals(0.90f, complexReplicateRegion.getAttributes().getLoadFactor()); + assertTrue(complexReplicateRegion.getAttributes().getStatisticsEnabled()); + assertTrue(complexReplicateRegion.getAttributes().getIndexMaintenanceSynchronous()); + assertEquals(String.class, complexReplicateRegion.getAttributes().getValueConstraint()); + assertNotNull(complexReplicateRegion.getAttributes().getCacheListeners()); + assertEquals(1, complexReplicateRegion.getAttributes().getCacheListeners().length); + assertTrue(complexReplicateRegion.getAttributes().getCacheListeners()[0] instanceof TestCacheListener); + assertEquals("ComplexListener", complexReplicateRegion.getAttributes().getCacheListeners()[0].toString()); + assertNotNull(complexReplicateRegion.getAttributes().getCacheLoader()); + assertEquals("ComplexLoader", complexReplicateRegion.getAttributes().getCacheLoader().toString()); + assertNotNull(complexReplicateRegion.getAttributes().getCacheWriter()); + assertEquals("Y", complexReplicateRegion.getAttributes().getCacheWriter().toString()); + + EvictionAttributes evictionAttributes = complexReplicateRegion.getAttributes().getEvictionAttributes(); + + assertNotNull(evictionAttributes); + assertEquals(EvictionAction.OVERFLOW_TO_DISK, evictionAttributes.getAction()); + assertEquals(EvictionAlgorithm.LRU_ENTRY, evictionAttributes.getAlgorithm()); + assertEquals(1024, evictionAttributes.getMaximum()); + + SubscriptionAttributes subscriptionAttributes = complexReplicateRegion.getAttributes().getSubscriptionAttributes(); + + assertNotNull(subscriptionAttributes); + assertEquals(InterestPolicy.ALL, subscriptionAttributes.getInterestPolicy()); } protected static interface Nameable { diff --git a/src/test/resources/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests-context.xml b/src/test/resources/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests-context.xml index a891af7e..73beb2c8 100644 --- a/src/test/resources/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests-context.xml +++ b/src/test/resources/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests-context.xml @@ -19,7 +19,7 @@ - + + disk-synchronous="false" ignore-jta="false" key-constraint="java.lang.String" persistent="false" + template="BaseRegion"> @@ -45,7 +46,7 @@ + enable-subscription-conflation="true" scope="global" template="BaseRegion"> @@ -53,19 +54,19 @@ - + - + - + - + @@ -74,7 +75,8 @@ + total-buckets="77" recovery-delay="1000" startup-recovery-delay="5000" + template="ExtendedRegionWithOverrides"> @@ -85,6 +87,8 @@ + +