SGF-196 - Support adding CacheListeners, CacheLoaders and CacheWriters, along with other mutable Region attributes to an existing Region.
This commit is contained in:
@@ -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/< 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 <lookup-region;gt; definitions.
|
||||
* Parser for GFE <lookup-region> 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);
|
||||
|
||||
Reference in New Issue
Block a user