SGF-196 - Support adding CacheListeners, CacheLoaders and CacheWriters, along with other mutable Region attributes to an existing Region.

This commit is contained in:
John Blum
2015-04-07 19:27:15 -07:00
parent b9881dfad9
commit b42ca233f0
9 changed files with 959 additions and 35 deletions

View File

@@ -16,15 +16,217 @@
package org.springframework.data.gemfire;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.gemstone.gemfire.cache.AttributesMutator;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheWriter;
import com.gemstone.gemfire.cache.CustomExpiry;
import com.gemstone.gemfire.cache.EvictionAttributesMutator;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue;
import com.gemstone.gemfire.cache.wan.GatewaySender;
/**
* The LookupRegionFactoryBean class is a concrete implementation of RegionLookupFactoryBean for handling &
* gt;gfe:lookup-region/&lt SDG XML namespace (XSD) elements.
* The LookupRegionFactoryBean class is a concrete implementation of RegionLookupFactoryBean for handling
* >gfe:lookup-region/< SDG XML namespace (XSD) elements.
*
* @author John Blum
* @see org.springframework.data.gemfire.RegionLookupFactoryBean
* @see com.gemstone.gemfire.cache.AttributesMutator
* @since 1.6.0
*/
@SuppressWarnings("unused")
public class LookupRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> {
private Boolean cloningEnabled;
private Boolean enableStatistics;
private AsyncEventQueue[] asyncEventQueues;
private CacheListener<K, V>[] cacheListeners;
private CacheLoader<K, V> cacheLoader;
private CacheWriter<K, V> cacheWriter;
private CustomExpiry<K, V> customEntryIdleTimeout;
private CustomExpiry<K, V> customEntryTimeToLive;
private ExpirationAttributes entryIdleTimeout;
private ExpirationAttributes entryTimeToLive;
private ExpirationAttributes regionIdleTimeout;
private ExpirationAttributes regionTimeToLive;
private GatewaySender[] gatewaySenders;
private Integer evictionMaximum;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
AttributesMutator<K, V> attributesMutator = getRegion().getAttributesMutator();
if (!ObjectUtils.isEmpty(asyncEventQueues)) {
for (AsyncEventQueue asyncEventQueue : asyncEventQueues) {
attributesMutator.addAsyncEventQueueId(asyncEventQueue.getId());
}
}
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> cacheListener : cacheListeners) {
attributesMutator.addCacheListener(cacheListener);
}
}
if (cacheLoader != null) {
attributesMutator.setCacheLoader(cacheLoader);
}
if (cacheWriter != null) {
attributesMutator.setCacheWriter(cacheWriter);
}
if (cloningEnabled != null) {
attributesMutator.setCloningEnabled(cloningEnabled);
}
if (isStatisticsEnabled()) {
assertStatisticsEnabled();
if (customEntryIdleTimeout != null) {
attributesMutator.setCustomEntryIdleTimeout(customEntryIdleTimeout);
}
if (customEntryTimeToLive != null) {
attributesMutator.setCustomEntryTimeToLive(customEntryTimeToLive);
}
if (entryIdleTimeout != null) {
attributesMutator.setEntryIdleTimeout(entryIdleTimeout);
}
if (entryTimeToLive != null) {
attributesMutator.setEntryTimeToLive(entryTimeToLive);
}
if (regionIdleTimeout != null) {
attributesMutator.setRegionIdleTimeout(regionIdleTimeout);
}
if (regionTimeToLive != null) {
attributesMutator.setRegionTimeToLive(regionTimeToLive);
}
}
if (evictionMaximum != null) {
EvictionAttributesMutator evictionAttributesMutator = attributesMutator.getEvictionAttributesMutator();
evictionAttributesMutator.setMaximum(evictionMaximum);
}
if (!ObjectUtils.isEmpty(gatewaySenders)) {
for (GatewaySender gatewaySender : gatewaySenders) {
attributesMutator.addGatewaySenderId(gatewaySender.getId());
}
}
}
@Override
final boolean isLookupEnabled() {
return true;
}
/* (non-Javadoc) */
public void setAsyncEventQueues(AsyncEventQueue[] asyncEventQueues) {
this.asyncEventQueues = asyncEventQueues;
}
/* (non-Javadoc) */
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
this.cacheListeners = cacheListeners;
}
/* (non-Javadoc) */
public void setCacheLoader(CacheLoader<K, V> cacheLoader) {
this.cacheLoader = cacheLoader;
}
/* (non-Javadoc) */
public void setCacheWriter(CacheWriter<K, V> cacheWriter) {
this.cacheWriter = cacheWriter;
}
/* (non-Javadoc) */
public void setCloningEnabled(Boolean cloningEnabled) {
this.cloningEnabled = cloningEnabled;
}
/* (non-Javadoc) */
public void setCustomEntryIdleTimeout(CustomExpiry<K, V> customEntryIdleTimeout) {
setStatisticsEnabled(customEntryIdleTimeout != null);
this.customEntryIdleTimeout = customEntryIdleTimeout;
}
/* (non-Javadoc) */
public void setCustomEntryTimeToLive(CustomExpiry<K, V> customEntryTimeToLive) {
setStatisticsEnabled(customEntryTimeToLive != null);
this.customEntryTimeToLive = customEntryTimeToLive;
}
/* (non-Javadoc) */
public void setEntryIdleTimeout(ExpirationAttributes entryIdleTimeout) {
setStatisticsEnabled(entryIdleTimeout != null);
this.entryIdleTimeout = entryIdleTimeout;
}
/* (non-Javadoc) */
public void setEntryTimeToLive(ExpirationAttributes entryTimeToLive) {
setStatisticsEnabled(entryTimeToLive != null);
this.entryTimeToLive = entryTimeToLive;
}
/* (non-Javadoc) */
public void setEvictionMaximum(final Integer evictionMaximum) {
this.evictionMaximum = evictionMaximum;
}
/* (non-Javadoc) */
public void setGatewaySenders(GatewaySender[] gatewaySenders) {
this.gatewaySenders = gatewaySenders;
}
/* (non-Javadoc) */
public void setRegionIdleTimeout(ExpirationAttributes regionIdleTimeout) {
setStatisticsEnabled(regionIdleTimeout != null);
this.regionIdleTimeout = regionIdleTimeout;
}
/* (non-Javadoc) */
public void setRegionTimeToLive(ExpirationAttributes regionTimeToLive) {
setStatisticsEnabled(regionTimeToLive != null);
this.regionTimeToLive = regionTimeToLive;
}
/* (non-Javadoc) */
public void setStatisticsEnabled(Boolean enableStatistics) {
this.enableStatistics = enableStatistics;
}
/* (non-Javadoc) */
protected boolean isStatisticsEnabled() {
return Boolean.TRUE.equals(this.enableStatistics);
}
/* (non-Javadoc) */
private void assertStatisticsEnabled() {
Region localRegion = getRegion();
Assert.state(localRegion.getAttributes().getStatisticsEnabled(), String.format(
"Statistics for Region '%1$s' must be enabled to change Entry & Region TTL/TTI Expiration settings",
localRegion.getFullPath()));
}
}

View File

@@ -29,11 +29,15 @@ import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
/**
* Simple FactoryBean for retrieving generic GemFire {@link Region}s. If the Region does not exist,
* an exception is thrown. For declaring and configuring new regions, see {@link RegionFactoryBean}.
* Simple FactoryBean for retrieving generic GemFire {@link Region}s. If lookups are not enabled or the Region
* does not exist, an exception is thrown. For declaring and configuring new Regions, see {@link RegionFactoryBean}.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanNameAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see com.gemstone.gemfire.cache.Region
*/
@SuppressWarnings("unused")
public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>, InitializingBean, BeanNameAware {
@@ -45,19 +49,19 @@ public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Regio
private GemFireCache cache;
private Region<?, ?> parent;
private Region<K, V> region;
private volatile Region<K, V> region;
private String beanName;
private String name;
private String regionName;
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "The 'cache' property must be set.");
Assert.notNull(cache, "the 'cache' reference property must be set");
String regionName = (StringUtils.hasText(this.regionName) ? this.regionName
: (StringUtils.hasText(name) ? name : beanName));
Assert.hasText(regionName, "The 'regionName', 'name' or 'beanName' property must be set.");
Assert.hasText(regionName, "'regionName', 'name' or 'beanName' property must be set");
synchronized (cache) {
//region = (getParent() != null ? getParent().getSubregion(regionName) : cache.getRegion(regionName));
@@ -71,7 +75,7 @@ public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Regio
}
if (region != null) {
log.info(String.format("Retrieved Region [%1$s] from Cache [%2$s].", regionName, cache.getName()));
log.info(String.format("found Region (%1$s) in Cache (%2$s)", regionName, cache.getName()));
}
else {
region = lookupFallback(cache, regionName);
@@ -93,11 +97,12 @@ public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Regio
}
public Region<K, V> getObject() throws Exception {
return region;
return getRegion();
}
public Class<?> getObjectType() {
return (region != null ? region.getClass() : Region.class);
Region localRegion = getRegion();
return (localRegion != null ? localRegion.getClass() : Region.class);
}
public boolean isSingleton() {
@@ -158,6 +163,16 @@ public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Regio
return parent;
}
/**
* Gets the reference to the GemFire Region obtained by this Spring FactoryBean during the lookup operation.
*
* @return a reference to the GemFire Region found during lookup.
* @see com.gemstone.gemfire.cache.Region
*/
protected Region<K, V> getRegion() {
return region;
}
/**
* Sets the name of the Cache Region as expected by GemFire. If no Region is found with the given name, a new one
* will be created. If no name is given, the value of the 'name' property will be used.
@@ -170,20 +185,19 @@ public abstract class RegionLookupFactoryBean<K, V> implements FactoryBean<Regio
this.regionName = regionName;
}
private boolean isLookupEnabled() {
/* (non-Javadoc) */
boolean isLookupEnabled() {
return Boolean.TRUE.equals(getLookupEnabled());
}
public Boolean getLookupEnabled() {
return lookupEnabled;
}
/* (non-Javadoc) */
public void setLookupEnabled(Boolean lookupEnabled) {
this.lookupEnabled = lookupEnabled;
}
protected Region<K, V> getRegion() {
return region;
/* (non-Javadoc) */
public Boolean getLookupEnabled() {
return lookupEnabled;
}
}

View File

@@ -36,6 +36,9 @@ import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue;
import com.gemstone.gemfire.cache.wan.GatewaySender;
/**
* Abstract base class encapsulating functionality common to all Region Parsers.
*
@@ -51,14 +54,14 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return getRegionFactoryClass();
}
protected abstract Class<?> 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 isRegionTemplate(final Element element) {
String localName = element.getLocalName();
return (localName != null && localName.endsWith("-template"));
@@ -135,25 +138,26 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
ParsingUtils.setPropertyValue(element, regionBuilder, "hub-id");
parseCollectionOfCustomSubElements(element, parserContext, regionBuilder,
"com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue", "async-event-queue", "asyncEventQueues");
parseCollectionOfCustomSubElements(element, parserContext, regionBuilder,
"com.gemstone.gemfire.cache.wan.GatewaySender", "gateway-sender","gatewaySenders");
parseCollectionOfCustomSubElements(element, parserContext, regionBuilder, AsyncEventQueue.class.getName(),
"async-event-queue", "asyncEventQueues");
parseCollectionOfCustomSubElements(element, parserContext, regionBuilder, GatewaySender.class.getName(),
"gateway-sender", "gatewaySenders");
List<Element> subElements = DomUtils.getChildElements(element);
for (Element subElement : subElements) {
if (subElement.getLocalName().equals("cache-listener")) {
regionBuilder.addPropertyValue("cacheListeners",
ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, regionBuilder));
regionBuilder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
else if (subElement.getLocalName().equals("cache-loader")) {
regionBuilder.addPropertyValue("cacheLoader",
ParsingUtils.parseRefOrSingleNestedBeanDeclaration(parserContext, subElement, regionBuilder));
regionBuilder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
else if (subElement.getLocalName().equals("cache-writer")) {
regionBuilder.addPropertyValue("cacheWriter",
ParsingUtils.parseRefOrSingleNestedBeanDeclaration(parserContext, subElement, regionBuilder));
regionBuilder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
}
}
@@ -200,7 +204,7 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return (regionAttributes instanceof BeanDefinition ? (BeanDefinition) regionAttributes : null);
}
private void parseCollectionOfCustomSubElements(Element element, ParserContext parserContext,
protected void parseCollectionOfCustomSubElements(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder, String className, String subElementName, String propertyName) {
List<Element> subElements = DomUtils.getChildElementsByTagName(element, subElementName,
subElementName + "-ref");

View File

@@ -19,14 +19,20 @@ 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.LookupRegionFactoryBean;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue;
import com.gemstone.gemfire.cache.wan.GatewaySender;
/**
* Parser for &lt;lookup-region;gt; definitions.
* Parser for GFE &lt;lookup-region&gt; bean definitions.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.data.gemfire.LookupRegionFactoryBean
* @see org.springframework.data.gemfire.config.AbstractRegionParser
*/
class LookupRegionParser extends AbstractRegionParser {
@@ -38,12 +44,44 @@ class LookupRegionParser extends AbstractRegionParser {
@Override
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
boolean subRegion) {
super.doParse(element, builder);
String resolvedCacheRef = ParsingUtils.resolveCacheReference(element.getAttribute("cache-ref"));
builder.addPropertyReference("cache", resolvedCacheRef);
ParsingUtils.setPropertyValue(element, builder, "name", "name");
ParsingUtils.setPropertyValue(element, builder, "cloning-enabled");
ParsingUtils.setPropertyValue(element, builder, "eviction-maximum");
ParsingUtils.setPropertyValue(element, builder, "name");
ParsingUtils.parseExpiration(parserContext, element, builder);
parseCollectionOfCustomSubElements(element, parserContext, builder, AsyncEventQueue.class.getName(),
"async-event-queue", "asyncEventQueues");
parseCollectionOfCustomSubElements(element, parserContext, builder, GatewaySender.class.getName(),
"gateway-sender", "gatewaySenders");
Element cacheListenerElement = DomUtils.getChildElementByTagName(element, "cache-listener");
if (cacheListenerElement != null) {
builder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext,
cacheListenerElement, builder));
}
Element cacheLoaderElement = DomUtils.getChildElementByTagName(element, "cache-loader");
if (cacheLoaderElement != null) {
builder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, cacheLoaderElement, builder));
}
Element cacheWriterElement = DomUtils.getChildElementByTagName(element, "cache-writer");
if (cacheWriterElement != null) {
builder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, cacheWriterElement, builder));
}
if (!subRegion) {
parseSubRegions(element, parserContext, resolvedCacheRef);

View File

@@ -448,7 +448,174 @@ Defines a lookup Subregion
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="basicRegionType">
<xsd:group ref="subRegionGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:sequence>
<xsd:element name="cache-listener" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="com.gemstone.gemfire.cache.CacheListener"><![CDATA[
A cache listener definition for this region. A cache listener handles region or entry related events (that occur after
various operations on the region). Multiple listeners can be declared in a nested manner.
Note: Avoid the risk of deadlock. Since the listener is invoked while holding a lock on the entry generating the event,
it is easy to generate a deadlock by interacting with the region. For this reason, it is highly recommended to use some
other thread for accessing the region and not waiting for it to complete its task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CacheListener" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Inner bean definition of the cache listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:any>
</xsd:sequence>
<xsd:attribute name="ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the cache listener bean referred by this declaration. Used as a convenience method. If no reference exists,
use inner bean declarations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="cache-loader" type="beanDeclarationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="com.gemstone.gemfire.cache.CacheLoader"><![CDATA[
The cache loader definition for this region. A cache loader allows data to be placed into a region.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CacheLoader" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="cache-writer" type="beanDeclarationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="com.gemstone.gemfire.cache.CacheWriter"><![CDATA[
The cache writer definition for this region. A cache writer acts as a dedicated synchronous listener that is notified
before a region or an entry is modified. A typical example would be a writer that updates the database.
Note: Only one CacheWriter is invoked. GemFire will always prefer the local one (if it exists) otherwise it will
arbitrarily pick one.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CacheWriter" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="region-ttl" type="expirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Time to live configuration for the region itself. Default: no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="region-tti" type="expirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Time to idle (or idle timeout) configuration for the region itself. Default: no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:choice>
<xsd:element name="entry-ttl" type="expirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Time to live configuration for the region entries. Default: no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="custom-entry-ttl" type="customExpirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CustomExpiry" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation><![CDATA[[
CustomExpiry Time-to-Live (TTL) configuration for the Region Entries. The default is no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
<xsd:choice>
<xsd:element name="entry-tti" type="expirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Time to idle (or idle timeout) configuration for the region entries. Default: no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="custom-entry-tti" type="customExpirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CustomExpiry" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation><![CDATA[[
CustomExpiry Time-to-Idle (or Idle Timeout, TTI) configuration for the Region entries. The default is no expiration.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="gateway-sender" type="baseGatewaySenderType"/>
<xsd:element name="gateway-sender-ref">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the gateway sender bean referred by this declaration. Used as a convenience method. If no reference exists,
use inner bean declarations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="async-event-queue" type="baseAsyncEventQueueType"/>
<xsd:element name="async-event-queue-ref">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the gateway sender bean referred by this declaration. Used as a convenience method. If no reference exists,
use inner bean declarations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:group ref="subRegionGroup" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="cloning-enabled" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Determines how fromDelta applies deltas to the local cache for delta propagation. When true, the updates are applied
to a clone of the value and then the clone is saved to the cache. When false, the value is modified in place
in the cache. GemFire default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="eviction-maximum" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[[
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -589,8 +756,7 @@ CustomExpiry Time-to-Live (TTL) configuration for the Region Entries. The defaul
</xsd:element>
</xsd:choice>
<xsd:choice>
<xsd:element name="entry-tti" type="expirationType"
minOccurs="0" maxOccurs="1">
<xsd:element name="entry-tti" type="expirationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Time to idle (or idle timeout) configuration for the region entries. Default: no expiration.