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);