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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.gemstone.gemfire.cache.AttributesMutator;
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
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.RegionAttributes;
|
||||
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue;
|
||||
import com.gemstone.gemfire.cache.wan.GatewaySender;
|
||||
|
||||
/**
|
||||
* The LookupRegionFactoryBeanTest class is a test suite of test cases testing the contract and functionality
|
||||
* of the LookupRegionFactoryBean class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.springframework.data.gemfire.LookupRegionFactoryBean
|
||||
* @see com.gemstone.gemfire.cache.AttributesMutator
|
||||
* @see com.gemstone.gemfire.cache.Cache
|
||||
* @see com.gemstone.gemfire.cache.EvictionAttributesMutator
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public class LookupRegionFactoryBeanTest {
|
||||
|
||||
protected AsyncEventQueue mockAsyncEventQueue(final String id) {
|
||||
AsyncEventQueue mockQueue = mock(AsyncEventQueue.class, String.format("MockAsyncEventQueue.%1$s", id));
|
||||
when(mockQueue.getId()).thenReturn(id);
|
||||
return mockQueue;
|
||||
}
|
||||
|
||||
protected GatewaySender mockGatewaySender(final String id) {
|
||||
GatewaySender mockGatewaySender = mock(GatewaySender.class, String.format("MockGatewaySender.%1$s", id));
|
||||
when(mockGatewaySender.getId()).thenReturn(id);
|
||||
return mockGatewaySender;
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAfterPropertiesSet() throws Exception {
|
||||
Cache mockCache = mock(Cache.class, "testAfterPropertiesSet.MockCache");
|
||||
|
||||
Region<Object, Object> mockRegion = mock(Region.class, "testAfterPropertiesSet.MockRegion");
|
||||
|
||||
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class,
|
||||
"testAfterPropertiesSet.MockRegionAttributes");
|
||||
|
||||
EvictionAttributesMutator mockEvictionAttributesMutator = mock(EvictionAttributesMutator.class,
|
||||
"testAfterPropertiesSet.EvictionAttributesMutator");
|
||||
|
||||
AttributesMutator<Object, Object> mockAttributesMutator = mock(AttributesMutator.class,
|
||||
"testAfterPropertiesSet.MockAttributesMutator");
|
||||
|
||||
when(mockCache.getRegion(eq("Example"))).thenReturn(mockRegion);
|
||||
when(mockRegion.getFullPath()).thenReturn("/Example");
|
||||
when(mockRegion.getName()).thenReturn("Example");
|
||||
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
|
||||
when(mockRegionAttributes.getStatisticsEnabled()).thenReturn(true);
|
||||
when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
|
||||
when(mockAttributesMutator.getEvictionAttributesMutator()).thenReturn(mockEvictionAttributesMutator);
|
||||
|
||||
AsyncEventQueue mockAsyncEventQueueOne = mockAsyncEventQueue("AEQ1");
|
||||
AsyncEventQueue mockAsyncEventQueueTwo = mockAsyncEventQueue("AEQ2");
|
||||
|
||||
CacheListener mockCacheListenerZero = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.0");
|
||||
CacheListener mockCacheListenerOne = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.1");
|
||||
CacheListener mockCacheListenerTwo = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.2");
|
||||
|
||||
CacheLoader mockCacheLoader = mock(CacheLoader.class, "testAfterPropertiesSet.MockCacheLoader");
|
||||
|
||||
CacheWriter mockCacheWriter = mock(CacheWriter.class, "testAfterPropertiesSet.MockCacheWriter");
|
||||
|
||||
CustomExpiry mockCustomExpiryTti = mock(CustomExpiry.class, "testAfterPropertiesSet.MockCustomExpiry.TTI");
|
||||
CustomExpiry mockCustomExpiryTtl = mock(CustomExpiry.class, "testAfterPropertiesSet.MockCustomExpiry.TTL");
|
||||
|
||||
ExpirationAttributes mockExpirationAttributesEntryTti = mock(ExpirationAttributes.class,
|
||||
"testAfterPropertiesSet.MockExpirationAttributes.Entry.TTI");
|
||||
ExpirationAttributes mockExpirationAttributesEntryTtl = mock(ExpirationAttributes.class,
|
||||
"testAfterPropertiesSet.MockExpirationAttributes.Entry.TTL");
|
||||
ExpirationAttributes mockExpirationAttributesRegionTti = mock(ExpirationAttributes.class,
|
||||
"testAfterPropertiesSet.MockExpirationAttributes.Region.TTI");
|
||||
ExpirationAttributes mockExpirationAttributesRegionTtl = mock(ExpirationAttributes.class,
|
||||
"testAfterPropertiesSet.MockExpirationAttributes.Region.TTL");
|
||||
|
||||
GatewaySender mockGatewaySender = mockGatewaySender("GWS1");
|
||||
|
||||
LookupRegionFactoryBean factoryBean = new LookupRegionFactoryBean();
|
||||
|
||||
factoryBean.setAsyncEventQueues(new AsyncEventQueue[] { mockAsyncEventQueueOne, mockAsyncEventQueueTwo });
|
||||
factoryBean.setBeanName("Example");
|
||||
factoryBean.setCache(mockCache);
|
||||
factoryBean.setCacheLoader(mockCacheLoader);
|
||||
factoryBean.setCacheWriter(mockCacheWriter);
|
||||
factoryBean.setCloningEnabled(true);
|
||||
factoryBean.setCustomEntryIdleTimeout(mockCustomExpiryTti);
|
||||
factoryBean.setCustomEntryTimeToLive(mockCustomExpiryTtl);
|
||||
factoryBean.setEntryIdleTimeout(mockExpirationAttributesEntryTti);
|
||||
factoryBean.setEntryTimeToLive(mockExpirationAttributesEntryTtl);
|
||||
factoryBean.setGatewaySenders(new GatewaySender[] { mockGatewaySender });
|
||||
factoryBean.setEvictionMaximum(1000);
|
||||
factoryBean.setRegionIdleTimeout(mockExpirationAttributesRegionTti);
|
||||
factoryBean.setRegionTimeToLive(mockExpirationAttributesRegionTtl);
|
||||
factoryBean.setStatisticsEnabled(true);
|
||||
|
||||
factoryBean.setCacheListeners(new CacheListener[] {
|
||||
mockCacheListenerZero, mockCacheListenerOne, mockCacheListenerTwo
|
||||
});
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
verify(mockAttributesMutator, times(1)).addAsyncEventQueueId(eq("AEQ1"));
|
||||
verify(mockAttributesMutator, times(1)).addAsyncEventQueueId(eq("AEQ2"));
|
||||
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerZero));
|
||||
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerOne));
|
||||
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerTwo));
|
||||
verify(mockAttributesMutator, times(1)).setCacheLoader(same(mockCacheLoader));
|
||||
verify(mockAttributesMutator, times(1)).setCacheWriter(same(mockCacheWriter));
|
||||
verify(mockAttributesMutator, times(1)).setCloningEnabled(eq(true));
|
||||
verify(mockAttributesMutator, times(1)).setCustomEntryIdleTimeout(same(mockCustomExpiryTti));
|
||||
verify(mockAttributesMutator, times(1)).setCustomEntryTimeToLive(same(mockCustomExpiryTtl));
|
||||
verify(mockAttributesMutator, times(1)).setEntryIdleTimeout(same(mockExpirationAttributesEntryTti));
|
||||
verify(mockAttributesMutator, times(1)).setEntryTimeToLive(same(mockExpirationAttributesEntryTtl));
|
||||
verify(mockAttributesMutator, times(1)).addGatewaySenderId(eq("GWS1"));
|
||||
verify(mockEvictionAttributesMutator, times(1)).setMaximum(eq(1000));
|
||||
verify(mockAttributesMutator, times(1)).setRegionIdleTimeout(same(mockExpirationAttributesRegionTti));
|
||||
verify(mockAttributesMutator, times(1)).setRegionTimeToLive(same(mockExpirationAttributesRegionTtl));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified() throws Exception {
|
||||
Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockCache");
|
||||
|
||||
Region<Object, Object> mockRegion = mock(Region.class, "testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockRegion");
|
||||
|
||||
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class,
|
||||
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockRegionAttributes");
|
||||
|
||||
AttributesMutator mockAttributesMutator = mock(AttributesMutator.class,
|
||||
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockAttributesMutator");
|
||||
|
||||
ExpirationAttributes mockExpirationAttributesEntryTtl = mock(ExpirationAttributes.class,
|
||||
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockExpirationAttributes.Entry.TTL");
|
||||
|
||||
when(mockCache.getRegion(eq("Example"))).thenReturn(mockRegion);
|
||||
when(mockRegion.getFullPath()).thenReturn("/Example");
|
||||
when(mockRegion.getName()).thenReturn("Example");
|
||||
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
|
||||
when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
|
||||
when(mockRegionAttributes.getStatisticsEnabled()).thenReturn(false);
|
||||
|
||||
LookupRegionFactoryBean factoryBean = new LookupRegionFactoryBean();
|
||||
|
||||
factoryBean.setBeanName("Example");
|
||||
factoryBean.setCache(mockCache);
|
||||
factoryBean.setEntryTimeToLive(mockExpirationAttributesEntryTtl);
|
||||
//factoryBean.setStatisticsEnabled(true);
|
||||
|
||||
assertTrue(factoryBean.isStatisticsEnabled());
|
||||
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertEquals("Statistics for Region '/Example' must be enabled to change Entry & Region TTL/TTI Expiration settings",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockAttributesMutator, never()).setEntryTimeToLive(any(ExpirationAttributes.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.gemstone.gemfire.cache.CacheListener;
|
||||
import com.gemstone.gemfire.cache.CacheLoader;
|
||||
import com.gemstone.gemfire.cache.CacheLoaderException;
|
||||
import com.gemstone.gemfire.cache.CacheWriter;
|
||||
import com.gemstone.gemfire.cache.CacheWriterException;
|
||||
import com.gemstone.gemfire.cache.CustomExpiry;
|
||||
import com.gemstone.gemfire.cache.DataPolicy;
|
||||
import com.gemstone.gemfire.cache.EntryEvent;
|
||||
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.RegionEvent;
|
||||
import com.gemstone.gemfire.cache.asyncqueue.AsyncEvent;
|
||||
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventListener;
|
||||
import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
|
||||
import com.gemstone.gemfire.management.internal.cli.util.spring.StringUtils;
|
||||
|
||||
/**
|
||||
* The LookupRegionMutationIntegrationTest class is a test suite of test cases testing the contract and integrated
|
||||
* functionality between natively-defined GemFire Cache Regions and SDG's Region lookup functionality combined with
|
||||
* Region attribute(s) mutation.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.springframework.data.gemfire.LookupRegionFactoryBean
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@SuppressWarnings("unused")
|
||||
public class LookupRegionMutationIntegrationTest {
|
||||
|
||||
@Resource(name = "Example")
|
||||
private Region<?, ?> example;
|
||||
|
||||
protected void assertGemFireComponent(Object gemfireComponent, String expectedName) {
|
||||
assertNotNull("The GemFire component must not be null!", gemfireComponent);
|
||||
assertEquals(expectedName, gemfireComponent.toString());
|
||||
}
|
||||
|
||||
protected void assertExpirationAttributes(ExpirationAttributes expirationAttributes,
|
||||
String description, int expectedTimeout, ExpirationAction expectedAction) {
|
||||
assertNotNull(String.format("ExpirationAttributes for '%1$s' must not be null!", description), expirationAttributes);
|
||||
assertEquals(expectedAction, expirationAttributes.getAction());
|
||||
assertEquals(expectedTimeout, expirationAttributes.getTimeout());
|
||||
}
|
||||
|
||||
protected void assertCacheListeners(CacheListener[] cacheListeners, Collection<String> expectedCacheListenerNames) {
|
||||
if (!expectedCacheListenerNames.isEmpty()) {
|
||||
assertNotNull("CacheListeners must not be null!", cacheListeners);
|
||||
assertEquals(expectedCacheListenerNames.size(), cacheListeners.length);
|
||||
assertTrue(toStrings(cacheListeners).containsAll(expectedCacheListenerNames));
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<String> toStrings(Object[] objects) {
|
||||
List<String> cacheListenerNames = new ArrayList<String>(objects.length);
|
||||
|
||||
for (Object object : objects) {
|
||||
cacheListenerNames.add(object.toString());
|
||||
}
|
||||
|
||||
return cacheListenerNames;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegionConfiguration() {
|
||||
assertNotNull("'/Example' Region was not properly initialized!", example);
|
||||
assertEquals("Example", example.getName());
|
||||
assertEquals("/Example", example.getFullPath());
|
||||
assertNotNull(example.getAttributes());
|
||||
assertEquals(DataPolicy.REPLICATE, example.getAttributes().getDataPolicy());
|
||||
assertEquals(13, example.getAttributes().getInitialCapacity());
|
||||
assertEquals(0.85f, example.getAttributes().getLoadFactor(), 0.0f);
|
||||
assertCacheListeners(example.getAttributes().getCacheListeners(), Arrays.asList("A", "B"));
|
||||
assertGemFireComponent(example.getAttributes().getCacheLoader(), "C");
|
||||
assertGemFireComponent(example.getAttributes().getCacheWriter(), "D");
|
||||
assertExpirationAttributes(example.getAttributes().getRegionTimeToLive(), "Region TTL",
|
||||
120, ExpirationAction.LOCAL_DESTROY);
|
||||
assertExpirationAttributes(example.getAttributes().getRegionIdleTimeout(), "Region TTI",
|
||||
60, ExpirationAction.INVALIDATE);
|
||||
assertExpirationAttributes(example.getAttributes().getEntryTimeToLive(), "Entry TTL",
|
||||
30, ExpirationAction.DESTROY);
|
||||
assertGemFireComponent(example.getAttributes().getCustomEntryIdleTimeout(), "E");
|
||||
assertNotNull(example.getAttributes().getGatewaySenderIds());
|
||||
assertEquals(1, example.getAttributes().getGatewaySenderIds().size());
|
||||
assertEquals("GWS", example.getAttributes().getGatewaySenderIds().iterator().next());
|
||||
assertNotNull(example.getAttributes().getAsyncEventQueueIds());
|
||||
assertEquals(1, example.getAttributes().getAsyncEventQueueIds().size());
|
||||
assertEquals("AEQ", example.getAttributes().getAsyncEventQueueIds().iterator().next());
|
||||
}
|
||||
|
||||
protected static interface Nameable extends BeanNameAware {
|
||||
String getName();
|
||||
void setName(String name);
|
||||
}
|
||||
|
||||
protected static abstract class AbstractNameable implements Nameable {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(final String name) {
|
||||
if (!StringUtils.hasText(this.name)) {
|
||||
setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TestAsyncEventListener extends AbstractNameable implements AsyncEventListener {
|
||||
|
||||
@Override public boolean processEvents(List<AsyncEvent> events) {
|
||||
throw new UnsupportedOperationException("Not Implemented!");
|
||||
}
|
||||
|
||||
@Override public void close() { }
|
||||
}
|
||||
|
||||
public static final class TestCacheListener<K, V> extends CacheListenerAdapter<K, V> implements Nameable {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(final String name) {
|
||||
if (!StringUtils.hasText(this.name)) {
|
||||
setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TestCacheLoader<K, V> extends AbstractNameable implements CacheLoader<K, V> {
|
||||
|
||||
@Override
|
||||
public V load(LoaderHelper<K, V> helper) throws CacheLoaderException {
|
||||
throw new UnsupportedOperationException("Not Implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() { }
|
||||
}
|
||||
|
||||
public static final class TestCacheWriter<K, V> extends AbstractNameable implements CacheWriter<K, V> {
|
||||
|
||||
@Override public void beforeUpdate(EntryEvent<K, V> event) throws CacheWriterException { }
|
||||
|
||||
@Override public void beforeCreate(EntryEvent<K, V> event) throws CacheWriterException { }
|
||||
|
||||
@Override public void beforeDestroy(EntryEvent<K, V> event) throws CacheWriterException { }
|
||||
|
||||
@Override public void beforeRegionDestroy(RegionEvent<K, V> event) throws CacheWriterException { }
|
||||
|
||||
@Override public void beforeRegionClear(RegionEvent<K, V> event) throws CacheWriterException { }
|
||||
|
||||
@Override public void close() { }
|
||||
}
|
||||
|
||||
public static final class TestCustomExpiry<K, V> extends AbstractNameable implements CustomExpiry<K, V> {
|
||||
|
||||
@Override public ExpirationAttributes getExpiry(Region.Entry<K, V> entry) {
|
||||
throw new UnsupportedOperationException("Not Implemented!");
|
||||
}
|
||||
|
||||
@Override public void close() { }
|
||||
}
|
||||
|
||||
}
|
||||
13
src/test/resources/lookup-region-mutation-cache.xml
Normal file
13
src/test/resources/lookup-region-mutation-cache.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 8.0//EN"
|
||||
"http://www.gemstone.com/dtd/cache8_0.dtd">
|
||||
<cache>
|
||||
<region name="Example">
|
||||
<region-attributes data-policy="replicate" cloning-enabled="false" initial-capacity="13" load-factor="0.85"
|
||||
statistics-enabled="true">
|
||||
<eviction-attributes>
|
||||
<lru-entry-count action="overflow-to-disk" maximum="500"/>
|
||||
</eviction-attributes>
|
||||
</region-attributes>
|
||||
</region>
|
||||
</cache>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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:util="http://www.springframework.org/schema/util"
|
||||
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
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
|
||||
">
|
||||
|
||||
<util:properties id="gemfireProperties">
|
||||
<prop key="name">LookupRegionMutationIntegrationTest</prop>
|
||||
<prop key="mcast-port">0</prop>
|
||||
<prop key="log-level">warning</prop>
|
||||
</util:properties>
|
||||
|
||||
<gfe:cache cache-xml-location="/lookup-region-mutation-cache.xml" properties-ref="gemfireProperties"/>
|
||||
|
||||
<bean id="B" class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest.TestCacheListener"/>
|
||||
|
||||
<gfe:lookup-region id="Example" cloning-enabled="true" eviction-maximum="1000">
|
||||
<gfe:cache-listener>
|
||||
<bean class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest.TestCacheListener" p:name="A"/>
|
||||
<ref bean="B"/>
|
||||
</gfe:cache-listener>
|
||||
<gfe:cache-loader>
|
||||
<bean class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest$TestCacheLoader" p:name="C"/>
|
||||
</gfe:cache-loader>
|
||||
<gfe:cache-writer>
|
||||
<bean class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest$TestCacheWriter" p:name="D"/>
|
||||
</gfe:cache-writer>
|
||||
<gfe:region-ttl timeout="120" action="LOCAL_DESTROY"/>
|
||||
<gfe:region-tti timeout="60" action="INVALIDATE"/>
|
||||
<gfe:entry-ttl timeout="30" action="DESTROY"/>
|
||||
<gfe:custom-entry-tti>
|
||||
<bean class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest$TestCustomExpiry" p:name="E"/>
|
||||
</gfe:custom-entry-tti>
|
||||
<gfe:gateway-sender name="GWS" remote-distributed-system-id="123" manual-start="true"/>
|
||||
<gfe:async-event-queue name="AEQ" persistent="false" parallel="true" dispatcher-threads="8">
|
||||
<gfe:async-event-listener>
|
||||
<bean class="org.springframework.data.gemfire.LookupRegionMutationIntegrationTest$TestAsyncEventListener" p:name="F"/>
|
||||
</gfe:async-event-listener>
|
||||
</gfe:async-event-queue>
|
||||
</gfe:lookup-region>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user