* @author Costin Leau
* @author David Turanski
+ * @author John Blum
*/
public class ClientRegionFactoryBean extends RegionLookupFactoryBean implements BeanFactoryAware,
DisposableBean {
- private static final Log log = LogFactory.getLog(ClientRegionFactoryBean.class);
-
+ private boolean close = true;
private boolean destroy = false;
- private boolean close = true;
-
- private Resource snapshot;
-
- private CacheListener cacheListeners[];
-
- private Interest[] interests;
-
- private String poolName;
-
private BeanFactory beanFactory;
+ private Boolean persistent;
+
+ private CacheListener[] cacheListeners;
+
private ClientRegionShortcut shortcut = null;
private DataPolicy dataPolicy;
+ private Interest[] interests;
+
private RegionAttributes attributes;
- private Region region;
+ private Resource snapshot;
private String diskStoreName;
-
- private String dataPolicyName;
+ private String poolName;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
- region = getRegion();
- postProcess(region);
+ postProcess(getRegion());
}
@Override
protected Region lookupFallback(GemFireCache cache, String regionName) throws Exception {
Assert.isTrue(cache instanceof ClientCache, "Unable to create regions from " + cache);
- ClientCache c = (ClientCache) cache;
+ // TODO use of GemFire internal 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");
-
-
- if (StringUtils.hasText(dataPolicyName)) {
- dataPolicy = new DataPolicyConverter().convert(dataPolicyName);
- Assert.notNull(dataPolicy, "Data policy " + dataPolicyName + " is invalid");
+ Assert.isTrue(((GemFireCacheImpl) cache).isClient(), "A client-cache instance is required.");
}
+ final ClientCache clientCache = (ClientCache) cache;
+
// first look at shortcut
- ClientRegionShortcut s = null;
-
- 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;
- }
- else if (DataPolicy.NORMAL.equals(this.dataPolicy)) {
- s = ClientRegionShortcut.CACHING_PROXY;
- }
- else {
- s = ClientRegionShortcut.LOCAL;
- }
- }
- else {
- s = ClientRegionShortcut.LOCAL;
- }
- }
- else {
- s = shortcut;
- }
-
- ClientRegionFactory factory = c.createClientRegionFactory(s);
+ ClientRegionFactory factory = clientCache.createClientRegionFactory(resolveClientRegionShortcut());
// map the attributes onto the client
if (attributes != null) {
@@ -173,7 +134,8 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
}
factory.setPoolName(poolName);
- } else {
+ }
+ else {
Pool pool = beanFactory.getBean(Pool.class);
factory.setPoolName(pool.getName());
}
@@ -182,26 +144,63 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
factory.setDiskStoreName(diskStoreName);
}
- Region reg = factory.create(regionName);
+ Region region = factory.create(regionName);
log.info("Created new cache region [" + regionName + "]");
+
if (snapshot != null) {
- reg.loadSnapshot(snapshot.getInputStream());
+ region.loadSnapshot(snapshot.getInputStream());
}
- return reg;
+ return region;
+ }
+
+ protected ClientRegionShortcut resolveClientRegionShortcut() {
+ ClientRegionShortcut resolvedShortcut = this.shortcut;
+
+ if (resolvedShortcut == null) {
+ if (this.dataPolicy != null) {
+ assertDataPolicyAndPersistentAttributeAreCompatible(this.dataPolicy);
+
+ if (DataPolicy.EMPTY.equals(this.dataPolicy)) {
+ resolvedShortcut = ClientRegionShortcut.PROXY;
+ }
+ else if (DataPolicy.NORMAL.equals(this.dataPolicy)) {
+ resolvedShortcut = ClientRegionShortcut.CACHING_PROXY;
+ }
+ else if (DataPolicy.PERSISTENT_REPLICATE.equals(this.dataPolicy)) {
+ resolvedShortcut = ClientRegionShortcut.LOCAL_PERSISTENT;
+ }
+ else {
+ // NOTE the Data Policy validation is based on the ClientRegionShortcut initialization logic
+ // in com.gemstone.gemfire.internal.cache.GemFireCacheImpl.initializeClientRegionShortcuts
+ throw new IllegalArgumentException(String.format(
+ "Data Policy '%1$s' is invalid for Client Regions.", this.dataPolicy));
+ }
+ }
+ else {
+ resolvedShortcut = (isPersistent() ? ClientRegionShortcut.LOCAL_PERSISTENT
+ : ClientRegionShortcut.LOCAL);
+ }
+
+ }
+
+ // NOTE the ClientRegionShortcut and Persistent attribute will be compatible if the shortcut was derived from
+ // the Data Policy.
+ assertClientRegionShortcutAndPersistentAttributeAreCompatible(resolvedShortcut);
+
+ return resolvedShortcut;
}
protected void postProcess(Region region) {
if (!ObjectUtils.isEmpty(interests)) {
for (Interest 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());
}
}
}
@@ -247,6 +246,20 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
region = null;
}
+ /**
+ * Sets the region attributes used for the region used by this factory.
+ * Allows maximum control in specifying the region settings. Used only when
+ * a new region is created. Note that using this method allows for advanced
+ * customization of the region - while it provides a lot of flexibility,
+ * note that it's quite easy to create misconfigured regions (especially in
+ * a client/server scenario).
+ *
+ * @param attributes the attributes to set on a newly created region
+ */
+ public void setAttributes(RegionAttributes attributes) {
+ this.attributes = attributes;
+ }
+
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
@@ -269,19 +282,9 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
return interests;
}
- /**
- * Sets the pool name used by this client.
- *
- * @param poolName
- */
- public void setPoolName(String poolName) {
- Assert.hasText(poolName, "pool name is required");
- this.poolName = poolName;
- }
-
/**
* Sets the pool used by this client.
- *
+ *
* @param pool
*/
public void setPool(Pool pool) {
@@ -289,6 +292,16 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
setPoolName(pool.getName());
}
+ /**
+ * Sets the pool name used by this client.
+ *
+ * @param poolName
+ */
+ public void setPoolName(String poolName) {
+ Assert.hasText(poolName, "pool name is required");
+ this.poolName = poolName;
+ }
+
/**
* Initializes the client using a GemFire {@link ClientRegionShortcut}. The
* recommended way for creating clients since it covers all the major
@@ -355,23 +368,77 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
this.cacheListeners = cacheListeners;
}
+ protected void assertClientRegionShortcutAndPersistentAttributeAreCompatible(final ClientRegionShortcut resolvedShortcut) {
+ final boolean persistentNotSpecified = (this.persistent == null);
+
+ if (ClientRegionShortcut.LOCAL_PERSISTENT.equals(resolvedShortcut)
+ || ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW.equals(resolvedShortcut)) {
+ Assert.isTrue(persistentNotSpecified || isPersistent(), String.format(
+ "Client Region Shortcut '%1$s' is invalid when persistent is false.", resolvedShortcut));
+ }
+ else {
+ Assert.isTrue(persistentNotSpecified || isNotPersistent(), String.format(
+ "Client Region Shortcut '%1$s' is invalid when persistent is true.", resolvedShortcut));
+ }
+ }
/**
- * Sets the data policy. Used only when a new region is created.
- *
- * @param dataPolicy the region data policy
+ * Validates that the settings for Data Policy and the 'persistent' attribute in elements
+ * are compatible.
+ *
+ * @param resolvedDataPolicy the GemFire Data Policy resolved form the Spring GemFire XML namespace configuration
+ * meta-data.
+ * @see #isPersistent()
+ * @see #isNotPersistent()
+ * @see com.gemstone.gemfire.cache.DataPolicy
+ */
+ protected void assertDataPolicyAndPersistentAttributeAreCompatible(final DataPolicy resolvedDataPolicy) {
+ final boolean persistentNotSpecified = (this.persistent == null);
+
+ if (resolvedDataPolicy.withPersistence()) {
+ Assert.isTrue(persistentNotSpecified || isPersistent(), String.format(
+ "Data Policy '%1$s' is invalid when persistent is false.", resolvedDataPolicy));
+ }
+ else {
+ // NOTE otherwise, the Data Policy is with persistence, so...
+ Assert.isTrue(persistentNotSpecified || isNotPersistent(), String.format(
+ "Data Policy '%1$s' is invalid when persistent is true.", resolvedDataPolicy));
+ }
+ }
+
+ /**
+ * Sets the Data Policy. Used only when a new Region is created.
+ *
+ * @param dataPolicy the client Region's Data Policy.
+ * @see com.gemstone.gemfire.cache.DataPolicy
*/
public void setDataPolicy(DataPolicy dataPolicy) {
this.dataPolicy = dataPolicy;
}
-
+
/**
- * An alternative way to set the data policy as a string. Useful for
- * property placeholders, etc.
- *
- * @param dataPolicyName
+ * An alternate way to set the Data Policy, using the String name of the enumerated value.
+ *
+ * @param dataPolicyName the enumerated value String name of the Data Policy.
+ * @see com.gemstone.gemfire.cache.DataPolicy
+ * @see #setDataPolicy(com.gemstone.gemfire.cache.DataPolicy)
+ * @deprecated use setDataPolicy(:DataPolicy) instead.
*/
public void setDataPolicyName(String dataPolicyName) {
- this.dataPolicyName = dataPolicyName;
+ final DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicyName);
+ Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicyName));
+ setDataPolicy(resolvedDataPolicy);
+ }
+
+ protected boolean isPersistent() {
+ return Boolean.TRUE.equals(persistent);
+ }
+
+ protected boolean isNotPersistent() {
+ return Boolean.FALSE.equals(persistent);
+ }
+
+ public void setPersistent(final boolean persistent) {
+ this.persistent = persistent;
}
/**
@@ -382,17 +449,4 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
this.diskStoreName = diskStoreName;
}
- /**
- * Sets the region attributes used for the region used by this factory.
- * Allows maximum control in specifying the region settings. Used only when
- * a new region is created. Note that using this method allows for advanced
- * customization of the region - while it provides a lot of flexibility,
- * note that it's quite easy to create misconfigured regions (especially in
- * a client/server scenario).
- *
- * @param attributes the attributes to set on a newly created region
- */
- public void setAttributes(RegionAttributes attributes) {
- this.attributes = attributes;
- }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/springframework/data/gemfire/config/ClientRegionParser.java b/src/main/java/org/springframework/data/gemfire/config/ClientRegionParser.java
index 7e61d11b..642cc283 100644
--- a/src/main/java/org/springframework/data/gemfire/config/ClientRegionParser.java
+++ b/src/main/java/org/springframework/data/gemfire/config/ClientRegionParser.java
@@ -29,14 +29,11 @@ import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
-import com.gemstone.gemfire.cache.DataPolicy;
-
/**
- * Parser for <client-region;gt; definitions.
- *
- * To avoid eager evaluations, the region interests are declared as nested
- * definition.
- *
+ * Parser for <client-region;gt; bean definitions.
+ *
+ * To avoid eager evaluations, the region interests are declared as nested definition.
+ *
* @author Costin Leau
* @author David Turanski
* @author John Blum
@@ -52,121 +49,103 @@ 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
+ validateDataPolicyShortcutMutualExclusion(element, parserContext);
+
+ String cacheRefAttributeValue = element.getAttribute("cache-ref");
+
+ builder.addPropertyReference("cache", (StringUtils.hasText(cacheRefAttributeValue) ? cacheRefAttributeValue
+ : 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, "persistent");
ParsingUtils.setPropertyValue(element, builder, "pool-name");
ParsingUtils.setPropertyValue(element, builder, "shortcut");
- // set the persistent policy
- String attr = element.getAttribute("persistent");
+ String diskStoreRefAttributeValue = element.getAttribute("disk-store-ref");
- boolean frozenDataPolicy = false;
-
- if (Boolean.parseBoolean(attr)) {
- // check first for GemFire 6.5
- builder.addPropertyValue("dataPolicy", DataPolicy.PERSISTENT_REPLICATE);
- frozenDataPolicy = 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);
-
- ParsingUtils.parseStatistics(element, attrBuilder);
-
- if (StringUtils.hasText(element.getAttribute("disk-store-ref"))) {
+ if (StringUtils.hasText(diskStoreRefAttributeValue)) {
ParsingUtils.setPropertyValue(element, builder, "disk-store-ref", "diskStoreName");
- builder.addDependsOn(element.getAttribute("disk-store-ref"));
+ builder.addDependsOn(diskStoreRefAttributeValue);
}
- boolean overwriteDataPolicy = false;
+ // Client RegionAttributes for overflow/eviction, expiration and statistics
+ BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
+ RegionAttributesFactoryBean.class);
- 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) {
- builder.addPropertyValue("dataPolicy", DataPolicy.NORMAL);
- }
+ ParsingUtils.parseOptionalRegionAttributes(parserContext, element, regionAttributesBuilder);
+ ParsingUtils.parseStatistics(element, regionAttributesBuilder);
+ ParsingUtils.parseEviction(parserContext, element, regionAttributesBuilder);
+ ParsingUtils.parseExpiration(parserContext, element, regionAttributesBuilder);
- builder.addPropertyValue("attributes", attrBuilder.getBeanDefinition());
-
-
+ builder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
ManagedList