Implemented SGF-206 - CacheLoader and CacheWriter support on client, local Regions in a GemFire ClientCache. Also, performed additional refactoring of related code.
This commit is contained in:
@@ -31,6 +31,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.CacheClosedException;
|
||||
import com.gemstone.gemfire.cache.CacheListener;
|
||||
import com.gemstone.gemfire.cache.CacheLoader;
|
||||
import com.gemstone.gemfire.cache.CacheWriter;
|
||||
import com.gemstone.gemfire.cache.DataPolicy;
|
||||
import com.gemstone.gemfire.cache.GemFireCache;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
@@ -42,102 +44,92 @@ import com.gemstone.gemfire.cache.client.Pool;
|
||||
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
|
||||
|
||||
/**
|
||||
* Client extension for GemFire regions.
|
||||
*
|
||||
* Client extension for GemFire Regions.
|
||||
* <p/>
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> implements BeanFactoryAware,
|
||||
DisposableBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ClientRegionFactoryBean.class);
|
||||
|
||||
private boolean close = false;
|
||||
private boolean destroy = false;
|
||||
|
||||
private boolean close = true;
|
||||
|
||||
private Resource snapshot;
|
||||
|
||||
private CacheListener<K, V> cacheListeners[];
|
||||
|
||||
private Interest<K>[] interests;
|
||||
|
||||
private String poolName;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private CacheListener<K, V>[] cacheListeners;
|
||||
|
||||
private CacheLoader<K, V> cacheLoader;
|
||||
|
||||
private CacheWriter<K, V> cacheWriter;
|
||||
|
||||
private ClientRegionShortcut shortcut = null;
|
||||
|
||||
private DataPolicy dataPolicy;
|
||||
|
||||
private Interest<K>[] interests;
|
||||
|
||||
private RegionAttributes<K, V> attributes;
|
||||
|
||||
private Region<K, V> region;
|
||||
|
||||
private String diskStoreName;
|
||||
private Resource snapshot;
|
||||
|
||||
private String dataPolicyName;
|
||||
private String diskStoreName;
|
||||
private String poolName;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
region = getRegion();
|
||||
postProcess(region);
|
||||
postProcess(getRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region<K, V> lookupFallback(GemFireCache cache, String regionName) throws Exception {
|
||||
Assert.isTrue(cache instanceof ClientCache, "Unable to create regions from " + cache);
|
||||
ClientCache c = (ClientCache) cache;
|
||||
Assert.isTrue(cache instanceof ClientCache, String.format("Unable to create regions from %1$s", cache));
|
||||
|
||||
// TODO reference to an internal GemFire class!
|
||||
if (cache instanceof GemFireCacheImpl) {
|
||||
Assert.isTrue(((GemFireCacheImpl) cache).isClient(), "A client-cache instance is required");
|
||||
}
|
||||
|
||||
Assert.isTrue(!(StringUtils.hasText(dataPolicyName) && dataPolicy != null), "Only one of 'dataPolicy' or 'dataPolicyName' can be set");
|
||||
|
||||
|
||||
|
||||
Assert.isTrue(!(StringUtils.hasText(dataPolicyName) && dataPolicy != null),
|
||||
"Only one of 'dataPolicy' or 'dataPolicyName' can be set");
|
||||
|
||||
if (StringUtils.hasText(dataPolicyName)) {
|
||||
dataPolicy = new DataPolicyConverter().convert(dataPolicyName);
|
||||
Assert.notNull(dataPolicy, "Data policy " + dataPolicyName + " is invalid");
|
||||
}
|
||||
|
||||
// first look at shortcut
|
||||
ClientRegionShortcut s = null;
|
||||
ClientRegionShortcut shortcut = this.shortcut;
|
||||
|
||||
if (shortcut == null) {
|
||||
if (dataPolicy != null) {
|
||||
if (DataPolicy.EMPTY.equals(dataPolicy)) {
|
||||
s = ClientRegionShortcut.PROXY;
|
||||
}
|
||||
else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) {
|
||||
s = ClientRegionShortcut.LOCAL_PERSISTENT;
|
||||
shortcut = ClientRegionShortcut.PROXY;
|
||||
}
|
||||
else if (DataPolicy.NORMAL.equals(this.dataPolicy)) {
|
||||
s = ClientRegionShortcut.CACHING_PROXY;
|
||||
shortcut = ClientRegionShortcut.CACHING_PROXY;
|
||||
}
|
||||
else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) {
|
||||
shortcut = ClientRegionShortcut.LOCAL_PERSISTENT;
|
||||
}
|
||||
else {
|
||||
s = ClientRegionShortcut.LOCAL;
|
||||
shortcut = ClientRegionShortcut.LOCAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
s = ClientRegionShortcut.LOCAL;
|
||||
shortcut = ClientRegionShortcut.LOCAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
s = shortcut;
|
||||
}
|
||||
|
||||
ClientRegionFactory<K, V> factory = c.createClientRegionFactory(s);
|
||||
ClientRegionFactory<K, V> factory = ((ClientCache) cache).createClientRegionFactory(shortcut);
|
||||
|
||||
// map the attributes onto the client
|
||||
if (attributes != null) {
|
||||
CacheListener<K, V>[] listeners = attributes.getCacheListeners();
|
||||
if (!ObjectUtils.isEmpty(listeners)) {
|
||||
for (CacheListener<K, V> listener : listeners) {
|
||||
factory.addCacheListener(listener);
|
||||
}
|
||||
}
|
||||
factory.setCloningEnabled(attributes.getCloningEnabled());
|
||||
factory.setConcurrencyLevel(attributes.getConcurrencyLevel());
|
||||
factory.setCustomEntryIdleTimeout(attributes.getCustomEntryIdleTimeout());
|
||||
@@ -157,11 +149,7 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
factory.setValueConstraint(attributes.getValueConstraint());
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(cacheListeners)) {
|
||||
for (CacheListener<K, V> listener : cacheListeners) {
|
||||
factory.addCacheListener(listener);
|
||||
}
|
||||
}
|
||||
addCacheListeners(factory);
|
||||
|
||||
if (StringUtils.hasText(poolName)) {
|
||||
// try to eagerly initialize the pool name, if defined as a bean
|
||||
@@ -173,7 +161,8 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
}
|
||||
|
||||
factory.setPoolName(poolName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Pool pool = beanFactory.getBean(Pool.class);
|
||||
factory.setPoolName(pool.getName());
|
||||
}
|
||||
@@ -182,35 +171,71 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
factory.setDiskStoreName(diskStoreName);
|
||||
}
|
||||
|
||||
Region<K, V> reg = factory.create(regionName);
|
||||
Region<K, V> clientRegion = factory.create(regionName);
|
||||
log.info("Created new cache region [" + regionName + "]");
|
||||
|
||||
if (snapshot != null) {
|
||||
reg.loadSnapshot(snapshot.getInputStream());
|
||||
clientRegion.loadSnapshot(snapshot.getInputStream());
|
||||
}
|
||||
|
||||
return reg;
|
||||
return clientRegion;
|
||||
}
|
||||
|
||||
private void addCacheListeners(ClientRegionFactory<K, V> factory) {
|
||||
if (attributes != null) {
|
||||
CacheListener<K, V>[] listeners = attributes.getCacheListeners();
|
||||
|
||||
if (!ObjectUtils.isEmpty(listeners)) {
|
||||
for (CacheListener<K, V> listener : listeners) {
|
||||
factory.addCacheListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(cacheListeners)) {
|
||||
for (CacheListener<K, V> listener : cacheListeners) {
|
||||
factory.addCacheListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void postProcess(Region<K, V> region) {
|
||||
registerInterests(region);
|
||||
setCacheLoader(region);
|
||||
setCacheWriter(region);
|
||||
}
|
||||
|
||||
private void registerInterests(final Region<K, V> region) {
|
||||
if (!ObjectUtils.isEmpty(interests)) {
|
||||
for (Interest<K> interest : interests) {
|
||||
if (interest instanceof RegexInterest) {
|
||||
// do the cast since it's safe
|
||||
region.registerInterestRegex((String) interest.getKey(), interest.getPolicy(),
|
||||
interest.isDurable(), interest.isReceiveValues());
|
||||
interest.isDurable(), interest.isReceiveValues());
|
||||
}
|
||||
else {
|
||||
region.registerInterest(interest.getKey(), interest.getPolicy(), interest.isDurable(),
|
||||
interest.isReceiveValues());
|
||||
interest.isReceiveValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setCacheLoader(Region<K, V> region) {
|
||||
if (cacheLoader != null) {
|
||||
region.getAttributesMutator().setCacheLoader(this.cacheLoader);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCacheWriter(Region<K, V> region) {
|
||||
if (cacheWriter != null) {
|
||||
region.getAttributesMutator().setCacheWriter(this.cacheWriter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
Region<K, V> region = getObject();
|
||||
// unregister interests
|
||||
|
||||
try {
|
||||
if (region != null && !ObjectUtils.isEmpty(interests)) {
|
||||
for (Interest<K> interest : interests) {
|
||||
@@ -222,9 +247,9 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
}
|
||||
}
|
||||
}
|
||||
// should not really happen since interests are validated at
|
||||
// start/registration
|
||||
}
|
||||
// NOTE AdminRegion, LocalDataSet, Proxy Region and RegionCreation all throw UnsupportedOperationException;
|
||||
// however, should not really happen since Interests are validated at start/registration
|
||||
catch (UnsupportedOperationException ex) {
|
||||
log.warn("Cannot unregister cache interests", ex);
|
||||
}
|
||||
@@ -235,16 +260,16 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
try {
|
||||
region.close();
|
||||
}
|
||||
catch (CacheClosedException cce) {
|
||||
// nothing to see folks, move on.
|
||||
catch (CacheClosedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO I think Region.close and Region.destroyRegion are mutually exclusive; thus, 1 operation (e.g. close)
|
||||
// does not cancel the other (i.e. destroy). This should just be if, not else if.
|
||||
else if (destroy) {
|
||||
region.destroyRegion();
|
||||
}
|
||||
}
|
||||
region = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -294,42 +319,44 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
* recommended way for creating clients since it covers all the major
|
||||
* scenarios with minimal configuration.
|
||||
*
|
||||
* @param shortcut
|
||||
* @param shortcut the ClientRegionShortcut to use.
|
||||
*/
|
||||
public void setShortcut(ClientRegionShortcut shortcut) {
|
||||
this.shortcut = shortcut;
|
||||
}
|
||||
|
||||
final boolean isDestroy() {
|
||||
return destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the region referred by this factory bean, will be
|
||||
* Indicates whether the region referred by this factory bean will be
|
||||
* destroyed on shutdown (default false). Note: destroy and close are
|
||||
* mutually exclusive. Enabling one will automatically disable the other.
|
||||
*
|
||||
* <p/>
|
||||
* @param destroy whether or not to destroy the region
|
||||
*
|
||||
* @see #setClose(boolean)
|
||||
*/
|
||||
public void setDestroy(boolean destroy) {
|
||||
this.destroy = destroy;
|
||||
this.close = (this.close && !destroy); // retain previous value iff destroy is false;
|
||||
}
|
||||
|
||||
if (destroy) {
|
||||
close = false;
|
||||
}
|
||||
final boolean isClose() {
|
||||
return close;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the region referred by this factory bean, will be
|
||||
* closed on shutdown (default true). Note: destroy and close are mutually
|
||||
* exclusive. Enabling one will automatically disable the other.
|
||||
*
|
||||
* <p/>
|
||||
* @param close whether to close or not the region
|
||||
* @see #setDestroy(boolean)
|
||||
*/
|
||||
public void setClose(boolean close) {
|
||||
this.close = close;
|
||||
if (close) {
|
||||
destroy = false;
|
||||
}
|
||||
this.destroy = (this.destroy && !close); // retain previous value iff close is false.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,6 +382,26 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
this.cacheListeners = cacheListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CacheLoader used to load data local to the client's Region on cache misses.
|
||||
* <p/>
|
||||
* @param cacheLoader a GemFire CacheLoader used to load data into the client Region.
|
||||
* @see com.gemstone.gemfire.cache.CacheLoader
|
||||
*/
|
||||
public void setCacheLoader(CacheLoader<K, V> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CacheWriter used to perform a synchronous write-behind when data is put into the client's Region.
|
||||
* <p/>
|
||||
* @param cacheWriter the GemFire CacheWriter used to perform synchronous write-behinds on put ops.
|
||||
* @see com.gemstone.gemfire.cache.CacheWriter
|
||||
*/
|
||||
public void setCacheWriter(CacheWriter<K, V> cacheWriter) {
|
||||
this.cacheWriter = cacheWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data policy. Used only when a new region is created.
|
||||
*
|
||||
@@ -395,4 +442,5 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
|
||||
public void setAttributes(RegionAttributes<K, V> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,10 +33,9 @@ import com.gemstone.gemfire.cache.DataPolicy;
|
||||
|
||||
/**
|
||||
* Parser for <client-region;gt; definitions.
|
||||
*
|
||||
* To avoid eager evaluations, the region interests are declared as nested
|
||||
* definition.
|
||||
*
|
||||
* <p/>
|
||||
* To avoid eager evaluations, the region interests are declared as nested definition.
|
||||
* <p/>
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
@@ -52,87 +51,92 @@ class ClientRegionParser extends AbstractRegionParser {
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
super.doParse(element, builder);
|
||||
|
||||
// set scope
|
||||
// since the user can define both client and p2p regions
|
||||
// setting the cache/DS to a be 'loner' isn't feasible
|
||||
// so to prevent both client and p2p communication in the region,
|
||||
// the scope is fixed to local
|
||||
String cacheRefAttribute = element.getAttribute("cache-ref");
|
||||
|
||||
builder.addPropertyReference("cache", (StringUtils.hasText(cacheRefAttribute) ? cacheRefAttribute
|
||||
: GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME));
|
||||
|
||||
ParsingUtils.setPropertyValue(element, builder, "close");
|
||||
ParsingUtils.setPropertyValue(element, builder, "destroy");
|
||||
ParsingUtils.setPropertyValue(element, builder, "data-policy", "dataPolicyName");
|
||||
ParsingUtils.setPropertyValue(element, builder, "name");
|
||||
ParsingUtils.setPropertyValue(element, builder, "pool-name");
|
||||
ParsingUtils.setPropertyValue(element, builder, "shortcut");
|
||||
|
||||
parseDiskStoreAttribute(element, builder);
|
||||
|
||||
boolean dataPolicyFrozen = false;
|
||||
|
||||
// TODO why is the DataPolicy determined in the ClientRegionParser and not in the ClientRegionFactoryBean when evaluating the configuration settings?
|
||||
// set the persistent policy
|
||||
String attr = element.getAttribute("persistent");
|
||||
|
||||
boolean frozenDataPolicy = false;
|
||||
|
||||
if (Boolean.parseBoolean(attr)) {
|
||||
// check first for GemFire 6.5
|
||||
if (Boolean.parseBoolean(element.getAttribute("persistent"))) {
|
||||
builder.addPropertyValue("dataPolicy", DataPolicy.PERSISTENT_REPLICATE);
|
||||
frozenDataPolicy = true;
|
||||
dataPolicyFrozen = true;
|
||||
}
|
||||
|
||||
attr = element.getAttribute("cache-ref");
|
||||
// add cache reference (fallback to default if nothing is specified)
|
||||
builder.addPropertyReference("cache", (StringUtils.hasText(attr) ? attr : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME));
|
||||
ParsingUtils.setPropertyValue(element, builder, "close");
|
||||
ParsingUtils.setPropertyValue(element, builder, "destroy");
|
||||
// eviction + overflow attributes
|
||||
// client attributes
|
||||
BeanDefinitionBuilder attrBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(RegionAttributesFactoryBean.class);
|
||||
// eviction + expiration + overflow + optional client region attributes
|
||||
BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RegionAttributesFactoryBean.class);
|
||||
|
||||
ParsingUtils.parseStatistics(element, attrBuilder);
|
||||
boolean overwriteDataPolicy = ParsingUtils.parseEviction(parserContext, element, regionAttributesBuilder);
|
||||
|
||||
if (StringUtils.hasText(element.getAttribute("disk-store-ref"))) {
|
||||
ParsingUtils.setPropertyValue(element, builder, "disk-store-ref", "diskStoreName");
|
||||
builder.addDependsOn(element.getAttribute("disk-store-ref"));
|
||||
}
|
||||
ParsingUtils.parseOptionalRegionAttributes(parserContext, element, regionAttributesBuilder);
|
||||
ParsingUtils.parseStatistics(element, regionAttributesBuilder);
|
||||
// NOTE parsing 'expiration' attributes must happen after parsing the user-defined setting for 'statistics'
|
||||
// in GemFire this attribute corresponds to the RegionAttributes 'statistics-enabled' setting). If the user
|
||||
// configured expiration settings (any?), then statistics must be enabled, regardless if the user explicitly
|
||||
// disabled them.
|
||||
ParsingUtils.parseExpiration(parserContext, element, regionAttributesBuilder);
|
||||
|
||||
boolean overwriteDataPolicy = false;
|
||||
|
||||
overwriteDataPolicy |= ParsingUtils.parseEviction(parserContext, element, attrBuilder);
|
||||
ParsingUtils.parseStatistics(element, attrBuilder);
|
||||
ParsingUtils.parseExpiration(parserContext, element, attrBuilder);
|
||||
ParsingUtils.parseEviction(parserContext, element, attrBuilder);
|
||||
ParsingUtils.parseOptionalRegionAttributes(parserContext, element, attrBuilder);
|
||||
|
||||
if (!frozenDataPolicy && overwriteDataPolicy) {
|
||||
// TODO understand why this determination is not made in the FactoryBean?
|
||||
if (!dataPolicyFrozen && overwriteDataPolicy) {
|
||||
builder.addPropertyValue("dataPolicy", DataPolicy.NORMAL);
|
||||
}
|
||||
|
||||
builder.addPropertyValue("attributes", attrBuilder.getBeanDefinition());
|
||||
|
||||
|
||||
builder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
|
||||
|
||||
List<Element> subElements = DomUtils.getChildElements(element);
|
||||
ManagedList<Object> interests = new ManagedList<Object>();
|
||||
|
||||
// parse nested declarations
|
||||
List<Element> subElements = DomUtils.getChildElements(element);
|
||||
|
||||
// parse nested cache-listener elements
|
||||
// parse nested elements
|
||||
for (Element subElement : subElements) {
|
||||
String name = subElement.getLocalName();
|
||||
String subElementLocalName = subElement.getLocalName();
|
||||
|
||||
if ("cache-listener".equals(name)) {
|
||||
builder.addPropertyValue("cacheListeners", parseCacheListener(parserContext, subElement, builder));
|
||||
if ("cache-listener".equals(subElementLocalName)) {
|
||||
builder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
|
||||
parserContext, subElement, builder));
|
||||
}
|
||||
|
||||
else if ("key-interest".equals(name)) {
|
||||
else if ("cache-loader".equals(subElementLocalName)) {
|
||||
builder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrNestedBeanDeclaration(
|
||||
parserContext, subElement, builder));
|
||||
}
|
||||
else if ("cache-writer".equals(subElementLocalName)) {
|
||||
builder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrNestedBeanDeclaration(
|
||||
parserContext, subElement, builder));
|
||||
}
|
||||
else if ("key-interest".equals(subElementLocalName)) {
|
||||
interests.add(parseKeyInterest(parserContext, subElement));
|
||||
}
|
||||
|
||||
else if ("regex-interest".equals(name)) {
|
||||
else if ("regex-interest".equals(subElementLocalName)) {
|
||||
interests.add(parseRegexInterest(parserContext, subElement));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO is adding an 'interests' property really based on whether there are "sub-elements", or should it be based on whether there are "interests" (as in 'key-interest' and 'regex-interest')?
|
||||
if (!subElements.isEmpty()) {
|
||||
builder.addPropertyValue("interests", interests);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseDiskStoreAttribute(Element element, BeanDefinitionBuilder builder) {
|
||||
String diskStoreRefAttribute = element.getAttribute("disk-store-ref");
|
||||
|
||||
if (StringUtils.hasText(diskStoreRefAttribute)) {
|
||||
builder.addPropertyValue("diskStoreName", diskStoreRefAttribute);
|
||||
builder.addDependsOn(diskStoreRefAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
|
||||
boolean subRegion) {
|
||||
@@ -141,32 +145,32 @@ class ClientRegionParser extends AbstractRegionParser {
|
||||
getClass().getName()));
|
||||
}
|
||||
|
||||
private Object parseCacheListener(ParserContext parserContext, Element subElement, BeanDefinitionBuilder builder) {
|
||||
return ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, builder);
|
||||
}
|
||||
|
||||
private Object parseKeyInterest(ParserContext parserContext, Element subElement) {
|
||||
BeanDefinitionBuilder keyInterestBuilder = BeanDefinitionBuilder.genericBeanDefinition(Interest.class);
|
||||
parseCommonInterestAttr(subElement, keyInterestBuilder);
|
||||
|
||||
parseCommonInterestAttributes(subElement, keyInterestBuilder);
|
||||
|
||||
Object key = ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, keyInterestBuilder,
|
||||
"key-ref");
|
||||
"key-ref");
|
||||
|
||||
keyInterestBuilder.addConstructorArgValue(key);
|
||||
|
||||
return keyInterestBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private Object parseRegexInterest(ParserContext parserContext, Element subElement) {
|
||||
BeanDefinitionBuilder regexInterestBuilder = BeanDefinitionBuilder.genericBeanDefinition(RegexInterest.class);
|
||||
|
||||
parseCommonInterestAttr(subElement, regexInterestBuilder);
|
||||
parseCommonInterestAttributes(subElement, regexInterestBuilder);
|
||||
ParsingUtils.setPropertyValue(subElement, regexInterestBuilder, "pattern", "key");
|
||||
|
||||
return regexInterestBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void parseCommonInterestAttr(Element element, BeanDefinitionBuilder builder) {
|
||||
private void parseCommonInterestAttributes(Element element, BeanDefinitionBuilder builder) {
|
||||
ParsingUtils.setPropertyValue(element, builder, "durable", "durable");
|
||||
ParsingUtils.setPropertyValue(element, builder, "result-policy", "policy");
|
||||
ParsingUtils.setPropertyValue(element, builder, "receive-values", "receiveValues");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,15 +20,10 @@ import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanMetadataAttribute;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedArray;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -44,21 +39,18 @@ import com.gemstone.gemfire.cache.ResumptionAction;
|
||||
import com.gemstone.gemfire.cache.Scope;
|
||||
|
||||
/**
|
||||
* Various minor utility used by the parser.
|
||||
*
|
||||
* Various minor utilities used by the parser.
|
||||
* <p/>
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author Lyndon Adams
|
||||
* @author John Blum
|
||||
*/
|
||||
abstract class ParsingUtils {
|
||||
|
||||
|
||||
private static Log log = LogFactory.getLog(ParsingUtils.class);
|
||||
|
||||
final static String GEMFIRE_VERSION = CacheFactory.getVersion();
|
||||
|
||||
private static final String ALIASES_KEY = ParsingUtils.class.getName() + ":aliases";
|
||||
|
||||
|
||||
static final String GEMFIRE_VERSION = CacheFactory.getVersion();
|
||||
|
||||
static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attributeName,
|
||||
String propertyName, Object defaultValue) {
|
||||
@@ -90,35 +82,6 @@ abstract class ParsingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for parsing bean aliases. Normally parsed by
|
||||
* AbstractBeanDefinitionParser however due to the attribute clash (bean
|
||||
* uses 'name' for aliases while region use it to indicate their name), the
|
||||
* parser needs to handle this differently by storing them as metadata which
|
||||
* gets deleted just before registration.
|
||||
*
|
||||
* @param element
|
||||
* @param builder
|
||||
*/
|
||||
static void addBeanAliasAsMetadata(Element element, BeanDefinitionBuilder builder) {
|
||||
String[] aliases = new String[0];
|
||||
String name = element.getAttributeNS(BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI,
|
||||
AbstractBeanDefinitionParser.NAME_ATTRIBUTE);
|
||||
|
||||
if (StringUtils.hasLength(name)) {
|
||||
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
|
||||
}
|
||||
BeanMetadataAttribute attr = new BeanMetadataAttribute(ALIASES_KEY, aliases);
|
||||
attr.setSource(element);
|
||||
builder.getRawBeanDefinition().addMetadataAttribute(attr);
|
||||
}
|
||||
|
||||
static BeanDefinitionHolder replaceBeanAliasAsMetadata(BeanDefinitionHolder holder) {
|
||||
BeanDefinition beanDefinition = holder.getBeanDefinition();
|
||||
return new BeanDefinitionHolder(beanDefinition, holder.getBeanName(),
|
||||
(String[]) beanDefinition.removeAttribute(ALIASES_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method handling parsing of nested definition of the type:
|
||||
*
|
||||
@@ -134,32 +97,28 @@ abstract class ParsingUtils {
|
||||
* </tag>
|
||||
* </pre>
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
* @param element the XML element.
|
||||
* @return Bean reference or nested Bean definition.
|
||||
*/
|
||||
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
|
||||
BeanDefinitionBuilder builder) {
|
||||
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, "ref", false);
|
||||
}
|
||||
|
||||
static Object getBeanReference(ParserContext parserContext, Element element) {
|
||||
return getBeanReference(parserContext, element, "ref");
|
||||
}
|
||||
static Object getBeanReference(ParserContext parserContext, Element element, String refAttributeName) {
|
||||
String refAttributeValue = element.getAttribute(refAttributeName);
|
||||
|
||||
static Object getBeanReference(ParserContext parserContext, Element element, String refAttrName) {
|
||||
String attr = element.getAttribute(refAttrName);
|
||||
boolean hasRef = StringUtils.hasText(attr);
|
||||
|
||||
// check nested declarations
|
||||
// check nested bean declarations
|
||||
List<Element> childElements = DomUtils.getChildElements(element);
|
||||
|
||||
if (hasRef) {
|
||||
if (StringUtils.hasText(refAttributeValue)) {
|
||||
if (!childElements.isEmpty()) {
|
||||
parserContext.getReaderContext().error(
|
||||
"either use the '" + refAttrName + "' attribute or a nested bean declaration for '"
|
||||
+ element.getLocalName() + "' element, but not both", element);
|
||||
parserContext.getReaderContext().error(String.format(
|
||||
"Use either the '%1$s' attribute or a nested bean declaration for '%2$s' element, but not both",
|
||||
refAttributeName, element.getLocalName()), element);
|
||||
}
|
||||
return new RuntimeBeanReference(attr);
|
||||
|
||||
return new RuntimeBeanReference(refAttributeValue);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
@@ -183,91 +142,93 @@ abstract class ParsingUtils {
|
||||
}
|
||||
|
||||
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
|
||||
BeanDefinitionBuilder builder, String refAttrName) {
|
||||
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, refAttrName, false);
|
||||
BeanDefinitionBuilder builder, String refAttributeName) {
|
||||
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, refAttributeName, false);
|
||||
}
|
||||
|
||||
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
|
||||
BeanDefinitionBuilder builder, String refAttrName, boolean single) {
|
||||
Object beanRef = getBeanReference(parserContext, element, refAttrName);
|
||||
if (beanRef != null) {
|
||||
return beanRef;
|
||||
BeanDefinitionBuilder builder, String refAttributeName, boolean single) {
|
||||
Object beanReference = getBeanReference(parserContext, element, refAttributeName);
|
||||
|
||||
if (beanReference != null) {
|
||||
return beanReference;
|
||||
}
|
||||
|
||||
// check nested declarations
|
||||
List<Element> childElements = DomUtils.getChildElements(element);
|
||||
|
||||
// nested parse nested bean definition
|
||||
// parse nested bean definition
|
||||
if (childElements.size() == 1) {
|
||||
return parserContext.getDelegate().parsePropertySubElement(childElements.get(0),
|
||||
builder.getRawBeanDefinition());
|
||||
}
|
||||
else {
|
||||
// TODO also triggered when there are no child elements; need to change the message...
|
||||
if (single) {
|
||||
parserContext.getReaderContext().error(
|
||||
"the element '" + element.getLocalName()
|
||||
+ "' does not support multiple nested bean definitions", element);
|
||||
parserContext.getReaderContext().error(String.format(
|
||||
"The element '%1$s' does not support multiple nested bean definitions",
|
||||
element.getLocalName()), element);
|
||||
}
|
||||
}
|
||||
|
||||
ManagedList<Object> list = new ManagedList<Object>();
|
||||
|
||||
for (Element el : childElements) {
|
||||
list.add(parserContext.getDelegate().parsePropertySubElement(el, builder.getRawBeanDefinition()));
|
||||
for (Element childElement : childElements) {
|
||||
list.add(parserContext.getDelegate().parsePropertySubElement(childElement, builder.getRawBeanDefinition()));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the eviction sub-element. Populates the given attribute factory
|
||||
* with the proper attributes.
|
||||
*
|
||||
* @param parserContext
|
||||
* @param element
|
||||
* @param attrBuilder
|
||||
* @return true if parsing actually occured, false otherwise
|
||||
* Parses the eviction sub-element. Populates the given attribute factory with the proper attributes.
|
||||
* <p/>
|
||||
* @param parserContext the context used for parsing the XML document.
|
||||
* @param element the XML elements being parsed.
|
||||
* @param attributesBuilder the Region Attributes builder.
|
||||
* @return true if parsing actually occurred, false otherwise.
|
||||
*/
|
||||
static boolean parseEviction(ParserContext parserContext, Element element, BeanDefinitionBuilder attrBuilder) {
|
||||
static boolean parseEviction(ParserContext parserContext, Element element, BeanDefinitionBuilder attributesBuilder) {
|
||||
Element evictionElement = DomUtils.getChildElementByTagName(element, "eviction");
|
||||
|
||||
if (evictionElement == null)
|
||||
return false;
|
||||
if (evictionElement != null) {
|
||||
BeanDefinitionBuilder evictionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
EvictionAttributesFactoryBean.class);
|
||||
|
||||
BeanDefinitionBuilder evictionDefBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(EvictionAttributesFactoryBean.class);
|
||||
setPropertyValue(evictionElement, evictionAttributesBuilder, "action");
|
||||
setPropertyValue(evictionElement, evictionAttributesBuilder, "threshold");
|
||||
|
||||
// do manual conversion since the enum is not public
|
||||
String attr = evictionElement.getAttribute("type");
|
||||
if (StringUtils.hasText(attr)) {
|
||||
evictionDefBuilder.addPropertyValue("type", EvictionType.valueOf(attr.toUpperCase()));
|
||||
String evictionType = evictionElement.getAttribute("type");
|
||||
|
||||
if (StringUtils.hasText(evictionType)) {
|
||||
evictionAttributesBuilder.addPropertyValue("type", EvictionType.valueOf(evictionType.toUpperCase()));
|
||||
}
|
||||
|
||||
Element objectSizerElement = DomUtils.getChildElementByTagName(evictionElement, "object-sizer");
|
||||
|
||||
if (objectSizerElement != null) {
|
||||
Object sizer = parseRefOrNestedBeanDeclaration(parserContext, objectSizerElement,
|
||||
evictionAttributesBuilder);
|
||||
evictionAttributesBuilder.addPropertyValue("ObjectSizer", sizer);
|
||||
}
|
||||
|
||||
attributesBuilder.addPropertyValue("evictionAttributes", evictionAttributesBuilder.getBeanDefinition());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setPropertyValue(evictionElement, evictionDefBuilder, "threshold");
|
||||
setPropertyValue(evictionElement, evictionDefBuilder, "action");
|
||||
|
||||
// get object sizer (if declared)
|
||||
Element objectSizerElement = DomUtils.getChildElementByTagName(evictionElement, "object-sizer");
|
||||
|
||||
if (objectSizerElement != null) {
|
||||
Object sizer = parseRefOrNestedBeanDeclaration(parserContext, objectSizerElement, evictionDefBuilder);
|
||||
evictionDefBuilder.addPropertyValue("ObjectSizer", sizer);
|
||||
}
|
||||
|
||||
attrBuilder.addPropertyValue("evictionAttributes", evictionDefBuilder.getBeanDefinition());
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the subscription sub-element. Populates the given attribute factory
|
||||
* with the proper attributes.
|
||||
*
|
||||
* @author Lyndon Adams
|
||||
* @param parserContext
|
||||
* @param element
|
||||
* @param attrBuilder
|
||||
* @return true if parsing actually occured, false otherwise
|
||||
* Parses the subscription sub-element. Populates the given attribute factory with the proper attributes.
|
||||
* <p/>
|
||||
* @param parserContext the context used while parsing the XML document.
|
||||
* @param element the XML element being parsed.
|
||||
* @param attrBuilder the Region Attributes builder.
|
||||
* @return true if parsing actually occurred, false otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
static boolean parseSubscription(ParserContext parserContext, Element element, BeanDefinitionBuilder attrBuilder) {
|
||||
Element subscriptionElement = DomUtils.getChildElementByTagName(element, "subscription");
|
||||
|
||||
@@ -301,23 +262,23 @@ abstract class ParsingUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the expiration sub-elements. Populates the given attribute factory
|
||||
* with proper attributes.
|
||||
*
|
||||
* @param parserContext
|
||||
* @param element
|
||||
* @param attrBuilder
|
||||
* @return
|
||||
* Parses the expiration sub-elements. Populates the given attribute factory with proper attributes.
|
||||
* <p/>
|
||||
* @param parserContext the context used while parsing the XML document.
|
||||
* @param element the XML element being parsed.
|
||||
* @param attrBuilder the Region Attributes builder.
|
||||
* @return a boolean indicating whether Region expiration attributes were specified.
|
||||
*/
|
||||
static boolean parseExpiration(ParserContext parserContext, Element element, BeanDefinitionBuilder attrBuilder) {
|
||||
boolean result = false;
|
||||
result |= parseExpiration(element, "region-ttl", "regionTimeToLive", attrBuilder);
|
||||
boolean result = parseExpiration(element, "region-ttl", "regionTimeToLive", attrBuilder);
|
||||
|
||||
result |= parseExpiration(element, "region-tti", "regionIdleTimeout", attrBuilder);
|
||||
result |= parseExpiration(element, "entry-ttl", "entryTimeToLive", attrBuilder);
|
||||
result |= parseExpiration(element, "entry-tti", "entryIdleTimeout", attrBuilder);
|
||||
result |= parseCustomExpiration(parserContext, element,"custom-entry-ttl","customEntryTimeToLive",attrBuilder);
|
||||
result |= parseCustomExpiration(parserContext, element,"custom-entry-tti","customEntryIdleTimeout",attrBuilder);
|
||||
|
||||
// TODO why?
|
||||
if (result) {
|
||||
// turn on statistics
|
||||
attrBuilder.addPropertyValue("statisticsEnabled", Boolean.TRUE);
|
||||
@@ -469,4 +430,4 @@ abstract class ParsingUtils {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user