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 79cdf298..eeb39024 100644
--- a/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java
+++ b/src/main/java/org/springframework/data/gemfire/config/AbstractRegionParser.java
@@ -49,9 +49,20 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return getRegionFactoryClass();
}
+ @Override
+ protected String getParentName(final Element element) {
+ String regionTemplate = element.getAttribute("template");
+ return (StringUtils.hasText(regionTemplate) ? regionTemplate : super.getParentName(element));
+ }
+
protected abstract Class> getRegionFactoryClass();
- protected boolean isSubRegion(Element element) {
+ protected boolean isRegionTemplate(final Element element) {
+ String localName = element.getLocalName();
+ return (localName != null && localName.endsWith("-template"));
+ }
+
+ protected boolean isSubRegion(final Element element) {
String localName = element.getParentNode().getLocalName();
return (localName != null && localName.endsWith("region"));
}
@@ -59,6 +70,7 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
+ builder.setAbstract(isRegionTemplate(element));
boolean subRegion = isSubRegion(element);
doParseRegion(element, parserContext, builder, subRegion);
}
diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java
index 60f15ea3..f15013f9 100644
--- a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java
+++ b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java
@@ -52,10 +52,15 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("cache-server", new CacheServerParser());
registerBeanDefinitionParser("client-cache", new ClientCacheParser());
registerBeanDefinitionParser("client-region", new ClientRegionParser());
+ registerBeanDefinitionParser("client-region-template", new ClientRegionParser());
registerBeanDefinitionParser("lookup-region", new LookupRegionParser());
+ registerBeanDefinitionParser("region-template", new TemplateRegionParser());
registerBeanDefinitionParser("local-region", new LocalRegionParser());
+ registerBeanDefinitionParser("local-region-template", new LocalRegionParser());
registerBeanDefinitionParser("partitioned-region", new PartitionedRegionParser());
+ registerBeanDefinitionParser("partitioned-region-template", new PartitionedRegionParser());
registerBeanDefinitionParser("replicated-region", new ReplicatedRegionParser());
+ registerBeanDefinitionParser("replicated-region-template", new ReplicatedRegionParser());
registerBeanDefinitionParser("async-event-queue", new AsyncEventQueueParser());
registerBeanDefinitionParser("disk-store", new DiskStoreParser());
registerBeanDefinitionParser("gateway-hub", new GatewayHubParser());
diff --git a/src/main/java/org/springframework/data/gemfire/config/TemplateRegionParser.java b/src/main/java/org/springframework/data/gemfire/config/TemplateRegionParser.java
new file mode 100644
index 00000000..f88dbc30
--- /dev/null
+++ b/src/main/java/org/springframework/data/gemfire/config/TemplateRegionParser.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2013 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 org.springframework.data.gemfire.config;
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.data.gemfire.RegionAttributesFactoryBean;
+import org.springframework.data.gemfire.RegionFactoryBean;
+import org.w3c.dom.Element;
+
+/**
+ * The TemplateRegionParser class is a generic Region parser used to parse Region Template bean definitions in the
+ * Spring context configuration meta-data to encapsulate common Region 'attribute' configuration irrespective of the
+ * Region's Data Policy (e.g. REPLICATE, PARTITION).
+ *
+ * @author John Blum
+ * @see org.springframework.data.gemfire.RegionFactoryBean
+ * @see org.springframework.data.gemfire.config.AbstractRegionParser
+ * @since 1.5.0
+ */
+class TemplateRegionParser extends AbstractRegionParser {
+
+ @Override
+ protected Class> getRegionFactoryClass() {
+ return RegionFactoryBean.class;
+ }
+
+ @Override
+ protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
+ boolean subRegion) {
+ BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
+ RegionAttributesFactoryBean.class);
+
+ doParseCommonRegionConfiguration(element, parserContext, builder, regionAttributesBuilder, subRegion);
+
+ builder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
+ }
+
+}
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 12e381ef..d54ed157 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
@@ -300,8 +300,7 @@ that have already been processed by the clients.
]]>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -434,15 +468,6 @@ The name of the region definition.]]>
-
-
-
-
-
-
-
-
-
@@ -549,6 +574,62 @@ CustomExpiry Time to idle (or idle timeout) configuration for the region entries
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
+Specifies the parent, template Region from which to inherit the Region attribute configuration.
+
-
-
-
-
-
-
-
-
-
-
@@ -724,7 +747,7 @@ The name of the region definition.]]>
-
+
@@ -821,35 +844,33 @@ use inner bean declarations.
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -861,6 +882,7 @@ use inner bean declarations.
+
@@ -875,27 +897,18 @@ use inner bean declarations.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -915,8 +928,7 @@ The id of the region bean definition.
]]>
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
-
@@ -1096,119 +1103,28 @@ The name of the region definition.
-
-
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -1466,8 +1382,138 @@ The name of the region definition.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1748,7 +1794,7 @@ The name of the bean defining the GemFire cache (by default 'gemfireCache').
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3056,10 +3115,8 @@ Deprecated as of Gemfire 7
-
-
+
+
-
+
diff --git a/src/test/java/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests.java b/src/test/java/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests.java
new file mode 100644
index 00000000..a9435df7
--- /dev/null
+++ b/src/test/java/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2010-2013 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 org.springframework.data.gemfire.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanIsAbstractException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer;
+import org.springframework.data.gemfire.test.support.CollectionUtils;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.util.ObjectUtils;
+
+import com.gemstone.gemfire.cache.CacheLoader;
+import com.gemstone.gemfire.cache.CacheLoaderException;
+import com.gemstone.gemfire.cache.EntryOperation;
+import com.gemstone.gemfire.cache.LoaderHelper;
+import com.gemstone.gemfire.cache.PartitionResolver;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.RegionAttributes;
+import com.gemstone.gemfire.cache.asyncqueue.AsyncEvent;
+import com.gemstone.gemfire.cache.asyncqueue.AsyncEventListener;
+import com.gemstone.gemfire.cache.partition.PartitionListenerAdapter;
+import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
+import com.gemstone.gemfire.cache.util.CacheWriterAdapter;
+
+/**
+ * The TemplateRegionsNamespaceTests class...
+ *
+ * @author John Blum
+ * @see org.junit.Test
+ * @see org.junit.runner.RunWith
+ * @see org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer
+ * @see org.springframework.test.context.ContextConfiguration
+ * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
+ * @since 1.5.0
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(initializers = GemfireTestApplicationContextInitializer.class)
+@SuppressWarnings("unused")
+public class TemplateRegionsNamespaceTests {
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ @Resource(name = "SimpleReplicateRegion")
+ private Region simpleReplicateRegion;
+
+ @SuppressWarnings("deprecation")
+ protected static void assertBaseRegionAttributes(final Region region) {
+ assertNotNull("The Region must not be null!", region);
+
+ RegionAttributes regionAttributes = region.getAttributes();
+
+ assertNotNull("The Region must have RegionAttributes defined!", regionAttributes);
+
+ assertTrue(regionAttributes.getCloningEnabled());
+ assertFalse(regionAttributes.getConcurrencyChecksEnabled());
+ assertTrue(regionAttributes.isDiskSynchronous());
+ assertTrue(regionAttributes.getIgnoreJTA());
+ assertEquals(Long.class, regionAttributes.getKeyConstraint());
+ assertEquals(0.90f, regionAttributes.getLoadFactor());
+ assertFalse(regionAttributes.getDataPolicy().withPersistence());
+ assertTrue(regionAttributes.getStatisticsEnabled());
+ assertEquals(String.class, regionAttributes.getValueConstraint());
+ assertTrue(regionAttributes.getIndexMaintenanceSynchronous());
+ assertTrue(ObjectUtils.isEmpty(regionAttributes.getCacheListeners()));
+ assertNotNull(regionAttributes.getCacheLoader());
+ assertTrue(regionAttributes.getCacheLoader() instanceof TestCacheLoader);
+ assertEquals("X", regionAttributes.getCacheLoader().toString());
+ assertNotNull(regionAttributes.getCacheWriter());
+ assertTrue(regionAttributes.getCacheWriter() instanceof TestCacheWriter);
+ assertEquals("Y", regionAttributes.getCacheWriter().toString());
+ }
+
+ @Test
+ public void testNoAbstractRegionTemplateBeans() {
+ String[] beanNames = {
+ "BaseRegion", "ExtendedRegionWithOverrides", "BaseReplicateRegion", "BasePartitionRegion", "BaseLocalRegion"
+ };
+
+ for (String beanName : beanNames) {
+ assertTrue(applicationContext.containsBean(beanName));
+ assertTrue(applicationContext.containsBeanDefinition(beanName));
+
+ try {
+ applicationContext.getBean(beanName);
+ fail(String.format("The abstract bean definition '%1$s' should not exists as a bean in the Spring context!",
+ beanName));
+ }
+ catch (BeansException ignore) {
+ assertTrue(ignore instanceof BeanIsAbstractException);
+ assertTrue(ignore.getMessage().contains(beanName));
+ }
+ }
+ }
+
+ @Test
+ public void testSimpleReplicateRegion() {
+ fail("Not Implemented!");
+ }
+
+ protected static interface Nameable {
+ void setName(String name);
+ }
+
+ protected static abstract class AbstractNameable implements Nameable {
+
+ private String name;
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ protected String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static final class TestAsyncEventListener extends AbstractNameable implements AsyncEventListener {
+
+ @Override public boolean processEvents(final List asyncEvents) {
+ return false;
+ }
+
+ @Override public void close() {
+
+ }
+ }
+
+ public static final class TestCacheListener extends CacheListenerAdapter {
+
+ private String name;
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static final class TestCacheLoader extends AbstractNameable implements CacheLoader {
+
+ @Override public Object load(final LoaderHelper loaderHelper) throws CacheLoaderException {
+ return null;
+ }
+
+ @Override public void close() {
+
+ }
+ }
+
+ public static final class TestCacheWriter extends CacheWriterAdapter {
+
+ private String name;
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static final class TestPartitionListener extends PartitionListenerAdapter {
+ private String name;
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static final class TestPartitionResolver extends AbstractNameable implements PartitionResolver {
+
+ @Override public Object getRoutingObject(final EntryOperation entryOperation) {
+ return null;
+ }
+
+ @Override public String getName() {
+ return super.getName();
+ }
+
+ @Override public void close() {
+ }
+ }
+
+}
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
new file mode 100644
index 00000000..a891af7e
--- /dev/null
+++ b/src/test/resources/org/springframework/data/gemfire/config/TemplateRegionsNamespaceTests-context.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+