Merge pull request #28 from jxblum/sgf195-fix

Sgf195 fix
This commit is contained in:
David Turanski
2013-09-30 05:11:11 -07:00
9 changed files with 261 additions and 132 deletions

View File

@@ -24,7 +24,9 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.SubRegionFactoryBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -35,11 +37,23 @@ import org.w3c.dom.Element;
*
* @author David Turanski
*/
abstract class AbstractRegionParser extends AliasReplacingBeanDefinitionParser {
abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
protected final Log log = LogFactory.getLog(getClass());
@Override
protected void doParseInternal(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
protected Class<?> getBeanClass(Element element) {
return (isSubRegion(element) ? SubRegionFactoryBean.class : getRegionFactoryClass());
}
protected abstract Class<?> getRegionFactoryClass();
protected boolean isSubRegion(Element element) {
return element.getParentNode().getLocalName().endsWith("region");
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
boolean subRegion = isSubRegion(element);
@@ -196,4 +210,4 @@ abstract class AbstractRegionParser extends AliasReplacingBeanDefinitionParser {
String name = element.getAttribute(NAME_ATTRIBUTE);
return StringUtils.hasText(name) ? name : element.getAttribute(ID_ATTRIBUTE);
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012 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.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.SubRegionFactoryBean;
import org.w3c.dom.Element;
/**
* Extension class dealing with the attribute clash (name) that triggers the
* region name to be considered a bean alias. Overrides the automatic alias
* detection and replaces it with its own using meta attributes (since the
* parsing method is final).
*
* @author Costin Leau
* @author David Turanski
*/
abstract class AliasReplacingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
if (isSubRegion(element)) {
return SubRegionFactoryBean.class;
}
else {
return getRegionFactoryClass();
}
}
protected abstract Class<?> getRegionFactoryClass();
@Override
protected final void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
ParsingUtils.addBeanAliasAsMetadata(element, builder);
doParseInternal(element, parserContext, builder);
}
@Override
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
// add the aliases from the metadata
super.registerBeanDefinition(ParsingUtils.replaceBeanAliasAsMetadata(definition), registry);
}
protected boolean isSubRegion(Element element) {
return element.getParentNode().getLocalName().endsWith("region");
}
protected abstract void doParseInternal(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
}

View File

@@ -39,15 +39,17 @@ import com.gemstone.gemfire.cache.DataPolicy;
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
*/
class ClientRegionParser extends AliasReplacingBeanDefinitionParser {
class ClientRegionParser extends AbstractRegionParser {
@Override
protected Class<?> getRegionFactoryClass() {
return ClientRegionFactoryBean.class;
}
@Override
protected void doParseInternal(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
// set scope
@@ -131,6 +133,14 @@ class ClientRegionParser extends AliasReplacingBeanDefinitionParser {
}
}
@Override
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
boolean subRegion) {
throw new UnsupportedOperationException(String.format(
"doParseRegion(:Element, :ParserContext, :BeanDefinitionBuilder, subRegion:boolean) is not supported on %1$s",
getClass().getName()));
}
private Object parseCacheListener(ParserContext parserContext, Element subElement, BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, builder);
}
@@ -159,4 +169,4 @@ class ClientRegionParser extends AliasReplacingBeanDefinitionParser {
ParsingUtils.setPropertyValue(element, builder, "result-policy", "policy");
ParsingUtils.setPropertyValue(element, builder, "receive-values", "receiveValues");
}
}
}

View File

@@ -25,23 +25,23 @@ import org.springframework.data.gemfire.FixedPartitionAttributesFactoryBean;
import org.springframework.data.gemfire.PartitionAttributesFactoryBean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.gemstone.gemfire.management.internal.cli.parser.ParserUtils;
/**
* Parser for &lt;partitioned-region;gt; definitions.
*
*
* To avoid eager evaluations, the region attributes are declared as a nested
* definition.
*
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
*/
class PartitionedRegionParser extends AbstractRegionParser {
@Override
protected Class<?> getRegionFactoryClass() {
return PartitionedRegionFactoryBean.class;
@@ -53,41 +53,40 @@ class PartitionedRegionParser extends AbstractRegionParser {
boolean subRegion) {
super.doParse(element, builder);
BeanDefinitionBuilder attrBuilder = subRegion ? builder : BeanDefinitionBuilder
.genericBeanDefinition(RegionAttributesFactoryBean.class);
BeanDefinitionBuilder regionAttributesBuilder = (subRegion ? builder
: BeanDefinitionBuilder.genericBeanDefinition(RegionAttributesFactoryBean.class));
super.doParseCommonRegionConfiguration(element, parserContext, builder, regionAttributesBuilder, subRegion);
BeanDefinitionBuilder partitionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
PartitionAttributesFactoryBean.class);
parseColocatedWith(element, builder, partitionAttributesBuilder, "colocated-with");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "local-max-memory");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "copies","redundantCopies");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "recovery-delay");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "startup-recovery-delay");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "total-max-memory");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "total-buckets","totalNumBuckets");
super.doParseCommonRegionConfiguration(element, parserContext, builder, attrBuilder, subRegion);
//
// partition attributes
BeanDefinitionBuilder parAttrBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PartitionAttributesFactoryBean.class);
ParsingUtils.setPropertyValue(element, parAttrBuilder, "colocated-with");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "local-max-memory");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "copies","redundantCopies");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "recovery-delay");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "startup-recovery-delay");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "total-max-memory");
ParsingUtils.setPropertyValue(element, parAttrBuilder, "total-buckets","totalNumBuckets");
//
Element subElement = DomUtils.getChildElementByTagName(element, "partition-resolver");
// parse nested partition resolver element
if (subElement != null) {
parAttrBuilder.addPropertyValue("partitionResolver",
parsePartitionResolver(parserContext, subElement, builder));
partitionAttributesBuilder.addPropertyValue("partitionResolver",
parsePartitionResolver(parserContext, subElement, builder));
}
subElement = DomUtils.getChildElementByTagName(element, "partition-listener");
// parse nested partition resolver element
// parse nested partition listener element
if (subElement != null) {
parAttrBuilder.addPropertyValue("partitionListeners",
parsePartitionListeners(parserContext, subElement, builder));
partitionAttributesBuilder.addPropertyValue("partitionListeners",
parsePartitionListeners(parserContext, subElement, builder));
}
List<Element> fixedPartitions = DomUtils.getChildElementsByTagName(element, "fixed-partition");
if (! CollectionUtils.isEmpty(fixedPartitions)){
@SuppressWarnings("rawtypes")
if (!CollectionUtils.isEmpty(fixedPartitions)){
@SuppressWarnings("rawtypes")
ManagedList fixedPartitionAttributes = new ManagedList();
for (Element fp: fixedPartitions) {
BeanDefinitionBuilder fpaBuilder = BeanDefinitionBuilder.genericBeanDefinition(FixedPartitionAttributesFactoryBean.class);
@@ -96,14 +95,31 @@ class PartitionedRegionParser extends AbstractRegionParser {
ParsingUtils.setPropertyValue(fp, fpaBuilder, "primary");
fixedPartitionAttributes.add(fpaBuilder.getBeanDefinition());
}
parAttrBuilder.addPropertyValue("fixedPartitionAttributes", fixedPartitionAttributes);
partitionAttributesBuilder.addPropertyValue("fixedPartitionAttributes", fixedPartitionAttributes);
}
// // add partition attributes attributes
attrBuilder.addPropertyValue("partitionAttributes", parAttrBuilder.getBeanDefinition());
// add partition/overflow settings as attributes
// add partition attributes to region attributes
regionAttributesBuilder.addPropertyValue("partitionAttributes", partitionAttributesBuilder.getBeanDefinition());
// add partition/overflow settings as attributes to Region (via PartitionRegionFactoryBean -> RegionFactoryBean)
if (!subRegion) {
builder.addPropertyValue("attributes", attrBuilder.getBeanDefinition());
builder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
}
}
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
// PartitionAttributesFactoryBean with a reference to the Region "this" Partitioned Region will be colocated
// with, where the colocated-with attribute refers to the the bean name/alias of the other, depended on Region
// providing that the Region's name is also a bean alias of the bean definition.
//ParsingUtils.setPropertyReference(element, partitionAttributesBuilder, attributeName, "colocatedWith");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, attributeName);
String colocatedWithBeanAlias = element.getAttribute(attributeName);
if (StringUtils.hasText(colocatedWithBeanAlias)) {
regionBuilder.addDependsOn(colocatedWithBeanAlias);
}
}
@@ -115,4 +131,5 @@ class PartitionedRegionParser extends AbstractRegionParser {
BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, builder);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
import static org.junit.Assert.*;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.gemstone.gemfire.cache.Region;
/**
* The ColocatedRegionIntegrationTest class is a test suite class containing test cases for JIRA issue SGF-195,
* concerning colocated Regions in GemFire.
* <p/>
* @author John Blum
* @link https://jira.springsource.org/browse/SGF-195
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.3.3
*/
@ContextConfiguration("/org/springframework/data/gemfire/colocated-region.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@SuppressWarnings("unused")
public class ColocatedRegionIntegrationTest {
@Resource(name = "colocatedRegion")
private Region colocatedRegion;
@Resource(name = "sourceRegion")
private Region sourceRegion;
protected static void assertRegionExists(final String expectedRegionName, final Region region) {
assertNotNull(region);
assertEquals(String.format("Expected Region with name %1$s; but was %2$s!",
expectedRegionName, region.getName()), expectedRegionName, region.getName());
}
@Test
public void testRegionsColocated() {
assertRegionExists("Source", sourceRegion);
assertRegionExists("Colocated", colocatedRegion);
assertNotNull(colocatedRegion.getAttributes());
assertNotNull(colocatedRegion.getAttributes().getPartitionAttributes());
assertEquals(sourceRegion.getName(), colocatedRegion.getAttributes().getPartitionAttributes().getColocatedWith());
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import com.gemstone.gemfire.cache.PartitionAttributes;
import com.gemstone.gemfire.cache.PartitionResolver;
/**
* The PartitionAttributesFactoryBeanTest class is test suite of test cases testing the contract and functionality of
* the PartitionAttributesFactoryBean class.
* <p/>
* @author John Blum
* @see org.junit.Test
* @see org.springframework.data.gemfire.PartitionAttributesFactoryBean
* @since 1.3.3
*/
@SuppressWarnings("unused")
public class PartitionAttributesFactoryBeanTest {
protected PartitionResolver createMockPartitionResolver(final String name) {
PartitionResolver partitionResolver = mock(PartitionResolver.class);
when(partitionResolver.getName()).thenReturn(name);
return partitionResolver;
}
@Test
public void testSetBasicProperties() throws Exception {
PartitionAttributesFactoryBean partitionAttributesFactoryBean = new PartitionAttributesFactoryBean();
partitionAttributesFactoryBean.setColocatedWith("mockColocatedRegion");
partitionAttributesFactoryBean.setLocalMaxMemory(1024);
partitionAttributesFactoryBean.setPartitionResolver(createMockPartitionResolver("mockPartitionResolver"));
partitionAttributesFactoryBean.setRecoveryDelay(1000l);
partitionAttributesFactoryBean.setRedundantCopies(1);
partitionAttributesFactoryBean.setStartupRecoveryDelay(60000l);
partitionAttributesFactoryBean.setTotalMaxMemory(8192l);
partitionAttributesFactoryBean.setTotalNumBuckets(42);
PartitionAttributes partitionAttributes = partitionAttributesFactoryBean.getObject();
assertNotNull(partitionAttributes);
assertEquals("mockColocatedRegion", partitionAttributes.getColocatedWith());
assertEquals(1024, partitionAttributes.getLocalMaxMemory());
assertNotNull(partitionAttributes.getPartitionResolver());
assertEquals("mockPartitionResolver", partitionAttributes.getPartitionResolver().getName());
assertEquals(1000l, partitionAttributes.getRecoveryDelay());
assertEquals(1, partitionAttributes.getRedundantCopies());
assertEquals(60000l, partitionAttributes.getStartupRecoveryDelay());
assertEquals(8192l, partitionAttributes.getTotalMaxMemory());
assertEquals(42, partitionAttributes.getTotalNumBuckets());
}
}

View File

@@ -32,8 +32,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.SimpleObjectSizer;
import org.springframework.data.gemfire.TestUtils;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.client.Interest;
import org.springframework.data.gemfire.client.RegexInterest;
import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -44,7 +42,6 @@ import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.EvictionAction;
import com.gemstone.gemfire.cache.EvictionAlgorithm;
import com.gemstone.gemfire.cache.EvictionAttributes;
import com.gemstone.gemfire.cache.InterestResultPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.util.ObjectSizer;
@@ -82,7 +79,11 @@ public class ClientRegionNamespaceTest {
@Test
public void testBeanNames() throws Exception {
assertTrue(ObjectUtils.isEmpty(context.getAliases("publisher")));
assertTrue(context.containsBean("SimpleRegion"));
assertTrue(context.containsBean("publisher"));
assertTrue(context.containsBean("ComplexRegion"));
assertTrue(context.containsBean("PersistentRegion"));
assertTrue(context.containsBean("OverflowRegion"));
}
@SuppressWarnings("rawtypes")
@@ -134,4 +135,4 @@ public class ClientRegionNamespaceTest {
ObjectSizer sizer = evicAttr.getObjectSizer();
assertEquals(SimpleObjectSizer.class, sizer.getClass());
}
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire-1.3.xsd">
<gfe:cache/>
<!-- NOTE the order of Region bean definitions is important in reproducing the issue in JIRA 195 -->
<gfe:partitioned-region id="colocatedRegion" name="Colocated" colocated-with="Source"/>
<gfe:partitioned-region id="sourceRegion" name="Source"/>
</beans>

View File

@@ -10,12 +10,12 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<gfe:client-cache />
<gfe:client-region id="simple" />
<gfe:client-region id="simple" name="SimpleRegion"/>
<gfe:client-region id="empty" pool-name="gemfire-pool" name="publisher" data-policy="empty" close="true" destroy="false"/>
<gfe:client-region id="complex" pool-name="gemfire-pool" close="true" destroy="false" load-factor="0.5" statistics="true">
<gfe:client-region id="complex" name="ComplexRegion" pool-name="gemfire-pool" close="true" destroy="false" load-factor="0.5" statistics="true">
<gfe:cache-listener>
<ref bean="c-listener"/>
<bean class="org.springframework.data.gemfire.SimpleCacheListener"/>
@@ -23,20 +23,20 @@
<gfe:entry-ttl action="INVALIDATE" timeout="500"/>
<gfe:eviction threshold="5"/>
</gfe:client-region>
<bean id="c-listener" class="org.springframework.data.gemfire.SimpleCacheListener"/>
<gfe:pool id="gemfire-pool" subscription-enabled="false">
<gfe:locator host="localhost" port="40403"/>
</gfe:pool>
<gfe:client-region id="persistent" pool-name="gemfire-pool" persistent="true" disk-store-ref="diskStore" disk-synchronous="true"/>
<gfe:disk-store id="diskStore" queue-size="50" auto-compact="true" max-oplog-size="10" time-interval="9999">
<gfe:disk-dir location="./" max-size="1"/>
</gfe:disk-store>
<gfe:client-region id="overflow" pool-name="gemfire-pool" disk-store-ref="diskStore">
<gfe:client-region id="persistent" name="PersistentRegion" pool-name="gemfire-pool" persistent="true" disk-store-ref="diskStore" disk-synchronous="true"/>
<gfe:disk-store id="diskStore" queue-size="50" auto-compact="true" max-oplog-size="10" time-interval="9999">
<gfe:disk-dir location="./" max-size="1"/>
</gfe:disk-store>
<gfe:client-region id="overflow" name="OverflowRegion" pool-name="gemfire-pool" disk-store-ref="diskStore">
<gfe:eviction type="MEMORY_SIZE" threshold="10" action="OVERFLOW_TO_DISK">
<gfe:object-sizer>
<bean class="org.springframework.data.gemfire.SimpleObjectSizer"/>
@@ -44,4 +44,4 @@
</gfe:eviction>
</gfe:client-region>
</beans>
</beans>