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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +