+ split ClientRegionFactory from RegionFactoryBean to cope with the different APIs used for configuring regions
This commit is contained in:
Costin Leau
2011-08-26 17:39:06 +03:00
parent 6fae163c8e
commit f617e8c657
6 changed files with 255 additions and 45 deletions

View File

@@ -22,6 +22,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
@@ -32,6 +34,7 @@ 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;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.RegionFactory;
@@ -41,6 +44,9 @@ import com.gemstone.gemfire.cache.Scope;
* FactoryBean for creating generic GemFire {@link Region}s. Will try to first locate the region (by name)
* and, in case none if found, proceed to creating one using the given settings.
*
* Note that this factory bean allows for very flexible creation of GemFire {@link Region}. For "client" regions
* however, see {@link ClientRegionFactoryBean} which offers easier configuration and defaults.
*
* @author Costin Leau
*/
public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> implements DisposableBean {
@@ -68,12 +74,16 @@ public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> imple
@Override
protected Region<K, V> lookupFallback(Cache cache, String regionName) throws Exception {
protected Region<K, V> lookupFallback(GemFireCache cache, String regionName) throws Exception {
Assert.isTrue(cache instanceof Cache, "Unable to create regions from " + cache);
Cache c = (Cache) cache;
if (attributes != null)
AttributesFactory.validateAttributes(attributes);
RegionFactory<K, V> regionFactory = (attributes != null ? cache.createRegionFactory(attributes)
: cache.<K, V> createRegionFactory());
final RegionFactory<K, V> regionFactory = (attributes != null ? c.createRegionFactory(attributes)
: c.<K, V> createRegionFactory());
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {

View File

@@ -25,7 +25,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
/**
@@ -40,7 +40,7 @@ public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>,
protected final Log log = LogFactory.getLog(getClass());
private String beanName;
private Cache cache;
private GemFireCache cache;
private String name;
Region<K, V> region;
@@ -68,7 +68,7 @@ public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>,
* @param regionName region name
* @throws Exception
*/
protected Region<K, V> lookupFallback(Cache cache, String regionName) throws Exception {
protected Region<K, V> lookupFallback(GemFireCache cache, String regionName) throws Exception {
throw new BeanInitializationException("Cannot find region [" + regionName + "] in cache " + cache);
}
@@ -94,7 +94,7 @@ public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>,
* @see org.springframework.data.gemfire.CacheFactoryBean
* @param cache the cache to set
*/
public void setCache(Cache cache) {
public void setCache(GemFireCache cache) {
this.cache = cache;
}

View File

@@ -16,16 +16,27 @@
package org.springframework.data.gemfire.client;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.data.gemfire.RegionFactoryBean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.AttributesFactory;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.ClientRegionFactory;
import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
import com.gemstone.gemfire.cache.client.Pool;
/**
@@ -33,13 +44,110 @@ import com.gemstone.gemfire.cache.client.Pool;
*
* @author Costin Leau
*/
public class ClientRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> implements BeanFactoryAware {
public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> implements BeanFactoryAware,
DisposableBean {
private static final Log log = LogFactory.getLog(ClientRegionFactoryBean.class);
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 ClientRegionShortcut shortcut = null;
private DataPolicy dataPolicy;
private RegionAttributes<K, V> attributes;
private Region<K, V> region;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
postProcess(region);
}
@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;
// 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;
}
}
s = ClientRegionShortcut.LOCAL;
}
ClientRegionFactory<K, V> factory = c.createClientRegionFactory(s);
// map the attributes onto the client
if (attributes != null) {
CacheListener<K, V>[] listeners = attributes.getCacheListeners();
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {
factory.addCacheListener(listener);
}
}
factory.setCloningEnabled(attributes.getCloningEnabled());
factory.setConcurrencyLevel(attributes.getConcurrencyLevel());
factory.setCustomEntryIdleTimeout(attributes.getCustomEntryIdleTimeout());
factory.setCustomEntryTimeToLive(attributes.getCustomEntryTimeToLive());
factory.setDiskStoreName(attributes.getDiskStoreName());
factory.setDiskSynchronous(attributes.isDiskSynchronous());
factory.setEntryIdleTimeout(attributes.getEntryIdleTimeout());
factory.setEntryTimeToLive(attributes.getEntryTimeToLive());
factory.setEvictionAttributes(attributes.getEvictionAttributes());
factory.setInitialCapacity(attributes.getInitialCapacity());
factory.setKeyConstraint(attributes.getKeyConstraint());
factory.setLoadFactor(attributes.getLoadFactor());
factory.setPoolName(attributes.getPoolName());
factory.setRegionIdleTimeout(attributes.getRegionIdleTimeout());
factory.setRegionTimeToLive(attributes.getRegionTimeToLive());
factory.setStatisticsEnabled(attributes.getStatisticsEnabled());
factory.setValueConstraint(attributes.getValueConstraint());
}
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {
factory.addCacheListener(listener);
}
}
if (StringUtils.hasText(poolName)) {
// try to eagerly initialize the pool name, if defined as a bean
if (beanFactory.isTypeMatch(poolName, Pool.class)) {
if (log.isDebugEnabled()) {
log.debug("Found bean definition for pool '" + poolName + "'. Eagerly initializing it...");
}
beanFactory.getBean(poolName, Pool.class);
}
factory.setPoolName(poolName);
}
Region<K, V> reg = factory.create(regionName);
log.info("Created new cache region [" + regionName + "]");
if (snapshot != null) {
reg.loadSnapshot(snapshot.getInputStream());
}
return reg;
}
protected void postProcess(Region<K, V> region) {
if (!ObjectUtils.isEmpty(interests)) {
for (Interest<K> interest : interests) {
@@ -54,22 +162,6 @@ public class ClientRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> imple
}
}
@Override
protected void postProcess(AttributesFactory<K, V> attrFactory) {
if (StringUtils.hasText(poolName)) {
// try to eagerly initialize the pool name, if defined as a bean
if (beanFactory.isTypeMatch(poolName, Pool.class)) {
if (log.isDebugEnabled()) {
log.debug("Found bean definition for pool '" + poolName + "'. Eagerly initializing it...");
}
beanFactory.getBean(poolName, Pool.class);
}
attrFactory.setPoolName(poolName);
}
}
@Override
public void destroy() throws Exception {
Region<K, V> region = getObject();
// unregister interests
@@ -89,7 +181,21 @@ public class ClientRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> imple
log.warn("Cannot unregister cache interests", ex);
}
super.destroy();
if (region != null) {
if (close) {
if (!region.getCache().isClosed()) {
try {
region.close();
} catch (CacheClosedException cce) {
// nothing to see folks, move on.
}
}
}
else if (destroy) {
region.destroyRegion();
}
}
region = null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
@@ -132,4 +238,92 @@ public class ClientRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> imple
Assert.notNull(pool, "pool cannot be null");
setPoolName(pool.getName());
}
/**
* Initializes the client using a GemFire {@link ClientRegionShortcut}.
* The recommended way for creating clients since it covers all the major scenarios with minimal
* configuration.
*
* @param shortcut
*/
public void setShortcut(ClientRegionShortcut shortcut) {
this.shortcut = shortcut;
}
/**
* 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.
*
* @param destroy whether or not to destroy the region
*
* @see #setClose(boolean)
*/
public void setDestroy(boolean destroy) {
this.destroy = destroy;
if (destroy) {
close = false;
}
}
/**
* 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.
*
* @param close whether to close or not the region
* @see #setDestroy(boolean)
*/
public void setClose(boolean close) {
this.close = close;
if (close) {
destroy = false;
}
}
/**
* 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 - if the region
* already exists, no loading will be performed.
*
* @see #setName(String)
* @param snapshot the snapshot to set
*/
public void setSnapshot(Resource snapshot) {
this.snapshot = snapshot;
}
/**
* Sets the cache listeners used for the region used by this factory.
* Used only when a new region is created.Overrides the settings
* specified through {@link #setAttributes(RegionAttributes)}.
*
* @param cacheListeners the cacheListeners to set on a newly created region
*/
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
this.cacheListeners = cacheListeners;
}
/**
* Sets the data policy. Used only when a new region is created.
*
* @param dataPolicy the region data policy
*/
public void setDataPolicy(DataPolicy dataPolicy) {
this.dataPolicy = dataPolicy;
}
/**
* 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<K, V> attributes) {
this.attributes = attributes;
}
}

View File

@@ -17,7 +17,6 @@
package org.springframework.data.gemfire.config;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
@@ -30,10 +29,7 @@ import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.Scope;
/**
* Parser for &lt;client-region;gt; definitions.
@@ -57,11 +53,11 @@ class ClientRegionParser extends AliasReplacingBeanDefinitionParser {
// 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
builder.addPropertyValue("scope", Scope.LOCAL);
ParsingUtils.setPropertyValue(element, builder, "data-policy", "dataPolicy");
ParsingUtils.setPropertyValue(element, builder, "name", "name");
ParsingUtils.setPropertyValue(element, builder, "pool-name", "poolName");
ParsingUtils.setPropertyValue(element, builder, "shortcut", "shortcut");
// set the persistent policy
String attr = element.getAttribute("persistent");
@@ -70,15 +66,8 @@ class ClientRegionParser extends AliasReplacingBeanDefinitionParser {
if (Boolean.parseBoolean(attr)) {
// check first for GemFire 6.5
if (ConcurrentMap.class.isAssignableFrom(Region.class)) {
builder.addPropertyValue("dataPolicy", DataPolicy.PERSISTENT_REPLICATE);
frozenDataPolicy = true;
}
else {
parserContext.getReaderContext().error(
"Can define persistent partitions only from GemFire 6.5 onwards - current version is ["
+ CacheFactory.getVersion() + "]", element);
}
builder.addPropertyValue("dataPolicy", DataPolicy.PERSISTENT_REPLICATE);
frozenDataPolicy = true;
}
attr = element.getAttribute("cache-ref");