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:
John Blum
2013-10-23 14:23:20 -07:00
parent 8deda7804c
commit 4ed76947d0
11 changed files with 602 additions and 287 deletions

View File

@@ -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;
}
}
}

View File

@@ -33,10 +33,9 @@ import com.gemstone.gemfire.cache.DataPolicy;
/**
* Parser for &lt;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");
}
}

View File

@@ -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;
}
}
}