Fixes JIRA issue SGF-203 involving the <gfe:*-region/> element's persistent, data-policy, and for client Regions, shortcut attributes.

This commit is contained in:
John Blum
2013-10-25 15:44:14 -07:00
parent c34adc9f6a
commit ff6c121d29
9 changed files with 682 additions and 164 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.data.gemfire;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.RegionFactory;
@@ -23,12 +24,13 @@ import com.gemstone.gemfire.cache.Scope;
/**
* @author David Turanski
*
* @author John Blum
*/
public class LocalRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> {
@Override
public void setScope(Scope scope) {
throw new UnsupportedOperationException("setScope() is not allowed for Local Regions");
throw new UnsupportedOperationException("Setting the Scope on Local Regions is not allowed.");
}
@Override
@@ -37,24 +39,38 @@ public class LocalRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> {
super.afterPropertiesSet();
}
/**
* Resolves the Data Policy used by this "local" GemFire Region (i.e. locally Scoped; Scope.LOCAL) based on the
* enumerated value from com.gemstone.gemfire.cache.RegionShortcuts (LOCAL, LOCAL_PERSISTENT, LOCAL_HEAP_LRU,
* LOCAL_OVERFLOW, and LOCAL_PERSISTENT_OVERFLOW), but without consideration of the Eviction settings.
* <p/>
* @param regionFactory the GemFire RegionFactory used to created the Local Region.
* @param persistent a boolean value indicating whether the Local Region should persist it's data.
* @param dataPolicy requested Data Policy as set by the user in the Spring GemFire configuration meta-data.
* @see com.gemstone.gemfire.cache.DataPolicy
* @see com.gemstone.gemfire.cache.RegionFactory
* @see com.gemstone.gemfire.cache.RegionShortcut
*/
@Override
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
if (dataPolicy != null) {
dataPolicy = dataPolicy.toUpperCase();
if ("NORMAL".equals(dataPolicy) || dataPolicy == null) {
regionFactory.setDataPolicy(DataPolicy.NORMAL);
}
else if ("PRELOADED".equals(dataPolicy)) {
regionFactory.setDataPolicy(DataPolicy.PRELOADED);
}
else if ("EMPTY".equals(dataPolicy)) {
Assert.isTrue(persistent == null || !persistent, "Cannot have persistence on an empty region");
regionFactory.setDataPolicy(DataPolicy.EMPTY);
}
else {
throw new IllegalArgumentException("Data policy '" + dataPolicy
+ "' is unsupported or invalid for local regions.");
}
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
Assert.isTrue(resolvedDataPolicy != null || (resolvedDataPolicy == null && !StringUtils.hasText(dataPolicy)),
String.format("Data Policy '%1$s' is invalid.", dataPolicy));
if (resolvedDataPolicy == null || DataPolicy.NORMAL.equals(resolvedDataPolicy)) {
// NOTE this is safe since a LOCAL Scoped NORMAL Region requiring persistence can be satisfied with
// PERSISTENT_REPLICATE, per the RegionShortcut.LOCAL_PERSISTENT
regionFactory.setDataPolicy(isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.NORMAL);
}
else if (DataPolicy.PRELOADED.equals(resolvedDataPolicy)) {
// NOTE this is safe since a LOCAL Scoped PRELOADED Region requiring persistence can be satisfied with
// PERSISTENT_REPLICATE, per the RegionShortcut.LOCAL_PERSISTENT
regionFactory.setDataPolicy(isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.PRELOADED);
}
else {
throw new IllegalArgumentException(String.format("Data Policy '%1$s' is not supported in Local Regions.",
dataPolicy));
}
}

View File

@@ -26,41 +26,41 @@ import com.gemstone.gemfire.cache.RegionFactory;
/**
* @author David Turanski
*
* @author John Blum
*/
public class PartitionedRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> {
@Override
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
if (dataPolicy != null) {
DataPolicy dp = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(dp, "Data policy " + dataPolicy + " is invalid");
Assert.isTrue(dp.withPartitioning(), "Data Policy " + dp.toString()
+ " is not supported in partitioned regions");
if (!isPersistent()) {
regionFactory.setDataPolicy(dp);
}
else {
Assert.isTrue(dp.withPersistence(), "Data Policy " + dp.toString()
+ "is invalid when persistent is false");
regionFactory.setDataPolicy(dp);
}
return;
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicy));
// Validate that the user-defined Data Policy matches the appropriate Spring GemFire XML namespace
// configuration meta-data element for Region (i.e. <gfe:partitioned-region .../>)!
Assert.isTrue(resolvedDataPolicy.withPartitioning(), String.format(
"Data Policy '%1$s' is not supported in Partitioned Regions.", resolvedDataPolicy));
// Validate that the data-policy and persistent attributes are compatible when specified!
assertDataPolicyAndPersistentAttributesAreCompatible(resolvedDataPolicy);
regionFactory.setDataPolicy(resolvedDataPolicy);
}
if (isPersistent()) {
// check first for GemFire 6.5
if (ConcurrentMap.class.isAssignableFrom(Region.class)) {
regionFactory.setDataPolicy(DataPolicy.PERSISTENT_PARTITION);
}
else {
throw new IllegalArgumentException(
"Can define persistent partitions only from GemFire 6.5 onwards - current version is ["
+ CacheFactory.getVersion() + "]");
}
else if (isPersistent()) {
// first, check the presence of GemFire 6.5 or Higher
Assert.isTrue(isGemFireVersion65orHigher(), String.format(
"Can define Persistent Partitioned Regions only from GemFire 6.5 onwards; current version is [%1$s]",
CacheFactory.getVersion()));
regionFactory.setDataPolicy(DataPolicy.PERSISTENT_PARTITION);
}
else {
regionFactory.setDataPolicy(DataPolicy.PARTITION);
}
}
private boolean isGemFireVersion65orHigher() {
return ConcurrentMap.class.isAssignableFrom(Region.class);
}
}

View File

@@ -24,14 +24,12 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.wan.GatewaySenderWrapper;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import com.gemstone.gemfire.cache.AttributesFactory;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheWriter;
@@ -56,20 +54,19 @@ import com.gemstone.gemfire.cache.wan.GatewaySender;
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
*/
public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> implements DisposableBean, SmartLifecycle {
private boolean autoStartup = true;
private boolean running;
protected final Log log = LogFactory.getLog(getClass());
private boolean destroy = false;
private boolean autoStartup = true;
private boolean close = true;
private boolean destroy = false;
private boolean running;
private Resource snapshot;
private Boolean enableGateway;
private Boolean persistent;
private CacheListener<K, V> cacheListeners[];
@@ -77,31 +74,23 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
private CacheWriter<K, V> cacheWriter;
private Object gatewaySenders[];
private Object asyncEventQueues[];
private Object[] asyncEventQueues;
private Object[] gatewaySenders;
private RegionAttributes<K, V> attributes;
private Resource snapshot;
private Scope scope;
private Boolean persistent;
private Boolean enableGateway;
private String hubId;
private String diskStoreName;
private String dataPolicy;
private Region<K, V> region;
private String diskStoreName;
private String hubId;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
region = getRegion();
postProcess(region);
postProcess(getRegion());
}
@Override
@@ -110,11 +99,12 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
Cache c = (Cache) cache;
if (attributes != null)
if (attributes != null) {
AttributesFactory.validateAttributes(attributes);
}
final RegionFactory<K, V> regionFactory = (attributes != null ? c.createRegionFactory(attributes) : c
.<K, V> createRegionFactory());
final RegionFactory<K, V> regionFactory = (attributes != null ? c.createRegionFactory(attributes) :
c.<K, V> createRegionFactory());
if (hubId != null) {
enableGateway = enableGateway == null ? true : enableGateway;
@@ -122,12 +112,14 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
regionFactory.setGatewayHubId(hubId);
}
if (enableGateway != null) {
if (enableGateway) {
Assert.notNull(hubId, "enableGateway requires the hubId property to be true");
}
regionFactory.setEnableGateway(enableGateway);
}
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {
regionFactory.addCacheListener(listener);
@@ -135,9 +127,8 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
}
if (!ObjectUtils.isEmpty(gatewaySenders)) {
Assert.isTrue(
hubId == null,
"It is invalid to configure a region with both a hubId and gatewaySenders. Note that the enableGateway and hubId properties are deprecated since Gemfire 7.0");
Assert.isTrue(hubId == null, "It is invalid to configure a region with both a hubId and gatewaySenders."
+ " Note that the enableGateway and hubId properties are deprecated since Gemfire 7.0");
for (Object gatewaySender : gatewaySenders) {
regionFactory.addGatewaySenderId(((GatewaySender) gatewaySender).getId());
}
@@ -169,60 +160,56 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
if (attributes != null) {
Assert.state(!attributes.isLockGrantor() || scope.isGlobal(),
"Lock grantor only applies to a global scoped region");
"Lock grantor only applies to a global scoped region");
}
// get underlying AttributesFactory
postProcess(findAttrFactory(regionFactory));
Region<K, V> reg = regionFactory.create(regionName);
Region<K, V> region = regionFactory.create(regionName);
log.info("Created new cache region [" + regionName + "]");
if (snapshot != null) {
reg.loadSnapshot(snapshot.getInputStream());
region.loadSnapshot(snapshot.getInputStream());
}
if (attributes != null && attributes.isLockGrantor()) {
reg.becomeLockGrantor();
region.becomeLockGrantor();
}
return reg;
return region;
}
/**
* This validates the configured data policy and may override it, taking
* into account the persistent attribute and constraints for the region type
* @param regionFactory
* @param persistent
* @param dataPolicy requested data policy
* Validates the configured Data Policy and may override it, taking into account the 'persistent' attribute
* and constraints for the Region type.
* <p/>
* @param regionFactory the GemFire RegionFactory used to created the Local Region.
* @param persistent a boolean value indicating whether the Local Region should persist it's data.
* @param dataPolicy requested Data Policy as set by the user in the Spring GemFire configuration meta-data.
* @see com.gemstone.gemfire.cache.DataPolicy
* @see com.gemstone.gemfire.cache.RegionFactory
*/
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
if (dataPolicy != null) {
regionFactory.setDataPolicy(convertAndValidate(dataPolicy));
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicy));
assertDataPolicyAndPersistentAttributesAreCompatible(resolvedDataPolicy);
regionFactory.setDataPolicy(resolvedDataPolicy);
}
else {
regionFactory.setDataPolicy(isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.DEFAULT);
}
}
private DataPolicy convertAndValidate(final String dataPolicy) {
final DataPolicy dataPolicyType = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(dataPolicyType, String.format("Data policy %1$s is invalid", dataPolicy));
if (dataPolicyType.withPersistence()) {
// NOTE isNotPersistent means the user explicitly set the persistent attribute for the Region to false,
// and this conflicts with the Data Policy (via the that was set by the user, which indicates persistence.
Assert.isTrue(!isNotPersistent(), String.format("Data policy %1$s is invalid when persistent is false",
dataPolicy));
}
return dataPolicyType;
}
@SuppressWarnings("unchecked")
private AttributesFactory<K, V> findAttrFactory(RegionFactory<K, V> regionFactory) {
Field attrField = ReflectionUtils.findField(RegionFactory.class, "attrsFactory", AttributesFactory.class);
ReflectionUtils.makeAccessible(attrField);
return (AttributesFactory<K, V>) ReflectionUtils.getField(attrField, regionFactory);
Field attrsFactoryField = ReflectionUtils.findField(RegionFactory.class, "attrsFactory",
AttributesFactory.class);
ReflectionUtils.makeAccessible(attrsFactoryField);
return (AttributesFactory<K, V>) ReflectionUtils.getField(attrsFactoryField, regionFactory);
}
/**
@@ -252,32 +239,22 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
@Override
public void destroy() throws Exception {
if (region != null) {
if (getRegion() != null) {
if (close) {
if (!region.getRegionService().isClosed()) {
if (!getRegion().getRegionService().isClosed()) {
try {
region.close();
region = null;
getRegion().close();
} catch (Exception cce) {
// nothing to see folks, move on.
}
}
} if (destroy) {
region.destroyRegion();
region = null;
getRegion().destroyRegion();
}
}
}
/**
* Indicates whether the region referred by this factory bean, will be
* destroyed on shutdown (default false).
*/
public void setDestroy(boolean destroy) {
this.destroy = destroy;
}
/**
* Indicates whether the region referred by this factory bean, will be
* closed on shutdown (default true).
@@ -286,6 +263,14 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
this.close = close;
}
/**
* Indicates whether the region referred by this factory bean, will be
* destroyed on shutdown (default false).
*/
public void setDestroy(boolean destroy) {
this.destroy = destroy;
}
/**
* Sets the snapshots used for loading a newly <i>created</i> region. That
* is, the snapshot will be used <i>only</i> when a new region is created -
@@ -400,12 +385,54 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
this.attributes = attributes;
}
protected boolean isPersistent() {
return persistent != null && persistent;
/**
* Validates that the settings for Data Policy and the 'persistent' attribute in <gfe:*-region/> elements
* are compatible.
* <p/>
* @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 assertDataPolicyAndPersistentAttributesAreCompatible(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));
}
}
/**
* Returns true when the user explicitly specified a value for the persistent attribute and it is true. If the
* persistent attribute was not explicitly specified, then the persistence setting is implicitly undefined
* and will be determined by the Data Policy.
* <p/>
* @return true when the user specified an explicit value for the persistent attribute and it is true;
* false otherwise.
* @see #isNotPersistent()
*/
protected boolean isPersistent() {
return Boolean.TRUE.equals(persistent);
}
/**
* Returns true when the user explicitly specified a value for the persistent attribute and it is false. If the
* persistent attribute was not explicitly specified, then the persistence setting is implicitly undefined
* and will be determined by the Data Policy.
* <p/>
* @return true when the user specified an explicit value for the persistent attribute and it is false;
* false otherwise.
* @see #isPersistent()
*/
protected boolean isNotPersistent() {
return persistent != null && !persistent;
return Boolean.FALSE.equals(persistent);
}
/* (non-Javadoc)
@@ -413,12 +440,11 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
*/
@Override
public void start() {
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (Object obj : gatewaySenders) {
GatewaySender gws = (GatewaySender) obj;
if (!gws.isManualStart() && !gws.isRunning()) {
if (!(gws.isManualStart() || gws.isRunning())) {
gws.start();
}
}
@@ -435,9 +461,8 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (Object obj : gatewaySenders) {
GatewaySender gws = (GatewaySender) obj;
gws.stop();
}
((GatewaySender) obj).stop();
}
}
}
this.running = false;
@@ -475,4 +500,5 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
stop();
callback.run();
}
}

View File

@@ -39,16 +39,19 @@ public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>,
protected final Log log = LogFactory.getLog(getClass());
private String beanName;
private GemFireCache cache;
private String name;
private Region<K, V> region;
private String beanName;
private String name;
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Cache property must be set");
name = (!StringUtils.hasText(name) ? beanName : name);
Assert.hasText(name, "Name (or beanName) property must be set");
synchronized (cache) {
region = cache.getRegion(name);
if (region != null) {
@@ -116,4 +119,5 @@ public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>,
protected Region<K, V> getRegion() {
return region;
}
}
}

View File

@@ -22,32 +22,35 @@ import com.gemstone.gemfire.cache.RegionFactory;
/**
* @author David Turanski
*
* @author John Blum
*/
public class ReplicatedRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> {
@Override
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
DataPolicy dp = null;
if (dataPolicy != null) {
dataPolicy = dataPolicy.toUpperCase();
if ("EMPTY".equals(dataPolicy)) {
Assert.isTrue(persistent == null || !persistent, "Cannot have persistence on an empty region");
dp = DataPolicy.EMPTY;
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicy));
// Validate that the data-policy and persistent attributes are compatible when specified!
assertDataPolicyAndPersistentAttributesAreCompatible(resolvedDataPolicy);
if (DataPolicy.EMPTY.equals(resolvedDataPolicy)) {
resolvedDataPolicy = DataPolicy.EMPTY;
}
else {
dp = new DataPolicyConverter().convert(dataPolicy);
Assert.notNull(dp, "Data policy " + dataPolicy + " is invalid");
Assert.isTrue(dp.withReplication(), "Data policy " + dataPolicy
+ " is invalid or unsupported in replicated regions");
if (isPersistent()) {
Assert.isTrue(dp.withPersistence(), "Data policy " + dataPolicy
+ " is invalid when persistent is false");
}
// Validate that the user-defined Data Policy matches the appropriate Spring GemFire XML namespace
// configuration meta-data element for Region (i.e. <gfe:replicated-region .../>)!
Assert.isTrue(resolvedDataPolicy.withReplication(), String.format("" +
"Data Policy '%1$s' is not supported in Replicated Regions.", resolvedDataPolicy));
}
regionFactory.setDataPolicy(resolvedDataPolicy);
}
else {
dp = (isPersistent()) ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.REPLICATE;
regionFactory.setDataPolicy(isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.REPLICATE);
}
regionFactory.setDataPolicy(dp);
}
}