Completed Client Cache Region Template testing for JIRA feature requests SGF-207 and SGF-254 adding support for template-defined Regions. In addition, fixed JIRA bug SGF-310 pertaining to the missing 'concurrency-level' attribute on the <gfe:client-region> element in the SDG XSD as well as SGF-311 involving the ClientRegionFactoryBean not properly setting the 'concurrencyChecksEnabled' property on GemFire's ClientRegionFactory class.

(cherry picked from commit 4bff3d9f7a)

Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2014-09-03 16:45:58 -07:00
parent a877bf55ce
commit 58c41f8e87
5 changed files with 336 additions and 19 deletions

View File

@@ -105,6 +105,7 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
// map region attributes onto the client region factory
if (attributes != null) {
factory.setCloningEnabled(attributes.getCloningEnabled());
factory.setConcurrencyChecksEnabled(attributes.getConcurrencyChecksEnabled());
factory.setConcurrencyLevel(attributes.getConcurrencyLevel());
factory.setCustomEntryIdleTimeout(attributes.getCustomEntryIdleTimeout());
factory.setCustomEntryTimeToLive(attributes.getCustomEntryTimeToLive());

View File

@@ -46,7 +46,7 @@ class ClientRegionParser extends AbstractRegionParser {
}
@Override
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder regionBuilder,
boolean subRegion) {
validateDataPolicyShortcutAttributesMutualExclusion(element, parserContext);
@@ -54,30 +54,32 @@ class ClientRegionParser extends AbstractRegionParser {
String resolvedCacheRef = ParsingUtils.resolveCacheReference(element.getAttribute("cache-ref"));
if (!subRegion) {
builder.addPropertyReference("cache", resolvedCacheRef);
ParsingUtils.setPropertyValue(element, builder, "close");
ParsingUtils.setPropertyValue(element, builder, "destroy");
regionBuilder.addPropertyReference("cache", resolvedCacheRef);
ParsingUtils.setPropertyValue(element, regionBuilder, "close");
ParsingUtils.setPropertyValue(element, regionBuilder, "destroy");
}
ParsingUtils.setPropertyValue(element, builder, "name");
ParsingUtils.setPropertyValue(element, builder, "data-policy", "dataPolicyName");
ParsingUtils.setPropertyValue(element, builder, "ignore-if-exists", "lookupEnabled");
ParsingUtils.setPropertyValue(element, builder, "persistent");
ParsingUtils.setPropertyValue(element, builder, "pool-name");
ParsingUtils.setPropertyValue(element, builder, "shortcut");
ParsingUtils.setPropertyValue(element, regionBuilder, "name");
ParsingUtils.setPropertyValue(element, regionBuilder, "data-policy", "dataPolicyName");
ParsingUtils.setPropertyValue(element, regionBuilder, "ignore-if-exists", "lookupEnabled");
ParsingUtils.setPropertyValue(element, regionBuilder, "persistent");
ParsingUtils.setPropertyValue(element, regionBuilder, "pool-name");
ParsingUtils.setPropertyValue(element, regionBuilder, "shortcut");
parseDiskStoreAttribute(element, builder);
parseDiskStoreAttribute(element, regionBuilder);
// Client RegionAttributes for overflow/eviction, expiration and statistics
BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
RegionAttributesFactoryBean.class);
mergeTemplateRegionAttributes(element, parserContext, regionBuilder, regionAttributesBuilder);
ParsingUtils.parseOptionalRegionAttributes(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseStatistics(element, regionAttributesBuilder);
ParsingUtils.parseExpiration(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseEviction(parserContext, element, regionAttributesBuilder);
builder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
regionBuilder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
List<Element> subElements = DomUtils.getChildElements(element);
@@ -87,16 +89,16 @@ class ClientRegionParser extends AbstractRegionParser {
String subElementLocalName = subElement.getLocalName();
if ("cache-listener".equals(subElementLocalName)) {
builder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, builder));
regionBuilder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
else if ("cache-loader".equals(subElementLocalName)) {
builder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, builder));
regionBuilder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
else if ("cache-writer".equals(subElementLocalName)) {
builder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, builder));
regionBuilder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
else if ("key-interest".equals(subElementLocalName)) {
interests.add(parseKeyInterest(subElement, parserContext));
@@ -107,7 +109,7 @@ class ClientRegionParser extends AbstractRegionParser {
}
if (!interests.isEmpty()) {
builder.addPropertyValue("interests", interests);
regionBuilder.addPropertyValue("interests", interests);
}
if (!subRegion) {

View File

@@ -1905,6 +1905,16 @@ The action to take when performing eviction.
</xsd:element>
<xsd:group ref="subRegionGroup" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="concurrency-level">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides an estimate of the maximum number of application threads that will concurrently access a Region entry
at one time. This attribute does not apply to Partitioned Regions. This attribute helps GemFire optimize the use of
system resources and reduce thread contention. This sets an initial parameter on the underlying
java.util.ConcurrentHashMap used for storing Region entries.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="data-policy" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -0,0 +1,246 @@
/*
* 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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import java.util.Arrays;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheLoaderException;
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.ExpirationAction;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.LoaderHelper;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
import com.gemstone.gemfire.cache.util.CacheWriterAdapter;
import com.gemstone.gemfire.cache.util.ObjectSizer;
/**
* The TemplateClientRegionNamespaceTest class is a test suite of test cases testing the contract and functionality
* of Client Region Templates using Spring Data GemFire XML namespace configuration meta-data.
*
* @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
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.Region
* @since 1.5.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = GemfireTestApplicationContextInitializer.class)
@SuppressWarnings("unused")
public class TemplateClientRegionNamespaceTest {
@Resource(name = "TemplateBasedClientRegion")
private Region<Integer, Object> templateBasedClientRegion;
protected void assertCacheListeners(final Region<?, ?> region, final String... expectedNames) {
assertNotNull(region);
assertNotNull(region.getAttributes());
assertNotNull(region.getAttributes().getCacheListeners());
assertEquals(expectedNames.length, region.getAttributes().getCacheListeners().length);
for (CacheListener cacheListener : region.getAttributes().getCacheListeners()) {
assertTrue(cacheListener instanceof TestCacheListener);
assertTrue(Arrays.asList(expectedNames).contains(cacheListener.toString()));
}
}
protected void assertCacheLoader(final Region<?, ?> region, final String expectedName) {
assertNotNull(region);
assertNotNull(region.getAttributes());
assertTrue(region.getAttributes().getCacheLoader() instanceof TestCacheLoader);
assertEquals(expectedName, region.getAttributes().getCacheLoader().toString());
}
protected void assertCacheWriter(final Region<?, ?> region, final String expectedName) {
assertNotNull(region);
assertNotNull(region.getAttributes());
assertTrue(region.getAttributes().getCacheWriter() instanceof TestCacheWriter);
assertEquals(expectedName, region.getAttributes().getCacheWriter().toString());
}
protected void assertDefaultEvictionAttributes(final EvictionAttributes evictionAttributes) {
assumeNotNull(evictionAttributes);
assertEvictionAttributes(evictionAttributes, EvictionAction.NONE, EvictionAlgorithm.NONE, 0, null);
}
protected void assertEvictionAttributes(final EvictionAttributes evictionAttributes,
final EvictionAction expectedAction,
final EvictionAlgorithm expectedAlgorithm,
final int expectedMaximum,
final ObjectSizer expectedObjectSizer)
{
assertNotNull("The 'EvictionAttributes' must not be null!", evictionAttributes);
assertEquals(expectedAction, evictionAttributes.getAction());
assertEquals(expectedAlgorithm, evictionAttributes.getAlgorithm());
assertEquals(expectedMaximum, evictionAttributes.getMaximum());
assertEquals(expectedObjectSizer, evictionAttributes.getObjectSizer());
}
protected void assertDefaultExpirationAttributes(final ExpirationAttributes expirationAttributes) {
assumeNotNull(expirationAttributes);
assertEquals(ExpirationAction.INVALIDATE, expirationAttributes.getAction());
assertEquals(0, expirationAttributes.getTimeout());
}
protected void assertExpirationAttributes(final ExpirationAttributes expirationAttributes,
final ExpirationAction expectedAction,
final int expectedTimeout)
{
assertNotNull("The 'ExpirationAttributes' must not be null!", expirationAttributes);
assertEquals(expectedAction, expirationAttributes.getAction());
assertEquals(expectedTimeout, expirationAttributes.getTimeout());
}
protected void assertDefaultRegionAttributes(final Region region) {
assertNotNull("The Region must not be null!", region);
assertNotNull(String.format("The Region (%1$s) must have 'RegionAttributes' defined!",
region.getFullPath()), region.getAttributes());
assertNull(region.getAttributes().getCompressor());
assertNull(region.getAttributes().getCustomEntryIdleTimeout());
assertNull(region.getAttributes().getCustomEntryTimeToLive());
assertNull(region.getAttributes().getDiskStoreName());
assertFalse(region.getAttributes().getEnableGateway());
assertNullEmpty(region.getAttributes().getGatewayHubId());
assertFalse(region.getAttributes().getMulticastEnabled());
assertDefaultExpirationAttributes(region.getAttributes().getRegionTimeToLive());
assertDefaultExpirationAttributes(region.getAttributes().getRegionIdleTimeout());
}
protected static void assertEmpty(final Object[] array) {
assertTrue((array == null || array.length == 0));
}
protected static void assertEmpty(final Iterable<?> collection) {
assertTrue(collection == null || !collection.iterator().hasNext());
}
protected static void assertNullEmpty(final String value) {
assertFalse(StringUtils.hasText(value));
}
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());
}
@Test
public void testTemplateBasedClientRegion() {
assertRegionMetaData(templateBasedClientRegion, "TemplateBasedClientRegion");
assertDefaultRegionAttributes(templateBasedClientRegion);
assertCacheListeners(templateBasedClientRegion, "XYZ");
assertCacheLoader(templateBasedClientRegion, "A");
assertCacheWriter(templateBasedClientRegion, "B");
assertFalse(templateBasedClientRegion.getAttributes().getCloningEnabled());
assertFalse(templateBasedClientRegion.getAttributes().getConcurrencyChecksEnabled());
assertEquals(16, templateBasedClientRegion.getAttributes().getConcurrencyLevel());
assertEquals(DataPolicy.NORMAL, templateBasedClientRegion.getAttributes().getDataPolicy());
assertFalse(templateBasedClientRegion.getAttributes().isDiskSynchronous());
assertEvictionAttributes(templateBasedClientRegion.getAttributes().getEvictionAttributes(),
EvictionAction.OVERFLOW_TO_DISK, EvictionAlgorithm.LRU_ENTRY, 1024, null);
assertEquals(51, templateBasedClientRegion.getAttributes().getInitialCapacity());
assertEquals(Integer.class, templateBasedClientRegion.getAttributes().getKeyConstraint());
assertEquals("0.85", String.valueOf(templateBasedClientRegion.getAttributes().getLoadFactor()));
assertEquals("ServerPool", templateBasedClientRegion.getAttributes().getPoolName());
assertTrue(templateBasedClientRegion.getAttributes().getStatisticsEnabled());
assertEquals(Object.class, templateBasedClientRegion.getAttributes().getValueConstraint());
templateBasedClientRegion.getInterestList();
}
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 implements CacheLoader {
private String name;
public void setName(final String name) {
this.name = name;
}
@Override
public Object load(final LoaderHelper loaderHelper) throws CacheLoaderException {
return null;
}
@Override
public void close() {
}
@Override
public String toString() {
return name;
}
}
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;
}
}
}

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<gfe:pool id="ServerPool">
<gfe:server host="localhost" port="40404"/>
</gfe:pool>
<gfe:client-cache/>
<!-- Template Regions -->
<gfe:region-template id="BaseRegionTemplate" cloning-enabled="true" concurrency-checks-enabled="false"
disk-synchronous="false" initial-capacity="51" key-constraint="java.lang.Long"
load-factor="0.85" persistent="false" statistics="true" value-constraint="java.lang.String">
<gfe:cache-listener>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheListener" p:name="X"/>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheListener" p:name="Y"/>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheListener" p:name="Z"/>
</gfe:cache-listener>
<gfe:entry-ttl timeout="300" action="INVALIDATE"/>
<gfe:entry-tti timeout="600" action="DESTROY"/>
</gfe:region-template>
<gfe:client-region-template id="ClientRegionTemplate" concurrency-level="4" pool-name="ServerPool" template="BaseRegionTemplate">
<gfe:cache-loader>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheLoader" p:name="A"/>
</gfe:cache-loader>
<gfe:cache-writer>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheWriter" p:name="B"/>
</gfe:cache-writer>
<!--
<gfe:key-interest durable="false" result-policy="KEYS_VALUES" receive-values="true">
<bean class="java.lang.String">
<constructor-arg value="ALL_KEYS"/>
</bean>
</gfe:key-interest>
<gfe:regex-interest pattern=".*" receive-values="false"/>
-->
<gfe:eviction type="ENTRY_COUNT" threshold="1024" action="LOCAL_DESTROY"/>
</gfe:client-region-template>
<gfe:client-region id="TemplateBasedClientRegion" cloning-enabled="false" concurrency-level="16"
key-constraint="java.lang.Integer" value-constraint="java.lang.Object"
template="ClientRegionTemplate">
<gfe:cache-listener>
<bean class="org.springframework.data.gemfire.config.TemplateClientRegionNamespaceTest$TestCacheListener" p:name="XYZ"/>
</gfe:cache-listener>
<gfe:eviction type="ENTRY_COUNT" threshold="1024" action="OVERFLOW_TO_DISK"/>
</gfe:client-region>
</beans>