diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java
index c0403123..64c3c1bd 100644
--- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java
@@ -16,19 +16,26 @@
package org.springframework.data.gemfire;
+import static java.util.stream.StreamSupport.stream;
import static org.springframework.data.gemfire.GemfireUtils.apacheGeodeProductName;
import static org.springframework.data.gemfire.GemfireUtils.apacheGeodeVersion;
import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.newBeanFactoryLocator;
+import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Properties;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.geode.GemFireCheckedException;
import org.apache.geode.GemFireException;
import org.apache.geode.cache.Cache;
@@ -36,20 +43,16 @@ import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DynamicRegionFactory;
import org.apache.geode.cache.GemFireCache;
+import org.apache.geode.cache.RegionService;
import org.apache.geode.cache.TransactionListener;
import org.apache.geode.cache.TransactionWriter;
import org.apache.geode.cache.util.GatewayConflictResolver;
-import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.internal.datasource.ConfigProperty;
import org.apache.geode.internal.jndi.JNDIInvoker;
import org.apache.geode.pdx.PdxSerializable;
import org.apache.geode.pdx.PdxSerializer;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
@@ -57,51 +60,56 @@ import org.springframework.context.Phased;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
+import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer;
+import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator;
-import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
/**
- * FactoryBean used to configure a GemFire peer Cache node. Allows either retrieval of an existing, opened Cache
- * or the creation of a new Cache instance.
- *
- * This class implements the {@link org.springframework.dao.support.PersistenceExceptionTranslator}
- * interface, as auto-detected by Spring's
- * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}, for AOP-based translation
- * of native Exceptions to Spring DataAccessExceptions. Hence, the presence of this class automatically enables
- * a PersistenceExceptionTranslationPostProcessor to translate GemFire Exceptions appropriately.
+ * Spring {@link FactoryBean} used to construct, configure and initialize a Pivotal GemFire/Apache Geode
+ * {@link Cache peer cache).
+ *
+ * Allows either retrieval of an existing, opened {@link Cache} or creation of a new {@link Cache}.
+ *
+ * This class implements the {@link PersistenceExceptionTranslator} interface and is auto-detected by Spring's
+ * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor} for AOP-based translation
+ * of native persistent store exceptions to Spring's {@link DataAccessException} hierarchy. Therefore, the presence
+ * of this class automatically enables a {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}
+ * to translate Pivotal GemFire/Apache Geode exceptions appropriately.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
- * @see org.springframework.beans.factory.BeanClassLoaderAware
- * @see org.springframework.beans.factory.BeanFactoryAware
- * @see org.springframework.beans.factory.BeanNameAware
- * @see org.springframework.beans.factory.FactoryBean
- * @see org.springframework.beans.factory.InitializingBean
- * @see org.springframework.beans.factory.DisposableBean
- * @see org.springframework.context.Phased
- * @see org.springframework.dao.support.PersistenceExceptionTranslator
+ * @see java.util.Properties
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.CacheFactory
* @see org.apache.geode.cache.GemFireCache
+ * @see org.apache.geode.cache.DynamicRegionFactory
+ * @see org.apache.geode.cache.RegionService
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
+ * @see org.apache.geode.cache.pdx.PdxSerializer
+ * @see org.springframework.beans.factory.BeanFactory
+ * @see org.springframework.beans.factory.DisposableBean
+ * @see org.springframework.beans.factory.FactoryBean
+ * @see org.springframework.beans.factory.InitializingBean
+ * @see org.springframework.context.Phased
+ * @see org.springframework.core.io.Resource
+ * @see org.springframework.dao.support.PersistenceExceptionTranslator
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ * @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
*/
@SuppressWarnings("unused")
-public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean,
- Phased, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
+public class CacheFactoryBean extends AbstractFactoryBeanSupport
+ implements DisposableBean, InitializingBean, PersistenceExceptionTranslator, Phased {
private boolean close = true;
private boolean useBeanFactoryLocator = false;
private int phase = -1;
- protected final Log log = LogFactory.getLog(getClass());
-
- private BeanFactory beanFactory;
-
private Boolean copyOnRead;
private Boolean enableAutoReconnect;
private Boolean pdxIgnoreUnreadFields;
@@ -111,13 +119,13 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
private Cache cache;
- private ClassLoader beanClassLoader;
-
private DynamicRegionSupport dynamicRegionSupport;
private Float criticalHeapPercentage;
private Float evictionHeapPercentage;
+ private GatewayConflictResolver gatewayConflictResolver;
+
protected GemfireBeanFactoryLocator beanFactoryLocator;
private Integer lockLease;
@@ -125,27 +133,34 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
private Integer messageSyncInterval;
private Integer searchTimeout;
+ private List peerCacheConfigurers = Collections.emptyList();
+
private List jndiDataSources;
private List transactionListeners;
- // Declared with type 'Object' for backward compatibility
- private Object gatewayConflictResolver;
- private Object pdxSerializer;
+ private PdxSerializer pdxSerializer;
+
+ private PeerCacheConfigurer compositePeerCacheConfigurer = (beanName, bean) ->
+ nullSafeList(peerCacheConfigurers).forEach(peerCacheConfigurer ->
+ peerCacheConfigurer.configure(beanName, bean));
private Properties properties;
private Resource cacheXml;
- private String beanName;
private String cacheResolutionMessagePrefix;
private String pdxDiskStoreName;
private TransactionWriter transactionWriter;
/**
- * @inheritDoc
+ * Initializes this {@link CacheFactoryBean} after properties have been set by the Spring container.
+ *
+ * @throws Exception if initialization fails.
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ * @see #initBeanFactoryLocator()
+ * @see #postProcessBeforeCacheInitialization(Properties)
*/
@Override
public void afterPropertiesSet() throws Exception {
@@ -153,66 +168,111 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
postProcessBeforeCacheInitialization(resolveProperties());
}
- /* (non-Javadoc) */
+ /**
+ * Initializes the {@link GemfireBeanFactoryLocator} if {@link #isUseBeanFactoryLocator()} returns {@literal true}
+ * and an existing {@link #getBeanFactoryLocator()} is not already present.
+ *
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator#newBeanFactoryLocator(BeanFactory, String)
+ * @see #isUseBeanFactoryLocator()
+ * @see #getBeanFactoryLocator()
+ * @see #getBeanFactory()
+ * @see #getBeanName()
+ */
private void initBeanFactoryLocator() {
- if (useBeanFactoryLocator && beanFactoryLocator == null) {
- beanFactoryLocator = newBeanFactoryLocator(this.beanFactory, this.beanName);
+ if (isUseBeanFactoryLocator() && getBeanFactoryLocator() == null) {
+ this.beanFactoryLocator = newBeanFactoryLocator(getBeanFactory(), getBeanName());
}
}
- /* (non-Javadoc) */
+ /**
+ * Post processes this {@link CacheFactoryBean} before cache initialization.
+ *
+ * This is also the point at which any configured {@link PeerCacheConfigurer} beans are called.
+ *
+ * @param gemfireProperties {@link Properties} used to configure Pivotal GemFire/Apache Geode.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ * @see java.util.Properties
+ */
protected void postProcessBeforeCacheInitialization(Properties gemfireProperties) {
+
gemfireProperties.setProperty("disable-auto-reconnect", String.valueOf(
!Boolean.TRUE.equals(getEnableAutoReconnect())));
+
gemfireProperties.setProperty("use-cluster-configuration", String.valueOf(
Boolean.TRUE.equals(getUseClusterConfiguration())));
+
+ applyPeerCacheConfigurers();
}
/* (non-Javadoc) */
- protected void setCache(Cache cache) {
- this.cache = cache;
+ private void applyPeerCacheConfigurers() {
+ applyPeerCacheConfigurers(getCompositePeerCacheConfigurer());
}
- /* (non-Javadoc) */
- @SuppressWarnings("unchecked")
- protected T getCache() {
- return (T) cache;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.FactoryBean#getObject()
+ /**
+ * Null-safe operation to apply the given array of {@link PeerCacheConfigurer PeerCacheConfigurers}
+ * to this {@link CacheFactoryBean}.
+ *
+ * @param peerCacheConfigurers array of {@link PeerCacheConfigurer PeerCacheConfigurers} applied to
+ * this {@link CacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ * @see #applyPeerCacheConfigurers(Iterable)
*/
- @Override
- public Cache getObject() throws Exception {
- return (cache != null ? cache : init());
+ protected void applyPeerCacheConfigurers(PeerCacheConfigurer... peerCacheConfigurers) {
+ applyPeerCacheConfigurers(Arrays.asList(nullSafeArray(peerCacheConfigurers, PeerCacheConfigurer.class)));
}
- /* (non-Javadoc) */
- Cache init() throws Exception {
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link PeerCacheConfigurer PeerCacheConfigurers}
+ * to this {@link CacheFactoryBean}.
+ *
+ * @param peerCacheConfigurers {@link Iterable} of {@link PeerCacheConfigurer PeerCacheConfigurers}
+ * applied to this {@link CacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ * @see java.lang.Iterable
+ */
+ protected void applyPeerCacheConfigurers(Iterable peerCacheConfigurers) {
+ stream(nullSafeIterable(peerCacheConfigurers).spliterator(), false)
+ .forEach(clientCacheConfigurer -> clientCacheConfigurer.configure(getBeanName(), this));
+ }
+
+ /**
+ * Initializes the {@link Cache}.
+ *
+ * @return a reference to the initialized {@link Cache}.
+ * @see org.apache.geode.cache.Cache
+ * @see #resolveCache()
+ * @see #postProcess(GemFireCache)
+ * @see #setCache(Cache)
+ */
+ @SuppressWarnings("deprecation")
+ Cache init() {
+
ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
- // use bean ClassLoader to load Spring configured, GemFire Declarable classes
- Thread.currentThread().setContextClassLoader(beanClassLoader);
+ // use bean ClassLoader to load Spring configured, Pivotal GemFire/Apache Geode classes
+ Thread.currentThread().setContextClassLoader(getBeanClassLoader());
- cache = postProcess(resolveCache());
+ setCache(postProcess(resolveCache()));
- DistributedSystem system = cache.getDistributedSystem();
+ Optional.ofNullable(this.getCache()).ifPresent(cache -> {
+ Optional.ofNullable(cache.getDistributedSystem()).map(DistributedSystem::getDistributedMember)
+ .ifPresent(member ->
+ logInfo(() -> String.format("Connected to Distributed System [%1$s] as Member [%2$s]"
+ .concat(" in Group(s) [%3$s] with Role(s) [%4$s] on Host [%5$s] having PID [%6$d]"),
+ cache.getDistributedSystem().getName(), member.getId(), member.getGroups(),
+ member.getRoles(), member.getHost(), member.getProcessId())));
- DistributedMember member = system.getDistributedMember();
+ logInfo(() -> String.format("%1$s %2$s version [%3$s] Cache [%4$s]", this.cacheResolutionMessagePrefix,
+ apacheGeodeProductName(), apacheGeodeVersion(), cache.getName()));
+ });
- if (log.isInfoEnabled()) {
- log.info(String.format("Connected to Distributed System [%1$s] as Member [%2$s]"
- .concat(" in Group(s) [%3$s] with Role(s) [%4$s] on Host [%5$s] having PID [%6$d]."),
- system.getName(), member.getId(), member.getGroups(), member.getRoles(), member.getHost(),
- member.getProcessId()));
- log.info(String.format("%1$s %2$s version [%3$s] Cache [%4$s].",
- cacheResolutionMessagePrefix, apacheGeodeProductName(), apacheGeodeVersion(), cache.getName()));
- }
-
- return cache;
+ return getCache();
+ }
+ catch (Exception e) {
+ throw newRuntimeException(e, "Error occurred when initializing peer cache");
}
finally {
Thread.currentThread().setContextClassLoader(currentThreadContextClassLoader);
@@ -220,189 +280,167 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/**
- * If Dynamic Regions are enabled, create and initialize a DynamicRegionFactory before creating the Cache.
- */
- private void initDynamicRegionFactory() {
- if (dynamicRegionSupport != null) {
- dynamicRegionSupport.initializeDynamicRegionFactory();
- }
- }
-
- /**
- * Resolves the GemFire Cache by first attempting to lookup and find an existing Cache instance in the VM;
- * if an existing Cache could not be found, then this method proceeds in attempting to create a new Cache instance.
+ * Resolves the {@link Cache} by first attempting to lookup an existing {@link Cache} instance in the JVM.
+ * If an existing {@link Cache} could not be found, then this method proceeds in attempting to create
+ * a new {@link Cache} instance.
*
- * @return the resolved GemFire Cache instance.
+ * @return the resolved {@link Cache} instance.
* @see org.apache.geode.cache.Cache
* @see #fetchCache()
+ * @see #resolveProperties()
* @see #createFactory(java.util.Properties)
* @see #prepareFactory(Object)
* @see #createCache(Object)
*/
protected Cache resolveCache() {
try {
- cacheResolutionMessagePrefix = "Found existing";
+ this.cacheResolutionMessagePrefix = "Found existing";
return (Cache) fetchCache();
}
catch (CacheClosedException ex) {
- cacheResolutionMessagePrefix = "Created new";
+ this.cacheResolutionMessagePrefix = "Created new";
initDynamicRegionFactory();
return (Cache) createCache(prepareFactory(createFactory(resolveProperties())));
}
}
/**
- * Fetches an existing GemFire Cache instance from the CacheFactory.
+ * Fetches an existing {@link Cache} instance from the {@link CacheFactory}.
*
- * @param parameterized Class type extension of GemFireCache.
- * @return the existing GemFire Cache instance if available.
- * @throws org.apache.geode.cache.CacheClosedException if an existing GemFire Cache instance does not exist.
- * @see org.apache.geode.cache.GemFireCache
+ * @param parameterized {@link Class} type extension of {@link GemFireCache}.
+ * @return an existing {@link Cache} instance if available.
+ * @throws org.apache.geode.cache.CacheClosedException if an existing {@link Cache} instance does not exist.
* @see org.apache.geode.cache.CacheFactory#getAnyInstance()
+ * @see org.apache.geode.cache.GemFireCache
+ * @see #getCache()
*/
@SuppressWarnings("unchecked")
protected T fetchCache() {
- return (T) (cache != null ? cache : CacheFactory.getAnyInstance());
+ return (T) Optional.ofNullable(getCache()).orElseGet(CacheFactory::getAnyInstance);
}
/**
- * Resolves the GemFire System properties used to configure the GemFire Cache instance.
+ * If Pivotal GemFire/Apache Geode Dynamic Regions are enabled, create and initialize a {@link DynamicRegionFactory}
+ * before creating the {@link Cache}.
*
- * @return a Properties object containing GemFire System properties used to configure the GemFire Cache instance.
+ * @see org.springframework.data.gemfire.CacheFactoryBean.DynamicRegionSupport#initDynamicRegionFactory()
+ * @see #getDynamicRegionSupport()
+ */
+ private void initDynamicRegionFactory() {
+ Optional.ofNullable(getDynamicRegionSupport()).ifPresent(DynamicRegionSupport::initializeDynamicRegionFactory);
+ }
+
+ /**
+ * Resolves the Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link Cache}.
+ *
+ * @return the resolved Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link Cache}.
+ * @see #setAndGetProperties(Properties)
* @see #getProperties()
*/
protected Properties resolveProperties() {
- return (properties != null ? properties : (properties = new Properties()));
+ return Optional.ofNullable(getProperties()).orElseGet(() -> setAndGetProperties(new Properties()));
}
/**
- * Creates an instance of GemFire factory initialized with the given GemFire System Properties
- * to create an instance of a GemFire cache.
+ * Constructs a new instance of {@link CacheFactory} initialized with the given Pivotal GemFire/Apache Geode
+ * {@link Properties} used to create an instance of a {@link Cache}.
*
- * @param gemfireProperties a Properties object containing GemFire System properties.
- * @return an instance of a GemFire factory used to create a GemFire cache instance.
- * @see java.util.Properties
+ * @param gemfireProperties {@link Properties} used by the {@link CacheFactory} to configure the {@link Cache}.
+ * @return a new instance of {@link CacheFactory} initialized with the given Pivotal GemFire/Apache Geode
+ * {@link Properties}.
* @see org.apache.geode.cache.CacheFactory
+ * @see java.util.Properties
*/
protected Object createFactory(Properties gemfireProperties) {
return new CacheFactory(gemfireProperties);
}
/**
- * Initializes the GemFire factory used to create the GemFire cache instance. Sets PDX options
- * specified by the user.
+ * Prepares and initializes the {@link CacheFactory} used to create the {@link Cache}.
*
- * @param factory the GemFire factory used to create an instance of the GemFire cache.
- * @return the initialized GemFire cache factory.
- * @see #isPdxOptionsSpecified()
+ * Sets PDX options specified by the user.
+ *
+ * @param factory {@link CacheFactory} used to create the {@link Cache}.
+ * @return the prepared and initialized {@link CacheFactory}.
+ * @see #initializePdx(CacheFactory)
*/
protected Object prepareFactory(Object factory) {
return initializePdx((CacheFactory) factory);
}
/**
- * Initialize the PDX settings on the {@link CacheFactory}.
+ * Configure PDX for the given {@link CacheFactory}.
*
- * @param cacheFactory the GemFire {@link CacheFactory} used to configure and create a GemFire {@link Cache}.
+ * @param cacheFactory {@link CacheFactory} used to configure PDX.
+ * @return the given {@link CacheFactory}.
* @see org.apache.geode.cache.CacheFactory
*/
- CacheFactory initializePdx(CacheFactory cacheFactory) {
- if (isPdxOptionsSpecified()) {
- if (pdxSerializer != null) {
- Assert.isInstanceOf(PdxSerializer.class, pdxSerializer,
- String.format("[%1$s] of type [%2$s] is not a PdxSerializer", pdxSerializer,
- ObjectUtils.nullSafeClassName(pdxSerializer)));
+ private CacheFactory initializePdx(CacheFactory cacheFactory) {
- cacheFactory.setPdxSerializer((PdxSerializer) pdxSerializer);
- }
- if (pdxDiskStoreName != null) {
- cacheFactory.setPdxDiskStore(pdxDiskStoreName);
- }
- if (pdxIgnoreUnreadFields != null) {
- cacheFactory.setPdxIgnoreUnreadFields(pdxIgnoreUnreadFields);
- }
- if (pdxPersistent != null) {
- cacheFactory.setPdxPersistent(pdxPersistent);
- }
- if (pdxReadSerialized != null) {
- cacheFactory.setPdxReadSerialized(pdxReadSerialized);
- }
- }
+ Optional.ofNullable(getPdxSerializer()).ifPresent(cacheFactory::setPdxSerializer);
+
+ Optional.ofNullable(getPdxDiskStoreName()).filter(StringUtils::hasText)
+ .ifPresent(cacheFactory::setPdxDiskStore);
+
+ Optional.ofNullable(getPdxIgnoreUnreadFields()).ifPresent(cacheFactory::setPdxIgnoreUnreadFields);
+
+ Optional.ofNullable(getPdxPersistent()).ifPresent(cacheFactory::setPdxPersistent);
+
+ Optional.ofNullable(getPdxReadSerialized()).ifPresent(cacheFactory::setPdxReadSerialized);
return cacheFactory;
}
/**
- * Determines whether the user specified PDX options.
+ * Creates a new {@link Cache} instance using the provided factory.
*
- * @return a boolean value indicating whether the user specified PDX options or not.
- */
- protected boolean isPdxOptionsSpecified() {
- return (pdxSerializer != null || pdxReadSerialized != null || pdxPersistent != null
- || pdxIgnoreUnreadFields != null || pdxDiskStoreName != null);
- }
-
- /**
- * Creates a new GemFire cache instance using the provided factory.
- *
- * @param parameterized Class type extension of GemFireCache.
- * @param factory the appropriate GemFire factory used to create a cache instance.
- * @return an instance of the GemFire cache.
- * @see org.apache.geode.cache.GemFireCache
+ * @param parameterized {@link Class} type extension of {@link GemFireCache}.
+ * @param factory instance of {@link CacheFactory}.
+ * @return a new instance of {@link Cache} created by the provided factory.
* @see org.apache.geode.cache.CacheFactory#create()
+ * @see org.apache.geode.cache.GemFireCache
*/
@SuppressWarnings("unchecked")
protected T createCache(Object factory) {
- return (T) (cache != null ? cache : ((CacheFactory) factory).create());
+ return (T) Optional.ofNullable(getCache()).orElseGet(((CacheFactory) factory)::create);
}
/**
- * Post processes the GemFire Cache instance by loading any cache.xml, applying settings specified in SDG XML
- * configuration meta-data, and registering the appropriate Transaction Listeners, Writer and JNDI settings.
+ * Post processes the {@link GemFireCache} by loading any {@literal cache.xml}, applying custom settings
+ * specified in SDG XML configuration meta-data, and registering appropriate Transaction Listeners, Writer
+ * and JNDI settings.
*
- * @param parameterized Class type extension of GemFireCache.
- * @param cache the GemFire Cache instance to process.
- * @return the GemFire Cache instance after processing.
- * @throws IOException if the cache.xml Resource could not be loaded and applied to the Cache instance.
+ * @param Parameterized {@link Class} type extension of {@link GemFireCache}.
+ * @param cache {@link GemFireCache} instance to post process.
+ * @return the given {@link GemFireCache}.
* @see org.apache.geode.cache.Cache#loadCacheXml(java.io.InputStream)
* @see #getCacheXml()
- * @see #setHeapPercentages(org.apache.geode.cache.GemFireCache)
+ * @see #configureHeapPercentages(org.apache.geode.cache.GemFireCache)
+ * @see #registerJndiDataSources()
* @see #registerTransactionListeners(org.apache.geode.cache.GemFireCache)
* @see #registerTransactionWriter(org.apache.geode.cache.GemFireCache)
- * @see #registerJndiDataSources()
*/
- protected T postProcess(T cache) throws IOException {
- Resource localCacheXml = getCacheXml();
+ protected T postProcess(T cache) {
- // load cache.xml Resource and initialize the Cache
- if (localCacheXml != null) {
- if (log.isDebugEnabled()) {
- log.debug(String.format("initializing Cache with '%1$s'", cacheXml));
+ // load cache.xml Resource and initialize the cache
+ Optional.ofNullable(getCacheXml()).ifPresent(cacheXml -> {
+ try {
+ logDebug("Initializing cache with [%s]", cacheXml);
+ cache.loadCacheXml(cacheXml.getInputStream());
}
+ catch (IOException e) {
+ throw newRuntimeException(e, "Failed to load cache.xml [%s]", cacheXml);
+ }
+ });
- cache.loadCacheXml(localCacheXml.getInputStream());
- }
+ Optional.ofNullable(getCopyOnRead()).ifPresent(cache::setCopyOnRead);
+ Optional.ofNullable(getGatewayConflictResolver()).ifPresent(((Cache) cache)::setGatewayConflictResolver);
+ Optional.ofNullable(getLockLease()).ifPresent(((Cache) cache)::setLockLease);
+ Optional.ofNullable(getLockTimeout()).ifPresent(((Cache) cache)::setLockTimeout);
+ Optional.ofNullable(getMessageSyncInterval()).ifPresent(((Cache) cache)::setMessageSyncInterval);
+ Optional.ofNullable(getSearchTimeout()).ifPresent(((Cache) cache)::setSearchTimeout);
- if (this.copyOnRead != null) {
- cache.setCopyOnRead(this.copyOnRead);
- }
- if (gatewayConflictResolver != null) {
- ((Cache) cache).setGatewayConflictResolver((GatewayConflictResolver) gatewayConflictResolver);
- }
- if (lockLease != null) {
- ((Cache) cache).setLockLease(lockLease);
- }
- if (lockTimeout != null) {
- ((Cache) cache).setLockTimeout(lockTimeout);
- }
- if (messageSyncInterval != null) {
- ((Cache) cache).setMessageSyncInterval(messageSyncInterval);
- }
- if (searchTimeout != null) {
- ((Cache) cache).setSearchTimeout(searchTimeout);
- }
-
- setHeapPercentages(cache);
+ configureHeapPercentages(cache);
registerTransactionListeners(cache);
registerTransactionWriter(cache);
registerJndiDataSources();
@@ -411,203 +449,174 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/* (non-Javadoc) */
- private void setHeapPercentages(GemFireCache cache) {
- if (criticalHeapPercentage != null) {
- Assert.isTrue(criticalHeapPercentage > 0.0 && criticalHeapPercentage <= 100.0,
- String.format("'criticalHeapPercentage' (%1$s) is invalid; must be > 0.0 and <= 100.0",
- criticalHeapPercentage));
+ private boolean isHeapPercentageValid(Float heapPercentage) {
+ return (heapPercentage > 0.0f && heapPercentage <= 100.0f);
+ }
+
+ /* (non-Javadoc) */
+ private void configureHeapPercentages(GemFireCache cache) {
+ Optional.ofNullable(getCriticalHeapPercentage()).ifPresent(criticalHeapPercentage -> {
+ Assert.isTrue(isHeapPercentageValid(criticalHeapPercentage), String.format(
+ "criticalHeapPercentage [%s] is not valid; must be > 0.0 and <= 100.0", criticalHeapPercentage));
+
cache.getResourceManager().setCriticalHeapPercentage(criticalHeapPercentage);
- }
+ });
+
+ Optional.ofNullable(getEvictionHeapPercentage()).ifPresent(evictionHeapPercentage -> {
+ Assert.isTrue(isHeapPercentageValid(evictionHeapPercentage), String.format(
+ "evictionHeapPercentage [%s] is not valid; must be > 0.0 and <= 100.0", evictionHeapPercentage));
- if (evictionHeapPercentage != null) {
- Assert.isTrue(evictionHeapPercentage > 0.0 && evictionHeapPercentage <= 100.0,
- String.format("'evictionHeapPercentage' (%1$s) is invalid; must be > 0.0 and <= 100.0",
- evictionHeapPercentage));
cache.getResourceManager().setEvictionHeapPercentage(evictionHeapPercentage);
- }
- }
-
- /* (non-Javadoc) */
- private void registerTransactionListeners(GemFireCache cache) {
- for (TransactionListener transactionListener : CollectionUtils.nullSafeCollection(transactionListeners)) {
- cache.getCacheTransactionManager().addListener(transactionListener);
- }
- }
-
- /* (non-Javadoc) */
- private void registerTransactionWriter(GemFireCache cache) {
- if (transactionWriter != null) {
- cache.getCacheTransactionManager().setWriter(transactionWriter);
- }
+ });
}
/* (non-Javadoc) */
private void registerJndiDataSources() {
- for (JndiDataSource jndiDataSource : CollectionUtils.nullSafeCollection(jndiDataSources)) {
- String typeAttributeValue = jndiDataSource.getAttributes().get("type");
- JndiDataSourceType jndiDataSourceType = JndiDataSourceType.valueOfIgnoreCase(typeAttributeValue);
+
+ nullSafeCollection(getJndiDataSources()).forEach(jndiDataSource -> {
+
+ String type = jndiDataSource.getAttributes().get("type");
+
+ JndiDataSourceType jndiDataSourceType = JndiDataSourceType.valueOfIgnoreCase(type);
Assert.notNull(jndiDataSourceType, String.format(
- "'jndi-binding' 'type' [%1$s] is invalid; 'type' must be one of %2$s", typeAttributeValue,
- Arrays.toString(JndiDataSourceType.values())));
+ "'jndi-binding' 'type' [%1$s] is invalid; 'type' must be one of %2$s", type,
+ Arrays.toString(JndiDataSourceType.values())));
jndiDataSource.getAttributes().put("type", jndiDataSourceType.getName());
JNDIInvoker.mapDatasource(jndiDataSource.getAttributes(), jndiDataSource.getProps());
- }
+ });
}
+ /* (non-Javadoc) */
+ private void registerTransactionListeners(GemFireCache cache) {
+ nullSafeCollection(getTransactionListeners())
+ .forEach(transactionListener -> cache.getCacheTransactionManager().addListener(transactionListener));
+ }
+
+ /* (non-Javadoc) */
+ private void registerTransactionWriter(GemFireCache cache) {
+ Optional.ofNullable(getTransactionWriter()).ifPresent(it -> cache.getCacheTransactionManager().setWriter(it));
+ }
+
+ /**
+ * Destroys the {@link Cache} bean on Spring container shutdown.
+ *
+ * @throws Exception if an error occurs while closing the cache.
+ * @see org.springframework.beans.factory.DisposableBean#destroy()
+ * @see #destroyBeanFactoryLocator()
+ * @see #close(GemFireCache)
+ * @see #isClose()
+ */
@Override
public void destroy() throws Exception {
- if (close) {
- Cache localCache = fetchCache();
-
- if (localCache != null && !localCache.isClosed()) {
- close(localCache);
- }
-
- this.cache = null;
-
- if (beanFactoryLocator != null) {
- beanFactoryLocator.destroy();
- beanFactoryLocator = null;
- }
+ if (isClose()) {
+ close(fetchCache());
+ destroyBeanFactoryLocator();
}
}
- /* (non-Javadoc) */
+ /**
+ * Null-safe internal method used to close the {@link GemFireCache} and calling {@link GemFireCache#close()}
+ * iff the cache {@link GemFireCache#isClosed() is not already closed}.
+ *
+ * @param cache {@link GemFireCache} to close.
+ * @see org.apache.geode.cache.GemFireCache#isClosed()
+ * @see org.apache.geode.cache.GemFireCache#close()
+ */
protected void close(GemFireCache cache) {
- cache.close();
+
+ Optional.ofNullable(cache)
+ .filter(it -> !it.isClosed())
+ .ifPresent(RegionService::close);
+
+ this.cache = null;
}
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ /**
+ * Destroys the {@link GemfireBeanFactoryLocator}.
+ *
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator#destroy()
+ */
+ private void destroyBeanFactoryLocator() {
+ Optional.ofNullable(getBeanFactoryLocator()).ifPresent(GemfireBeanFactoryLocator::destroy);
+ this.beanFactoryLocator = null;
+ }
+
+ /**
+ * Translates the given Pivotal GemFire/Apache Geode {@link RuntimeException} thrown to a corresponding exception
+ * from Spring's generic {@link DataAccessException} hierarchy, if possible.
+ *
+ * @param exception {@link RuntimeException} to translate.
+ * @return the translated Spring {@link DataAccessException} or {@literal null} if the Pivotal GemFire/Apache Geode
+ * {@link RuntimeException} could not be converted.
+ * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(RuntimeException)
+ * @see org.springframework.dao.DataAccessException
*/
@Override
- public Class extends GemFireCache> getObjectType() {
- return (cache != null ? cache.getClass() : Cache.class);
- }
+ public DataAccessException translateExceptionIfPossible(RuntimeException exception) {
- /* (non-Javadoc) */
- protected void setPhase(int phase) {
- this.phase = phase;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.context.Phased#getPhase()
- */
- @Override
- public int getPhase() {
- return phase;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.FactoryBean#isSingleton()
- */
- @Override
- public boolean isSingleton() {
- return true;
- }
-
- @Override
- public DataAccessException translateExceptionIfPossible(RuntimeException e) {
- if (e instanceof GemFireException) {
- return GemfireCacheUtils.convertGemfireAccessException((GemFireException) e);
- }
-
- if (e instanceof IllegalArgumentException) {
- DataAccessException wrapped = GemfireCacheUtils.convertQueryExceptions(e);
- // ignore conversion if the generic exception is returned
+ if (exception instanceof IllegalArgumentException) {
+ DataAccessException wrapped = GemfireCacheUtils.convertQueryExceptions(exception);
+ // ignore conversion if generic exception is returned
if (!(wrapped instanceof GemfireSystemException)) {
return wrapped;
}
}
- if (e.getCause() instanceof GemFireException) {
- return GemfireCacheUtils.convertGemfireAccessException((GemFireException) e.getCause());
+ if (exception instanceof GemFireException) {
+ return GemfireCacheUtils.convertGemfireAccessException((GemFireException) exception);
}
- if (e.getCause() instanceof GemFireCheckedException) {
- return GemfireCacheUtils.convertGemfireAccessException((GemFireCheckedException) e.getCause());
+ if (exception.getCause() instanceof GemFireException) {
+ return GemfireCacheUtils.convertGemfireAccessException((GemFireException) exception.getCause());
+ }
+
+ if (exception.getCause() instanceof GemFireCheckedException) {
+ return GemfireCacheUtils.convertGemfireAccessException((GemFireCheckedException) exception.getCause());
}
return null;
}
/**
- * Sets a reference to the {@link ClassLoader} used to load and create bean classes in the Spring container.
+ * Returns a reference to the configured {@link GemfireBeanFactoryLocator} used to resolve Spring bean references
+ * in native Pivotal GemFire/Apache Geode native config (e.g. {@literal cache.xml}).
*
- * @param classLoader the {@link ClassLoader} used to load and create beans in the Spring container.
- * @see java.lang.ClassLoader
+ * @return a reference to the configured {@link GemfireBeanFactoryLocator}.
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
*/
- @Override
- public void setBeanClassLoader(ClassLoader classLoader) {
- this.beanClassLoader = classLoader;
- }
-
- /**
- * Gets a reference to the {@link ClassLoader} used to load and create bean classes in the Spring container.
- *
- * @return the {@link ClassLoader} used to load and create beans in the Spring container.
- * @see java.lang.ClassLoader
- */
- public ClassLoader getBeanClassLoader() {
- return beanClassLoader;
- }
-
- /**
- * Sets a reference to the Spring {@link BeanFactory} containing this GemFire {@link Cache} {@link FactoryBean}.
- *
- * @param beanFactory a reference to the Spring {@link BeanFactory}.
- * @see org.springframework.beans.factory.BeanFactory
- * @see #getBeanFactory()
- */
- @Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.beanFactory = beanFactory;
- }
-
- /**
- * Gets a reference to the Spring BeanFactory containing this GemFire {@link Cache} {@link FactoryBean}.
- *
- * @return a reference to the Spring {@link BeanFactory}.
- * @see org.springframework.beans.factory.BeanFactory
- * @see #setBeanFactory(BeanFactory)
- */
- public BeanFactory getBeanFactory() {
- return beanFactory;
- }
-
- /* (non-Javadoc) */
public GemfireBeanFactoryLocator getBeanFactoryLocator() {
- return beanFactoryLocator;
+ return this.beanFactoryLocator;
}
/**
- * Sets the Spring bean name for this GemFire {@link Cache}.
+ * Sets a reference to the constructed, configured an initialized {@link Cache}
+ * created by this {@link CacheFactoryBean}.
*
- * @param name a String value indicating the Spring container bean name for the GemFire {@link Cache} object.
+ * @param cache {@link Cache} created by this {@link CacheFactoryBean}.
+ * @see org.apache.geode.cache.Cache
*/
- @Override
- public void setBeanName(String name) {
- this.beanName = name;
+ protected void setCache(Cache cache) {
+ this.cache = cache;
}
/**
- * Gets the Spring bean name for this GemFire {@link Cache}.
+ * Returns a direct reference to the constructed, configured an initialized {@link Cache}
+ * created by this {@link CacheFactoryBean}.
*
- * @return a String value indicating the Spring container bean name for the GemFire {@link Cache} object.
+ * @return a direct reference to the {@link Cache} created by this {@link CacheFactoryBean}.
+ * @see org.apache.geode.cache.Cache
*/
- public String getBeanName() {
- return beanName;
+ @SuppressWarnings("unchecked")
+ protected T getCache() {
+ return (T) this.cache;
}
/**
- * Sets the {@link Cache} configuration meta-data.
+ * Sets a reference to the Pivotal GemFire/Apache Geode native {@literal cache.xml} {@link Resource}.
*
- * @param cacheXml the cache.xml {@link Resource} used to initialize the GemFire {@link Cache}.
+ * @param cacheXml reference to the Pivotal GemFire/Apache Geode native {@literal cache.xml} {@link Resource}.
* @see org.springframework.core.io.Resource
*/
public void setCacheXml(Resource cacheXml) {
@@ -615,69 +624,160 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/**
- * Gets a reference to the GemFire native cache.xml file as a Spring {@link Resource}.
+ * Returns a reference to the Pivotal GemFire/Apache Geode native {@literal cache.xml}
+ * as a Spring {@link Resource}.
*
- * @return the a reference to the GemFire native cache.xml as a Spring {@link Resource}.
+ * @return a reference to the Pivotal GemFire/Apache Geode native {@literal cache.xml}
+ * as a Spring {@link Resource}.
* @see org.springframework.core.io.Resource
*/
public Resource getCacheXml() {
- return cacheXml;
+ return this.cacheXml;
}
- /* (non-Javadoc) */
+ /**
+ * Returns the {@literal cache.xml} {@link Resource} as a {@link File}.
+ *
+ * @return the {@literal cache.xml} {@link Resource} as a {@link File}.
+ * @throws IllegalStateException if the {@link Resource} is not a valid {@link File} in the file system
+ * or a general problem exists accessing or reading the {@link File}.
+ * @see org.springframework.core.io.Resource
+ * @see java.io.File
+ * @see #getCacheXml()
+ */
private File getCacheXmlFile() {
try {
return getCacheXml().getFile();
}
- catch (IOException e) {
- throw new IllegalStateException(String.format("Resource (%1$s) is not resolvable as a file", e));
+ catch (Throwable e) {
+ throw newIllegalStateException(e, "Resource [%s] is not resolvable as a file", getCacheXml());
}
}
- /* (non-Javadoc) */
+ /**
+ * Determines whether the {@link Resource cache.xml} {@link File} is present.
+ *
+ * @return boolean value indicating whether a {@link Resource cache.xml} {@link File} is present.
+ * @see #getCacheXmlFile()
+ */
private boolean isCacheXmlAvailable() {
try {
- Resource localCacheXml = getCacheXml();
- return (localCacheXml != null && localCacheXml.getFile().isFile());
+ return (getCacheXmlFile() != null);
}
- catch (IOException ignore) {
+ catch (Throwable ignore) {
return false;
}
}
/**
- * Sets the cache properties.
+ * Returns an object reference to the {@link Cache} created by this {@link CacheFactoryBean}.
*
- * @param properties the properties to set
+ * @return an object reference to the {@link Cache} created by this {@link CacheFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObject()
+ * @see org.apache.geode.cache.Cache
+ * @see #getCache()
+ */
+ @Override
+ public Cache getObject() throws Exception {
+ return Optional.ofNullable(this.getCache()).orElseGet(this::init);
+ }
+
+ /**
+ * Returns the {@link Class} type of the {@link GemFireCache} produced by this {@link CacheFactoryBean}.
+ *
+ * @return the {@link Class} type of the {@link GemFireCache} produced by this {@link CacheFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Class extends GemFireCache> getObjectType() {
+ return Optional.ofNullable(getCache()).map(Object::getClass).orElse(Cache.class);
+ }
+
+ /**
+ * Set the phase for the {@link Cache} bean in the lifecycle managed by the Spring container.
+ *
+ * @param phase {@link Integer#TYPE int} value indicating the phase of this {@link Cache} bean
+ * in the lifecycle managed by the Spring container.
+ * @see org.springframework.context.Phased#getPhase()
+ */
+ protected void setPhase(int phase) {
+ this.phase = phase;
+ }
+
+ /**
+ * Returns the configured phase of the {@link Cache} bean in the lifecycle managed by the Spring container.
+ *
+ * @return an {@link Integer#TYPE int} value indicating the phase of this {@link Cache} bean in the lifecycle
+ * managed by the Spring container.
+ * @see org.springframework.context.Phased#getPhase()
+ */
+ @Override
+ public int getPhase() {
+ return this.phase;
+ }
+
+ /**
+ * Sets and then returns a reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
+ *
+ * @param properties reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
+ * @return a reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
+ * @see java.util.Properties
+ * @see #setProperties(Properties)
+ * @see #getProperties()
+ */
+ protected Properties setAndGetProperties(Properties properties) {
+ setProperties(properties);
+ return getProperties();
+ }
+
+ /**
+ * Returns a reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
+ *
+ * @param properties reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
+ * @see java.util.Properties
*/
public void setProperties(Properties properties) {
this.properties = properties;
}
/**
- * Gets a reference to the GemFire System Properties.
+ * Returns a reference to Pivotal GemFire/Apache Geode {@link Properties} used to configure the cache.
*
- * @return a reference to the GemFire System Properties.
+ * @return a reference to Pivotal GemFire/Apache Geode {@link Properties}.
* @see java.util.Properties
*/
public Properties getProperties() {
- return properties;
+ return this.properties;
}
/**
- * Set whether the Cache should be closed.
+ * Sets a value to indicate whether the cache will be closed on shutdown of the Spring container.
*
- * @param close set to false if destroy() should not close the cache
+ * @param close boolean value indicating whether the cache will be closed on shutdown of the Spring container.
*/
public void setClose(boolean close) {
this.close = close;
}
/**
- * @return close.
+ * Returns a boolean value indicating whether the cache will be closed on shutdown of the Spring container.
+ *
+ * @return a boolean value indicating whether the cache will be closed on shutdown of the Spring container.
*/
- public Boolean getClose() {
- return close;
+ public boolean isClose() {
+ return this.close;
+ }
+
+ /**
+ * Returns a reference to the Composite {@link PeerCacheConfigurer} used to apply additional configuration
+ * to this {@link CacheFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link PeerCacheConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ */
+ public PeerCacheConfigurer getCompositePeerCacheConfigurer() {
+ return this.compositePeerCacheConfigurer;
}
/**
@@ -770,14 +870,14 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
* compatibility with Gemfire 6 compatibility. This must be an instance of
* {@link org.apache.geode.cache.util.GatewayConflictResolver}
*/
- public void setGatewayConflictResolver(Object gatewayConflictResolver) {
+ public void setGatewayConflictResolver(GatewayConflictResolver gatewayConflictResolver) {
this.gatewayConflictResolver = gatewayConflictResolver;
}
/**
* @return the gatewayConflictResolver
*/
- public Object getGatewayConflictResolver() {
+ public GatewayConflictResolver getGatewayConflictResolver() {
return gatewayConflictResolver;
}
@@ -920,17 +1020,42 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
*
* @param serializer pdx serializer configured for this cache.
*/
- public void setPdxSerializer(Object serializer) {
+ public void setPdxSerializer(PdxSerializer serializer) {
this.pdxSerializer = serializer;
}
/**
* @return the pdxSerializer
*/
- public Object getPdxSerializer() {
+ public PdxSerializer getPdxSerializer() {
return pdxSerializer;
}
+ /**
+ * Null-safe operation to set an array of {@link PeerCacheConfigurer PeerCacheConfigurers} used to apply
+ * additional configuration to this {@link CacheFactoryBean} when using Annotation-based configuration.
+ *
+ * @param peerCacheConfigurers array of {@link PeerCacheConfigurer PeerCacheConfigurers} used to apply
+ * additional configuration to this {@link CacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ * @see #setPeerCacheConfigurers(List)
+ */
+ public void setPeerCacheConfigurers(PeerCacheConfigurer... peerCacheConfigurers) {
+ setPeerCacheConfigurers(Arrays.asList(nullSafeArray(peerCacheConfigurers, PeerCacheConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link PeerCacheConfigurer PeerCacheConfigurers} to apply
+ * additional configuration to this {@link CacheFactoryBean} when using Annotation-based configuration.
+ *
+ * @param peerCacheConfigurers {@link Iterable} of {@link PeerCacheConfigurer PeerCacheConfigurers} used to apply
+ * additional configuration to this {@link CacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
+ */
+ public void setPeerCacheConfigurers(List peerCacheConfigurers) {
+ this.peerCacheConfigurers = Optional.ofNullable(peerCacheConfigurers).orElseGet(Collections::emptyList);
+ }
+
/**
* Set the number of seconds a netSearch operation can wait for data before timing out.
*
@@ -984,42 +1109,48 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/**
- * Indicates whether a bean factory locator is enabled for this cache definition or not. The locator stores
- * the enclosing bean factory reference to allow auto-wiring of Spring beans into GemFire managed classes.
- * Usually disabled when the same cache is used in multiple application context/bean factories inside
- * the same VM.
+ * Sets whether to enable the {@link GemfireBeanFactoryLocator}.
*
- * @param usage true if the bean factory locator is to be used underneath the hood.
+ * @param use boolean value indicating whether to enable the {@link GemfireBeanFactoryLocator}.
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
*/
- public void setUseBeanFactoryLocator(boolean usage) {
- this.useBeanFactoryLocator = usage;
+ public void setUseBeanFactoryLocator(boolean use) {
+ this.useBeanFactoryLocator = use;
}
/**
- * @return useBeanFactoryLocator
+ * Determines whether the {@link GemfireBeanFactoryLocator} has been enabled.
+ *
+ * @return a boolean value indicating whether the {@link GemfireBeanFactoryLocator} has been enabled.
+ * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
*/
public boolean isUseBeanFactoryLocator() {
- return useBeanFactoryLocator;
+ return this.useBeanFactoryLocator;
}
/**
- * Sets the state of the use-shared-configuration GemFire distribution config setting.
+ * Sets the state of the {@literal use-shared-configuration} Pivotal GemFire/Apache Geode
+ * distribution configuration setting.
*
- * @param useSharedConfiguration a boolean value to set the use-shared-configuration GemFire distribution property.
+ * @param useSharedConfiguration boolean value to set the {@literal use-shared-configuration}
+ * Pivotal GemFire/Apache Geode distribution configuration setting.
*/
public void setUseClusterConfiguration(Boolean useSharedConfiguration) {
this.useClusterConfiguration = useSharedConfiguration;
}
/**
- * Gets the value of the use-shared-configuration GemFire configuration setting.
+ * Return the state of the {@literal use-shared-configuration} Pivotal GemFire/Apache Geode
+ * distribution configuration setting.
*
- * @return a boolean value indicating whether shared configuration use has been enabled or not.
+ * @return the current boolean value for the {@literal use-shared-configuration}
+ * Pivotal GemFire/Apache Geode distribution configuration setting.
*/
public Boolean getUseClusterConfiguration() {
return this.useClusterConfiguration;
}
+ /* (non-Javadoc) */
public static class DynamicRegionSupport {
private Boolean persistent = Boolean.TRUE;
@@ -1070,6 +1201,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
}
+ /* (non-Javadoc) */
public static class JndiDataSource {
private List props;
@@ -1091,5 +1223,4 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
this.props = props;
}
}
-
}
diff --git a/src/main/java/org/springframework/data/gemfire/DiskStoreFactoryBean.java b/src/main/java/org/springframework/data/gemfire/DiskStoreFactoryBean.java
index b752db35..b85f07f7 100644
--- a/src/main/java/org/springframework/data/gemfire/DiskStoreFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/DiskStoreFactoryBean.java
@@ -16,30 +16,44 @@
package org.springframework.data.gemfire;
+import static java.util.stream.StreamSupport.stream;
+import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
+
import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.DiskStoreFactory;
import org.apache.geode.cache.GemFireCache;
-import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer;
+import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
- * FactoryBean for creating a GemFire DiskStore.
+ * Spring {@link FactoryBean} used to create {@link DiskStore}.
*
* @author David Turanski
* @author John Blum
- * @see org.springframework.beans.factory.BeanNameAware
+ * @see org.apache.geode.cache.DiskStore
+ * @see org.apache.geode.cache.DiskStoreFactory
+ * @see org.apache.geode.cache.GemFireCache
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ * @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
*/
@SuppressWarnings("unused")
-public class DiskStoreFactoryBean implements BeanNameAware , FactoryBean, InitializingBean {
+public class DiskStoreFactoryBean extends AbstractFactoryBeanSupport implements InitializingBean {
private Boolean allowForceCompaction;
private Boolean autoCompact;
@@ -57,78 +71,184 @@ public class DiskStoreFactoryBean implements BeanNameAware , FactoryBean diskStoreConfigurers = Collections.emptyList();
+
+ private DiskStoreConfigurer compositeDiskStoreConfigurer = (beanName, bean) ->
+ nullSafeCollection(diskStoreConfigurers).forEach(diskStoreConfigurer ->
+ diskStoreConfigurer.configure(beanName, bean));
+
private List diskDirs;
- private String name;
-
- @Override
- public DiskStore getObject() throws Exception {
- return diskStore;
- }
-
- @Override
- public Class> getObjectType() {
- return (diskStore != null ? diskStore.getClass() : DiskStore.class);
- }
-
- @Override
- public boolean isSingleton() {
- return true;
- }
-
@Override
public void afterPropertiesSet() throws Exception {
- Assert.state(cache != null, String.format("A reference to the GemFire Cache must be set for Disk Store '%1$s'.",
- getName()));
- DiskStoreFactory diskStoreFactory = cache.createDiskStoreFactory();
+ String diskStoreName = resolveDiskStoreName();
- if (allowForceCompaction != null) {
- diskStoreFactory.setAllowForceCompaction(allowForceCompaction);
- }
- if (autoCompact != null) {
- diskStoreFactory.setAutoCompact(autoCompact);
- }
- if (compactionThreshold != null) {
- diskStoreFactory.setCompactionThreshold(compactionThreshold);
- }
- if (diskUsageCriticalPercentage != null) {
- diskStoreFactory.setDiskUsageCriticalPercentage(diskUsageCriticalPercentage);
- }
- if (diskUsageWarningPercentage != null) {
- diskStoreFactory.setDiskUsageWarningPercentage(diskUsageWarningPercentage);
- }
- if (maxOplogSize != null) {
- diskStoreFactory.setMaxOplogSize(maxOplogSize);
- }
- if (queueSize != null) {
- diskStoreFactory.setQueueSize(queueSize);
- }
- if (timeInterval != null) {
- diskStoreFactory.setTimeInterval(timeInterval);
- }
- if (writeBufferSize != null) {
- diskStoreFactory.setWriteBufferSize(writeBufferSize);
- }
+ applyDiskStoreConfigurers(diskStoreName);
- if (!CollectionUtils.isEmpty(diskDirs)) {
- File[] diskDirFiles = new File[diskDirs.size()];
- int[] diskDirSizes = new int[diskDirs.size()];
+ GemFireCache cache = resolveCache(diskStoreName);
- for (int index = 0; index < diskDirs.size(); index++) {
- DiskDir diskDir = diskDirs.get(index);
- diskDirFiles[index] = new File(diskDir.location);
- diskDirSizes[index] = (diskDir.maxSize != null ? diskDir.maxSize
- : DiskStoreFactory.DEFAULT_DISK_DIR_SIZE);
- }
+ DiskStoreFactory diskStoreFactory = postProcess(configure(createDiskStoreFactory(cache)));
- diskStoreFactory.setDiskDirsAndSizes(diskDirFiles, diskDirSizes);
- }
+ this.diskStore = postProcess(newDiskStore(diskStoreFactory, diskStoreName));
+ }
- diskStore = diskStoreFactory.create(getName());
+ /* (non-Javadoc) */
+ private void applyDiskStoreConfigurers(String diskStoreName) {
+ applyDiskStoreConfigurers(diskStoreName, getCompositeDiskStoreConfigurer());
+ }
- Assert.notNull(diskStore, String.format("DiskStore with name '%1$s' failed to be created successfully.",
- diskStore.getName()));
+ /**
+ * Null-safe operation to apply the given array of {@link DiskStoreConfigurer DiskStoreConfigurers}
+ * to this {@link DiskStoreFactoryBean}.
+ *
+ * @param diskStoreName {@link String} containing the name of the {@link DiskStore}.
+ * @param diskStoreConfigurers array of {@link DiskStoreConfigurer DiskStoreConfigurers} applied
+ * to this {@link DiskStoreFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ * @see #applyDiskStoreConfigurers(String, Iterable)
+ */
+ protected void applyDiskStoreConfigurers(String diskStoreName, DiskStoreConfigurer... diskStoreConfigurers) {
+ applyDiskStoreConfigurers(diskStoreName,
+ Arrays.asList(nullSafeArray(diskStoreConfigurers, DiskStoreConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers}
+ * to this {@link DiskStoreFactoryBean}.
+ *
+ * @param diskStoreName {@link String} containing the name of the {@link DiskStore}.
+ * @param diskStoreConfigurers {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers} applied
+ * to this {@link DiskStoreFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ */
+ protected void applyDiskStoreConfigurers(String diskStoreName, Iterable diskStoreConfigurers) {
+ stream(nullSafeIterable(diskStoreConfigurers).spliterator(), false)
+ .forEach(diskStoreConfigurer -> diskStoreConfigurer.configure(diskStoreName, this));
+ }
+
+ /* (non-Javadoc) */
+ private GemFireCache resolveCache(String diskStoreName) {
+ return Optional.ofNullable(this.cache)
+ .orElseThrow(() -> newIllegalStateException("Cache is required to create DiskStore [%s]", diskStoreName));
+ }
+
+ /* (non-Javadoc) */
+ final String resolveDiskStoreName() {
+ return Optional.ofNullable(getBeanName()).filter(StringUtils::hasText)
+ .orElse(DiskStoreFactory.DEFAULT_DISK_STORE_NAME);
+ }
+
+ /**
+ * Creates an instance of {@link DiskStoreFactory} using the given {@link GemFireCache} in order to
+ * construct, configure and initialize a new {@link DiskStore}.
+ *
+ * @param cache reference to the {@link GemFireCache} used to create the {@link DiskStoreFactory}.
+ * @return a new instance of {@link DiskStoreFactory}.
+ * @see org.apache.geode.cache.GemFireCache#createDiskStoreFactory()
+ * @see org.apache.geode.cache.DiskStoreFactory
+ */
+ protected DiskStoreFactory createDiskStoreFactory(GemFireCache cache) {
+ return cache.createDiskStoreFactory();
+ }
+
+ /**
+ * Configures the given {@link DiskStoreFactory} with the configuration settings present
+ * on this {@link DiskStoreFactoryBean}
+ *
+ * @param diskStoreFactory {@link DiskStoreFactory} to configure.
+ * @return the given {@link DiskStoreFactory}
+ * @see org.apache.geode.cache.DiskStoreFactory
+ */
+ protected DiskStoreFactory configure(DiskStoreFactory diskStoreFactory) {
+
+ Optional.ofNullable(this.allowForceCompaction).ifPresent(diskStoreFactory::setAllowForceCompaction);
+ Optional.ofNullable(this.autoCompact).ifPresent(diskStoreFactory::setAutoCompact);
+ Optional.ofNullable(this.compactionThreshold).ifPresent(diskStoreFactory::setCompactionThreshold);
+ Optional.ofNullable(this.diskUsageCriticalPercentage).ifPresent(diskStoreFactory::setDiskUsageCriticalPercentage);
+ Optional.ofNullable(this.diskUsageWarningPercentage).ifPresent(diskStoreFactory::setDiskUsageWarningPercentage);
+ Optional.ofNullable(this.maxOplogSize).ifPresent(diskStoreFactory::setMaxOplogSize);
+ Optional.ofNullable(this.queueSize).ifPresent(diskStoreFactory::setQueueSize);
+ Optional.ofNullable(this.timeInterval).ifPresent(diskStoreFactory::setTimeInterval);
+ Optional.ofNullable(this.writeBufferSize).ifPresent(diskStoreFactory::setWriteBufferSize);
+
+ Optional.ofNullable(this.diskDirs).filter(diskDirs -> !CollectionUtils.isEmpty(diskDirs))
+ .ifPresent(diskDirs -> {
+
+ File[] diskDirFiles = new File[diskDirs.size()];
+ int[] diskDirSizes = new int[diskDirs.size()];
+
+ for (int index = 0; index < diskDirs.size(); index++) {
+ DiskDir diskDir = diskDirs.get(index);
+ diskDirFiles[index] = new File(diskDir.location);
+ diskDirSizes[index] = Optional.ofNullable(diskDir.maxSize)
+ .orElse(DiskStoreFactory.DEFAULT_DISK_DIR_SIZE);
+ }
+
+ diskStoreFactory.setDiskDirsAndSizes(diskDirFiles, diskDirSizes);
+ });
+
+ return diskStoreFactory;
+ }
+
+ /**
+ * Constructs a new instance of {@link DiskStore} with the given {@link String name}
+ * using the provided {@link DiskStoreFactory}
+ *
+ * @param diskStoreFactory {@link DiskStoreFactory} used to create the {@link DiskStore}.
+ * @param diskStoreName {@link String} containing the name of the new {@link DiskStore}.
+ * @return a new instance of {@link DiskStore} with the given {@link String name}.
+ * @see org.apache.geode.cache.DiskStoreFactory
+ * @see org.apache.geode.cache.DiskStore
+ */
+ protected DiskStore newDiskStore(DiskStoreFactory diskStoreFactory, String diskStoreName) {
+ return diskStoreFactory.create(diskStoreName);
+ }
+
+ /**
+ * Post-process the {@link DiskStoreFactory} with any custom {@link DiskStoreFactory} or {@link DiskStore}
+ * configuration settings as required by the application.
+ *
+ * @param diskStoreFactory {@link DiskStoreFactory} to process.
+ * @return the given {@link DiskStoreFactory}.
+ * @see org.apache.geode.cache.DiskStoreFactory
+ */
+ protected DiskStoreFactory postProcess(DiskStoreFactory diskStoreFactory) {
+ return diskStoreFactory;
+ }
+
+ /**
+ * Post-process the provided {@link DiskStore} constructed, configured and initialized
+ * by this {@link DiskStoreFactoryBean}.
+ *
+ * @param diskStore {@link DiskStore} to process.
+ * @return the given {@link DiskStore}.
+ * @see org.apache.geode.cache.DiskStore
+ */
+ protected DiskStore postProcess(DiskStore diskStore) {
+ return diskStore;
+ }
+
+ /**
+ * Returns a reference to the Composite {@link DiskStoreConfigurer} used to apply additional configuration
+ * to this {@link DiskStoreFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link DiskStoreConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ */
+ protected DiskStoreConfigurer getCompositeDiskStoreConfigurer() {
+ return this.compositeDiskStoreConfigurer;
+ }
+
+ @Override
+ public DiskStore getObject() throws Exception {
+ return this.diskStore;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Class> getObjectType() {
+ return Optional.ofNullable(this.diskStore).map(DiskStore::getClass).orElse((Class) DiskStore.class);
}
public void setCache(GemFireCache cache) {
@@ -143,26 +263,47 @@ public class DiskStoreFactoryBean implements BeanNameAware , FactoryBean= 0 && compactionThreshold <= 100),
String.format("The DiskStore's (%1$s) compaction threshold (%2$d) must be an integer value between 0 and 100 inclusive.",
- this.name, compactionThreshold));
+ resolveDiskStoreName(), compactionThreshold));
}
public void setDiskDirs(List diskDirs) {
this.diskDirs = diskDirs;
}
+ /**
+ * Null-safe operation to set an array of {@link DiskStoreConfigurer DiskStoreConfigurers} used to
+ * apply additional configuration to this {@link DiskStoreFactoryBean} when using Annotation-based configuration.
+ *
+ * @param diskStoreConfigurers array of {@link DiskStoreConfigurer DiskStoreConfigurers} used to apply
+ * additional configuration to this {@link DiskStoreFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ * @see #setDiskStoreConfigurers(List)
+ */
+ public void setDiskStoreConfigurers(DiskStoreConfigurer... diskStoreConfigurers) {
+ setDiskStoreConfigurers(Arrays.asList(nullSafeArray(diskStoreConfigurers, DiskStoreConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers}
+ * used to apply additional configuration to this {@link DiskStoreFactoryBean}
+ * when using Annotation-based configuration.
+ *
+ * @param diskStoreConfigurers {@link Iterable } of {@link DiskStoreConfigurer DiskStoreConfigurers} used to
+ * apply additional configuration to this {@link DiskStoreFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
+ */
+ public void setDiskStoreConfigurers(List diskStoreConfigurers) {
+ this.diskStoreConfigurers = Optional.ofNullable(diskStoreConfigurers).orElseGet(Collections::emptyList);
+ }
+
public void setDiskUsageCriticalPercentage(Float diskUsageCriticalPercentage) {
this.diskUsageCriticalPercentage = diskUsageCriticalPercentage;
}
@@ -187,11 +328,8 @@ public class DiskStoreFactoryBean implements BeanNameAware , FactoryBean, BeanNameAware, BeanFactoryAware {
+public class IndexFactoryBean extends AbstractFactoryBeanSupport implements InitializingBean {
private boolean define = false;
private boolean override = true;
- private BeanFactory beanFactory;
-
private Index index;
private IndexType indexType;
+ //@Autowired(required = false)
+ private List indexConfigurers = Collections.emptyList();
+
+ private IndexConfigurer compositeIndexConfigurer = new IndexConfigurer() {
+
+ @Override
+ public void configure(String beanName, IndexFactoryBean bean) {
+ nullSafeCollection(indexConfigurers).forEach(indexConfigurer -> indexConfigurer.configure(beanName, bean));
+ }
+ };
+
private QueryService queryService;
private RegionService cache;
- private String beanName;
private String expression;
private String from;
private String imports;
@@ -83,29 +100,66 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
*/
@Override
public void afterPropertiesSet() throws Exception {
- Assert.notNull(cache, "The GemFire Cache reference must not be null!");
- queryService = lookupQueryService();
+ this.indexName = Optional.ofNullable(this.name).filter(StringUtils::hasText).orElse(getBeanName());
+
+ Assert.hasText(this.indexName, "Index name is required");
+
+ applyIndexConfigurers(this.indexName);
+
+ Assert.notNull(cache, "Cache is required");
+
+ this.queryService = lookupQueryService();
Assert.notNull(queryService, "QueryService is required to create an Index");
- Assert.hasText(expression, "Index 'expression' is required");
- Assert.hasText(from, "Index 'from clause' is required");
+ Assert.hasText(expression, "Index expression is required");
+ Assert.hasText(from, "Index from clause is required");
if (IndexType.isKey(indexType)) {
- Assert.isNull(imports, "'imports' are not supported with a KEY Index");
+ Assert.isNull(imports, "imports are not supported with a KEY Index");
}
- indexName = (StringUtils.hasText(name) ? name : beanName);
+ this.index = createIndex(queryService, indexName);
+ }
- Assert.hasText(indexName, "Index 'name' is required");
+ /* (non-Javadoc) */
+ private void applyIndexConfigurers(String indexName) {
+ applyIndexConfigurers(indexName, getCompositeRegionConfigurer());
+ }
- index = createIndex(queryService, indexName);
+ /**
+ * Null-safe operation to apply the given array of {@link IndexConfigurer IndexConfigurers}
+ * to this {@link IndexFactoryBean}.
+ *
+ * @param indexName {@link String} containing the name of the {@link Index}.
+ * @param indexConfigurers array of {@link IndexConfigurer IndexConfigurers} applied
+ * to this {@link IndexFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ * @see #applyIndexConfigurers(String, Iterable)
+ */
+ protected void applyIndexConfigurers(String indexName, IndexConfigurer... indexConfigurers) {
+ applyIndexConfigurers(indexName, Arrays.asList(nullSafeArray(indexConfigurers, IndexConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link IndexConfigurer IndexConfigurers}
+ * to this {@link IndexFactoryBean}.
+ *
+ * @param indexName {@link String} containing the name of the {@link Index}.
+ * @param indexConfigurers {@link Iterable} of {@link IndexConfigurer IndexConfigurers} applied
+ * to this {@link IndexFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ protected void applyIndexConfigurers(String indexName, Iterable indexConfigurers) {
+ stream(nullSafeIterable(indexConfigurers).spliterator(), false)
+ .forEach(indexConfigurer -> indexConfigurer.configure(indexName, this));
}
/* (non-Javadoc) */
QueryService doLookupQueryService() {
- return (queryService != null ? queryService
- : (cache instanceof ClientCache ? ((ClientCache) cache).getLocalQueryService() : cache.getQueryService()));
+ return Optional.ofNullable(this.queryService).orElseGet(() ->
+ (this.cache instanceof ClientCache ? ((ClientCache) this.cache).getLocalQueryService()
+ : this.cache.getQueryService()));
}
/* (non-Javadoc) */
@@ -131,6 +185,7 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
/* (non-Javadoc) */
Index createIndex(QueryService queryService, String indexName) throws Exception {
+
Index existingIndex = getExistingIndex(queryService, indexName);
if (existingIndex != null) {
@@ -183,6 +238,7 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
/* (non-Javadoc) */
Index createKeyIndex(QueryService queryService, String indexName, String expression, String from) throws Exception {
+
if (isDefine()) {
queryService.defineKeyIndex(indexName, expression, from);
return new IndexWrapper(queryService, indexName);
@@ -194,7 +250,7 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
/* (non-Javadoc) */
Index createHashIndex(QueryService queryService, String indexName, String expression, String from,
- String imports) throws Exception {
+ String imports) throws Exception {
boolean hasImports = StringUtils.hasText(imports);
@@ -246,7 +302,8 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
/* (non-Javadoc) */
Index getExistingIndex(QueryService queryService, String indexName) {
- for (Index index : CollectionUtils.nullSafeCollection(queryService.getIndexes())) {
+
+ for (Index index : nullSafeCollection(queryService.getIndexes())) {
if (index.getName().equalsIgnoreCase(indexName)) {
return index;
}
@@ -255,29 +312,33 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
return null;
}
+ /**
+ * Returns a reference to the Composite {@link IndexConfigurer} used to apply additional configuration
+ * to this {@link IndexFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link IndexConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
+ */
+ protected IndexConfigurer getCompositeRegionConfigurer() {
+ return this.compositeIndexConfigurer;
+ }
+
/**
* @inheritDoc
*/
@Override
public Index getObject() {
- index = (index != null ? index : getExistingIndex(queryService, indexName));
- return index;
+ return Optional.ofNullable(this.index)
+ .orElseGet(() -> this.index = getExistingIndex(queryService, indexName));
}
/**
* @inheritDoc
*/
@Override
+ @SuppressWarnings("unchecked")
public Class> getObjectType() {
- return (index != null ? index.getClass() : Index.class);
- }
-
- /**
- * @inheritDoc
- */
- @Override
- public boolean isSingleton() {
- return true;
+ return Optional.ofNullable(this.index).map(Index::getClass).orElse((Class) Index.class);
}
/**
@@ -298,24 +359,6 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
this.queryService = service;
}
- /* (non-Javadoc) */
- @Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.beanFactory = beanFactory;
- }
-
- /* (non-Javadoc) */
- protected BeanFactory getBeanFactory() {
- Assert.state(beanFactory != null, "'beanFactory' was not properly initialized");
- return beanFactory;
- }
-
- /* (non-Javadoc) */
- @Override
- public void setBeanName(String name) {
- this.beanName = name;
- }
-
/**
* @param name the name to set
*/
@@ -363,6 +406,31 @@ public class IndexFactoryBean implements InitializingBean, FactoryBean, B
this.imports = imports;
}
+ /**
+ * Null-safe operation to set an array of {@link IndexConfigurer IndexConfigurers} used to apply
+ * additional configuration to this {@link IndexFactoryBean} when using Annotation-based configuration.
+ *
+ * @param indexConfigurers array of {@link IndexConfigurer IndexConfigurers} used to apply
+ * additional configuration to this {@link IndexFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
+ * @see #setIndexConfigurers(List)
+ */
+ public void setIndexConfigurers(IndexConfigurer... indexConfigurers) {
+ setIndexConfigurers(Arrays.asList(nullSafeArray(indexConfigurers, IndexConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link IndexConfigurer IndexConfigurers} used to apply
+ * additional configuration to this {@link IndexFactoryBean} when using Annotation-based configuration.
+ *
+ * @param indexConfigurers {@link Iterable } of {@link IndexConfigurer IndexConfigurers} used to apply
+ * additional configuration to this {@link IndexFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
+ */
+ public void setIndexConfigurers(List indexConfigurers) {
+ this.indexConfigurers = Optional.ofNullable(indexConfigurers).orElseGet(Collections::emptyList);
+ }
+
/**
* @param override the override to set
*/
diff --git a/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java
index e9ae755d..85084138 100644
--- a/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java
@@ -16,7 +16,19 @@
package org.springframework.data.gemfire;
+import static java.util.Arrays.stream;
+import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
+
import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.StreamSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -40,30 +52,49 @@ import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.wan.GatewaySender;
import org.apache.geode.internal.cache.UserSpecifiedRegionAttributes;
import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
-import org.springframework.data.gemfire.util.ArrayUtils;
+import org.springframework.data.gemfire.config.annotation.RegionConfigurer;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
/**
- * Base class for FactoryBeans used to create 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.
+ * Abstract Spring {@link FactoryBean} base class extended by other SDG {@link FactoryBean FactoryBeans} used to
+ * construct, configure and initialize peer {@link Region Regions}.
*
- * 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.
+ * This {@link FactoryBean} allows for very easy and flexible creation of peer {@link Region}.
+ * For client {@link Region Regions}, however, see the {@link ClientRegionFactoryBean}.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
+ * @see org.apache.geode.cache.Cache
+ * @see org.apache.geode.cache.CacheListener
+ * @see org.apache.geode.cache.CacheLoader
+ * @see org.apache.geode.cache.CacheWriter
+ * @see org.apache.geode.cache.DataPolicy
+ * @see org.apache.geode.cache.EvictionAttributes
+ * @see org.apache.geode.cache.GemFireCache
+ * @see org.apache.geode.cache.PartitionAttributes
+ * @see org.apache.geode.cache.Region
+ * @see org.apache.geode.cache.RegionAttributes
+ * @see org.apache.geode.cache.RegionFactory
+ * @see org.apache.geode.cache.RegionShortcut
+ * @see org.apache.geode.cache.Scope
+ * @see org.apache.geode.cache.asyncqueue.AsyncEventQueue
+ * @see org.springframework.beans.factory.DisposableBean
+ * @see org.springframework.context.SmartLifecycle
+ * @see org.springframework.data.gemfire.RegionLookupFactoryBean
+ * @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
*/
@SuppressWarnings("unused")
-public abstract class RegionFactoryBean extends RegionLookupFactoryBean implements DisposableBean, SmartLifecycle {
+public abstract class RegionFactoryBean extends RegionLookupFactoryBean
+ implements DisposableBean, SmartLifecycle {
protected final Log log = LogFactory.getLog(getClass());
@@ -91,8 +122,19 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean regionConfigurers = Collections.emptyList();
+
private RegionAttributes attributes;
+ private RegionConfigurer compositeRegionConfigurer = new RegionConfigurer() {
+
+ @Override
+ public void configure(String beanName, RegionFactoryBean, ?> bean) {
+ nullSafeCollection(regionConfigurers)
+ .forEach(regionConfigurer -> regionConfigurer.configure(beanName, bean));
+ }
+ };
+
private RegionShortcut shortcut;
private Resource snapshot;
@@ -102,129 +144,215 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean createRegion(GemFireCache gemfireCache, String regionName) throws Exception {
+
+ applyRegionConfigurers(regionName);
+
+ verifyLockGrantorEligibility(getAttributes(), getScope());
+
+ Cache cache = resolveCache(gemfireCache);
+
+ RegionFactory regionFactory = postProcess(configure(createRegionFactory(cache)));
+
+ Region region = newRegion(regionFactory, getParent(), regionName);
+
+ return enableAsLockGrantor(region);
+ }
+
+ /* (non-Javadoc) */
+ private void applyRegionConfigurers(String regionName) {
+ applyRegionConfigurers(regionName, getCompositeRegionConfigurer());
}
/**
- * @inheritDoc
+ * Null-safe operation to apply the given array of {@link RegionConfigurer RegionConfigurers}
+ * to this {@link RegionFactoryBean}.
+ *
+ * @param regionName {@link String} containing the name of the {@link Region}.
+ * @param regionConfigurers array of {@link RegionConfigurer RegionConfigurers} applied
+ * to this {@link RegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ * @see #applyRegionConfigurers(String, Iterable)
*/
- @Override
- @SuppressWarnings({ "unchecked" })
- protected Region lookupRegion(GemFireCache gemfireCache, String regionName) throws Exception {
- Assert.isTrue(gemfireCache instanceof Cache, String.format("Unable to create Regions from '%1$s'.",
- gemfireCache));
+ protected void applyRegionConfigurers(String regionName, RegionConfigurer... regionConfigurers) {
+ applyRegionConfigurers(regionName, Arrays.asList(nullSafeArray(regionConfigurers, RegionConfigurer.class)));
+ }
- Cache cache = (Cache) gemfireCache;
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link RegionConfigurer RegionConfigurers}
+ * to this {@link RegionFactoryBean}.
+ *
+ * @param regionName {@link String} containing the name of the {@link Region}.
+ * @param regionConfigurers {@link Iterable} of {@link RegionConfigurer RegionConfigurers} applied
+ * to this {@link RegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ protected void applyRegionConfigurers(String regionName, Iterable regionConfigurers) {
+ StreamSupport.stream(nullSafeIterable(regionConfigurers).spliterator(), false)
+ .forEach(regionConfigurer -> regionConfigurer.configure(regionName, this));
+ }
- RegionFactory regionFactory = createRegionFactory(cache);
+ /* (non-Javadoc) */
+ private Region enableAsLockGrantor(Region region) {
- for (AsyncEventQueue asyncEventQueue : ArrayUtils.nullSafeArray(asyncEventQueues, AsyncEventQueue.class)) {
- regionFactory.addAsyncEventQueueId(asyncEventQueue.getId());
- }
-
- for (CacheListener listener : ArrayUtils.nullSafeArray(cacheListeners, CacheListener.class)) {
- regionFactory.addCacheListener(listener);
- }
-
- if (cacheLoader != null) {
- regionFactory.setCacheLoader(cacheLoader);
- }
-
- if (cacheWriter != null) {
- regionFactory.setCacheWriter(cacheWriter);
- }
-
- resolveDataPolicy(regionFactory, persistent, dataPolicy);
-
- if (isDiskStoreConfigurationAllowed()) {
- regionFactory.setDiskStoreName(diskStoreName);
- }
-
- if (evictionAttributes != null) {
- regionFactory.setEvictionAttributes(evictionAttributes);
- }
-
- for (GatewaySender gatewaySender : ArrayUtils.nullSafeArray(gatewaySenders, GatewaySender.class)) {
- regionFactory.addGatewaySenderId(((GatewaySender) gatewaySender).getId());
- }
-
- if (keyConstraint != null) {
- regionFactory.setKeyConstraint(keyConstraint);
- }
-
- if (scope != null) {
- regionFactory.setScope(scope);
- }
-
- if (valueConstraint != null) {
- regionFactory.setValueConstraint(valueConstraint);
- }
-
- if (attributes != null) {
- Assert.state(!attributes.isLockGrantor() || (scope == null) || scope.isGlobal(),
- "Lock Grantor only applies to a 'GLOBAL' scoped Region.");
- }
-
- postProcess(regionFactory);
-
- Region region = (getParent() != null ? regionFactory.createSubregion(getParent(), regionName)
- : regionFactory.create(regionName));
-
- if (log.isInfoEnabled()) {
- if (getParent() != null) {
- log.info(String.format("Created new Cache sub-Region [%1$s] under parent Region [%2$s].",
- regionName, getParent().getName()));
- }
- else {
- log.info(String.format("Created new Cache Region [%1$s].", regionName));
- }
- }
-
- if (snapshot != null) {
- region.loadSnapshot(snapshot.getInputStream());
- }
-
- if (attributes != null && attributes.isLockGrantor()) {
- region.becomeLockGrantor();
- }
+ Optional.ofNullable(region)
+ .filter(it -> it.getAttributes().isLockGrantor())
+ .ifPresent(Region::becomeLockGrantor);
return region;
}
+ /* (non-Javadoc) */
+ private Region newRegion(RegionFactory regionFactory, Region, ?> parentRegion, String regionName) {
+
+ return Optional.ofNullable(parentRegion)
+ .map(parent -> {
+ logInfo("Creating Subregion [%1$s] with parent Region [%2$s]",
+ regionName, parent.getName());
+
+ return regionFactory.createSubregion(parent, regionName);
+ })
+ .orElseGet(() -> {
+ logInfo("Created Region [%1$s]", regionName);
+
+ return regionFactory.create(regionName);
+ });
+ }
+
+ /* (non-Javadoc) */
+ private Cache resolveCache(GemFireCache gemfireCache) {
+
+ return Optional.ofNullable(gemfireCache)
+ .filter(cache -> cache instanceof Cache)
+ .map(cache -> (Cache) cache)
+ .orElseThrow(() -> newIllegalArgumentException("Peer Cache is required"));
+ }
+
+ /* (non-Javadoc) */
+ private RegionAttributes verifyLockGrantorEligibility(RegionAttributes regionAttributes, Scope scope) {
+
+ Optional.ofNullable(regionAttributes).ifPresent(attributes ->
+ Assert.state(!attributes.isLockGrantor() || verifyScope(scope),
+ "Lock Grantor only applies to GLOBAL Scoped Regions"));
+
+ return regionAttributes;
+ }
+
+ /* (non-Javadoc) */
+ private boolean verifyScope(Scope scope) {
+ return (scope == null || Scope.GLOBAL.equals(scope));
+ }
+
/**
- * Creates an instance of RegionFactory using the given Cache instance used to configure and construct the Region
- * created by this FactoryBean.
+ * Creates an instance of {@link RegionFactory} with the given {@link Cache} which is then used to construct,
+ * configure and initialize the {@link Region} specified by this {@link RegionFactoryBean}.
*
- * @param cache the GemFire Cache instance.
- * @return a RegionFactory used to configure and construct the Region created by this FactoryBean.
- * @see org.apache.geode.cache.Cache#createRegionFactory()
- * @see org.apache.geode.cache.Cache#createRegionFactory(org.apache.geode.cache.RegionAttributes)
+ * @param cache reference to the {@link Cache}.
+ * @return a {@link RegionFactory} used to construct, configure and initialized the {@link Region} specified by
+ * this {@link RegionFactoryBean}.
* @see org.apache.geode.cache.Cache#createRegionFactory(org.apache.geode.cache.RegionShortcut)
+ * @see org.apache.geode.cache.Cache#createRegionFactory(org.apache.geode.cache.RegionAttributes)
+ * @see org.apache.geode.cache.Cache#createRegionFactory()
* @see org.apache.geode.cache.RegionFactory
*/
protected RegionFactory createRegionFactory(Cache cache) {
- if (shortcut != null) {
- RegionFactory regionFactory = mergeRegionAttributes(
- cache.createRegionFactory(shortcut), attributes);
+
+ if (this.shortcut != null) {
+ RegionFactory regionFactory =
+ mergeRegionAttributes(cache.createRegionFactory(this.shortcut), this.attributes);
+
setDataPolicy(getDataPolicy(regionFactory));
+
return regionFactory;
}
- else if (attributes != null) {
- return cache.createRegionFactory(attributes);
+ else if (this.attributes != null) {
+ return cache.createRegionFactory(this.attributes);
}
else {
return cache.createRegionFactory();
}
}
+ /**
+ * Configures the {@link RegionFactory} based on the configuration settings of this {@link RegionFactoryBean}.
+ *
+ * @param regionFactory {@link RegionFactory} to configure
+ * @return the given {@link RegionFactory}.
+ * @see org.apache.geode.cache.RegionFactory
+ */
+ protected RegionFactory configure(RegionFactory regionFactory) {
+
+ stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
+ .forEach(asyncEventQueue -> regionFactory.addAsyncEventQueueId(asyncEventQueue.getId()));
+
+ stream(nullSafeArray(this.cacheListeners, CacheListener.class)).forEach(regionFactory::addCacheListener);
+
+ Optional.ofNullable(this.cacheLoader).ifPresent(regionFactory::setCacheLoader);
+
+ Optional.ofNullable(this.cacheWriter).ifPresent(regionFactory::setCacheWriter);
+
+ resolveDataPolicy(regionFactory, persistent, dataPolicy);
+
+ Optional.ofNullable(this.diskStoreName)
+ .filter(name -> isDiskStoreConfigurationAllowed())
+ .ifPresent(regionFactory::setDiskStoreName);
+
+ Optional.ofNullable(this.evictionAttributes).ifPresent(regionFactory::setEvictionAttributes);
+
+ stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
+ .forEach(gatewaySender -> regionFactory.addGatewaySenderId(((GatewaySender) gatewaySender).getId()));
+
+ Optional.ofNullable(this.keyConstraint).ifPresent(regionFactory::setKeyConstraint);
+
+ Optional.ofNullable(this.scope).ifPresent(regionFactory::setScope);
+
+ Optional.ofNullable(this.valueConstraint).ifPresent(regionFactory::setValueConstraint);
+
+ return regionFactory;
+ }
+
+ /**
+ * Post-process the {@link RegionFactory} used to create the {@link Region} specified by
+ * this {@link RegionFactoryBean} during initialization.
+ *
+ * The {@link RegionFactory} has been already constructed, configured and initialized by
+ * this {@link RegionFactoryBean} before this method gets invoked.
+ *
+ * @param regionFactory {@link RegionFactory} used to create the {@link Region}.
+ * @return the given {@link RegionFactory}.
+ * @see org.apache.geode.cache.RegionFactory
+ */
+ protected RegionFactory postProcess(RegionFactory regionFactory) {
+
+ regionFactory.setOffHeap(Boolean.TRUE.equals(this.offHeap));
+
+ return regionFactory;
+ }
+
+ /**
+ * Returns a reference to the Composite {@link RegionConfigurer} used to apply additional configuration
+ * to this {@link RegionFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link RegionConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ protected RegionConfigurer getCompositeRegionConfigurer() {
+ return this.compositeRegionConfigurer;
+ }
+
/*
- * (non-Javadoc) - This method should not be considered part of the RegionFactoryBean API
- * and is strictly for testing purposes!
+ * (non-Javadoc)
+ *
+ * This method is not considered part of the RegionFactoryBean API and is strictly used for testing purposes!
*
* NOTE cannot pass RegionAttributes.class as the "targetType" in the second invocation of getFieldValue(..)
* since the "regionAttributes" field is naively declared as a instance of the implementation class type
@@ -238,8 +366,8 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean void mergePartitionAttributes(final RegionFactory regionFactory,
- final RegionAttributes regionAttributes) {
+ /**
+ *
+ * @param regionFactory
+ * @param regionAttributes
+ */
+ protected void mergePartitionAttributes(RegionFactory regionFactory,
+ RegionAttributes regionAttributes) {
// NOTE PartitionAttributes are created by certain RegionShortcuts; need the null check since RegionAttributes
// can technically return null!
@@ -348,8 +482,9 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean regionFactory) {
- regionFactory.setOffHeap(Boolean.TRUE.equals(offHeap));
- }
-
- /**
- * Post-process the Region for this factory bean during the initialization process. The Region is
- * already configured and initialized by the factory bean before this method is invoked.
- *
- * @param region the GemFire Region to post-process.
- * @see org.apache.geode.cache.Region
- */
- protected Region postProcess(Region region) {
- return region;
- }
-
/**
* Validates and sets the Data Policy on the RegionFactory used to create and configure the Region from this
* FactoryBean.
@@ -478,6 +594,7 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean regionFactory, Boolean persistent, DataPolicy dataPolicy) {
+
if (dataPolicy != null) {
assertDataPolicyAndPersistentAttributesAreCompatible(dataPolicy);
regionFactory.setDataPolicy(dataPolicy);
@@ -499,10 +616,11 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean regionFactory, Boolean persistent, String dataPolicy) {
+
if (dataPolicy != null) {
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
- Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicy));
+ Assert.notNull(resolvedDataPolicy, String.format("Data Policy [%1$s] is invalid.", dataPolicy));
assertDataPolicyAndPersistentAttributesAreCompatible(resolvedDataPolicy);
regionFactory.setDataPolicy(resolvedDataPolicy);
@@ -521,16 +639,21 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean region = getObject();
- if (region != null) {
- if (close) {
+ Optional.ofNullable(getObject()).ifPresent(region -> {
+ if (this.close) {
if (!region.getRegionService().isClosed()) {
try {
region.close();
@@ -541,10 +664,10 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean region = getRegion();
- return (region != null ? region.getAttributes() : attributes);
+ public RegionAttributes getAttributes() {
+ return Optional.ofNullable(getRegion()).map(Region::getAttributes).orElse(this.attributes);
}
/**
@@ -662,8 +784,8 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean newIllegalStateException("Data Policy has not been properly resolved yet"));
}
/**
@@ -706,7 +828,7 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean regionConfigurers) {
+ this.regionConfigurers = Optional.ofNullable(regionConfigurers).orElseGet(Collections::emptyList);
+ }
+
+ public Scope getScope() {
+ return this.scope;
+ }
+
/**
* Sets the region scope. Used only when a new region is created. Overrides
* the settings specified through {@link #setAttributes(RegionAttributes)}.
@@ -749,23 +900,10 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBeancreated region. That
- * is, the snapshot will be used only 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;
- }
-
public void setValueConstraint(Class valueConstraint) {
this.valueConstraint = valueConstraint;
}
@@ -776,6 +914,7 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean extends RegionLookupFactoryBean
- implements FactoryBean>, InitializingBean, BeanNameAware {
-
- protected final Log log = LogFactory.getLog(getClass());
+public abstract class RegionLookupFactoryBean extends AbstractFactoryBeanSupport>
+ implements InitializingBean {
private Boolean lookupEnabled = false;
@@ -52,60 +56,109 @@ public abstract class RegionLookupFactoryBean
private Region, ?> parent;
+ private Resource snapshot;
+
private volatile Region region;
- private String beanName;
private String name;
private String regionName;
/**
- * @inheritDoc
+ * Initializes this {@link RegionLookupFactoryBean} after properties have been set by the Spring container.
+ *
+ * @throws Exception if initialization fails.
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ * @see #createRegion(GemFireCache, String)
*/
@Override
@SuppressWarnings("all")
public void afterPropertiesSet() throws Exception {
- Assert.notNull(this.cache, "A 'Cache' reference must be set");
+
+ GemFireCache cache = getCache();
+
+ Assert.notNull(cache, "Cache is required");
String regionName = resolveRegionName();
- Assert.hasText(regionName, "'regionName', 'name' or 'beanName' property must be set");
+ Assert.hasText(regionName, "regionName, name or beanName property must be set");
- synchronized (this.cache) {
- if (isLookupEnabled()) {
- this.region = Optional.ofNullable(getParent())
+ synchronized (cache) {
+ setRegion(isLookupEnabled()
+ ? Optional.ofNullable(getParent())
.map(parentRegion -> parentRegion.getSubregion(regionName))
- .orElseGet(() -> this.cache.getRegion(regionName));
- }
+ .orElseGet(() -> cache.getRegion(regionName))
+ : null);
- if (region != null) {
- log.info(String.format("Found Region [%1$s] in Cache [%2$s]", regionName, cache.getName()));
+ if (getRegion() != null) {
+ logInfo("Found Region [%1$s] in Cache [%2$s]", regionName, cache.getName());
}
else {
- log.info(String.format("Falling back to creating Region [%1$s] in Cache [%2$s]",
- regionName, cache.getName()));
+ logInfo("Falling back to creating Region [%1$s] in Cache [%2$s]",
+ regionName, cache.getName());
- region = lookupRegion(cache, regionName);
+ setRegion(postProcess(loadSnapshot(createRegion(cache, regionName))));
}
}
}
/**
- * Method to perform a lookup when the named {@link Region} does not exist. By default, this implementation
- * throws an exception.
+ * Creates a new {@link Region} with the given {@link String name}.
*
- * @param cache reference to the GemFire cache.
- * @param regionName name of the GemFire {@link Region}.
- * @return the {@link Region} in the GemFire cache with the given name.
- * @throws BeanInitializationException if the lookup operation fails.
+ * This method gets called when a {@link Region} with the specified {@link String name} does not already exist.
+ * By default, this method implementation throws a {@link BeanInitializationException} and it is expected
+ * that {@link Class subclasses} will override this method.
+ *
+ * @param cache reference to the {@link GemFireCache}.
+ * @param regionName {@link String name} of the new {@link Region}.
+ * @return a new {@link Region} with the given {@link String name}.
+ * @throws BeanInitializationException by default unless a {@link Class subclass} overrides this method.
+ * @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
- protected Region lookupRegion(GemFireCache cache, String regionName) throws Exception {
- throw new BeanInitializationException(String.format(
- "Region [%1$s] in Cache [%2$s] not found", regionName, cache));
+ protected Region createRegion(GemFireCache cache, String regionName) throws Exception {
+ throw new BeanInitializationException(
+ String.format("Region [%1$s] in Cache [%2$s] not found", regionName, cache));
}
/**
- * @inheritDoc
+ * Loads the configured data {@link Resource snapshot} into the given {@link Region}.
+ *
+ * @param region {@link Region} to load.
+ * @return the given {@link Region}.
+ * @throws RuntimeException if the snapshot load fails.
+ * @see org.apache.geode.cache.Region#loadSnapshot(InputStream)
+ */
+ protected Region loadSnapshot(Region region) {
+
+ Optional.ofNullable(this.snapshot).ifPresent(snapshot -> {
+ try {
+ region.loadSnapshot(snapshot.getInputStream());
+ }
+ catch (Exception e) {
+ throw newRuntimeException(e, "Failed to load snapshot [%s]", snapshot);
+ }
+ });
+
+ return region;
+ }
+
+ /**
+ * Post-process the {@link Region} created by this {@link RegionFactoryBean}.
+ *
+ * @param region {@link Region} to process.
+ * @see org.apache.geode.cache.Region
+ */
+ protected Region postProcess(Region region) {
+ return region;
+ }
+
+ /**
+ * Returns an object reference to the {@link Region} created by this {@link RegionLookupFactoryBean}.
+ *
+ * @return an object reference to the {@link Region} created by this {@link RegionLookupFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObject()
+ * @see org.apache.geode.cache.Region
+ * @see #getRegion()
*/
@Override
public Region getObject() throws Exception {
@@ -113,49 +166,42 @@ public abstract class RegionLookupFactoryBean
}
/**
- * @inheritDoc
- */
- @Override
- public Class> getObjectType() {
- Region region = getRegion();
- return (region != null ? region.getClass() : Region.class);
- }
-
- /**
- * @inheritDoc
- */
- @Override
- public boolean isSingleton() {
- return true;
- }
-
- /**
- * Resolves the name of the GemFire {@link Region}.
+ * Returns the {@link Class} type of the {@link Region} produced by this {@link RegionLookupFactoryBean}.
*
- * @return a {@link String} indicating the name of the GemFire {@link Region}.
+ * @return the {@link Class} type of the {@link Region} produced by this {@link RegionLookupFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Class> getObjectType() {
+ return Optional.ofNullable(getRegion()).map(Region::getClass).orElse((Class) Region.class);
+ }
+
+ /**
+ * Resolves the {@link String name} of the {@link Region}.
+ *
+ * @return a {@link String} containing the name of the {@link Region}.
* @see org.apache.geode.cache.Region#getName()
*/
public String resolveRegionName() {
return (StringUtils.hasText(this.regionName) ? this.regionName
- : (StringUtils.hasText(this.name) ? this.name : this.beanName));
+ : (StringUtils.hasText(this.name) ? this.name : getBeanName()));
}
/**
- * Sets the name of the {@link Region} based on the bean 'id' attribute. If no {@link Region} is found
- * with the given name, a new one will be created.
+ * Returns a reference to the {@link GemFireCache} used to create the {@link Region}.
*
- * @param name name of this {@link Region} bean in the Spring {@link org.springframework.context.ApplicationContext}.
- * @see org.springframework.beans.factory.BeanNameAware#setBeanName(String)
+ * @return a reference to the {@link GemFireCache} used to create the {@link Region}..
+ * @see org.apache.geode.cache.GemFireCache
*/
- public void setBeanName(String name) {
- this.beanName = name;
+ public GemFireCache getCache() {
+ return this.cache;
}
/**
* Sets a reference to the {@link GemFireCache} used to create the {@link Region}.
*
* @param cache reference to the {@link GemFireCache}.
- * @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.apache.geode.cache.GemFireCache
*/
public void setCache(GemFireCache cache) {
@@ -174,7 +220,7 @@ public abstract class RegionLookupFactoryBean
/* (non-Javadoc) */
public Boolean getLookupEnabled() {
- return lookupEnabled;
+ return this.lookupEnabled;
}
/**
@@ -217,13 +263,23 @@ public abstract class RegionLookupFactoryBean
}
/**
- * Returns a reference to the GemFire {@link Region} resolved by this Spring {@link FactoryBean}
- * during the lookup operation; maybe a new {@link Region}.
+ * Sets a reference to the {@link Region} to be resolved by this Spring {@link FactoryBean}.
*
- * @return a reference to the GemFire {@link Region} resolved during lookup.
+ * @param region reference to the resolvable {@link Region}.
* @see org.apache.geode.cache.Region
*/
- protected Region getRegion() {
+ protected void setRegion(Region region) {
+ this.region = region;
+ }
+
+ /**
+ * Returns a reference to the {@link Region} resolved by this Spring {@link FactoryBean}
+ * during the lookup operation; maybe a new {@link Region}.
+ *
+ * @return a reference to the {@link Region} resolved during lookup.
+ * @see org.apache.geode.cache.Region
+ */
+ public Region getRegion() {
return this.region;
}
@@ -238,4 +294,16 @@ public abstract class RegionLookupFactoryBean
public void setRegionName(String regionName) {
this.regionName = regionName;
}
+
+ /**
+ * Sets the snapshots used for loading a newly created region. That
+ * is, the snapshot will be used only 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;
+ }
}
diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java
index b84b358e..806d5b7c 100644
--- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java
@@ -16,9 +16,19 @@
package org.springframework.data.gemfire.client;
+import static java.util.stream.StreamSupport.stream;
+import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+
import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.cache.GemFireCache;
@@ -27,34 +37,32 @@ import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.distributed.DistributedSystem;
-import org.apache.geode.pdx.PdxSerializer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.client.support.DefaultableDelegatingPoolAdapter;
import org.springframework.data.gemfire.client.support.DelegatingPoolAdapter;
+import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.data.gemfire.support.ConnectionEndpointList;
import org.springframework.data.gemfire.util.SpringUtils;
-import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
/**
- * FactoryBean dedicated to creating GemFire client caches.
+ * Spring {@link org.springframework.beans.factory.FactoryBean} used to create a Pivotal GemFire/Apache Geode
+ * {@link ClientCache}.
*
* @author Costin Leau
* @author Lyndon Adams
* @author John Blum
- * @see org.springframework.context.ApplicationListener
- * @see org.springframework.context.event.ContextRefreshedEvent
- * @see org.springframework.data.gemfire.CacheFactoryBean
- * @see org.springframework.data.gemfire.support.ConnectionEndpoint
- * @see org.springframework.data.gemfire.support.ConnectionEndpointList
+ * @see java.net.InetSocketAddress
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientCacheFactory
@@ -62,6 +70,14 @@ import org.springframework.util.ObjectUtils;
* @see org.apache.geode.cache.client.PoolManager
* @see org.apache.geode.distributed.DistributedSystem
* @see org.apache.geode.pdx.PdxSerializer
+ * @see org.springframework.beans.factory.BeanFactory
+ * @see org.springframework.context.ApplicationContext
+ * @see org.springframework.context.ApplicationListener
+ * @see org.springframework.context.event.ContextRefreshedEvent
+ * @see org.springframework.data.gemfire.CacheFactoryBean
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ * @see org.springframework.data.gemfire.support.ConnectionEndpoint
+ * @see org.springframework.data.gemfire.support.ConnectionEndpointList
*/
@SuppressWarnings("unused")
public class ClientCacheFactoryBean extends CacheFactoryBean implements ApplicationListener {
@@ -89,6 +105,8 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
private Integer subscriptionMessageTrackingTimeout;
private Integer subscriptionRedundancy;
+ private List clientCacheConfigurers = Collections.emptyList();
+
private Long idleTimeout;
private Long pingInterval;
@@ -98,37 +116,83 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
private String poolName;
private String serverGroup;
+ private final ClientCacheConfigurer compositeClientCacheConfigurer = (beanName, bean) ->
+ nullSafeCollection(clientCacheConfigurers).forEach(clientCacheConfigurer ->
+ clientCacheConfigurer.configure(beanName, bean));
+
+ /**
+ * Post processes this {@link ClientCacheFactoryBean} before cache initialization.
+ *
+ * This is also the point at which any configured {@link ClientCacheConfigurer} beans are called.
+ *
+ * @param gemfireProperties {@link Properties} used to configure Pivotal GemFire/Apache Geode.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ * @see java.util.Properties
+ */
@Override
protected void postProcessBeforeCacheInitialization(Properties gemfireProperties) {
+ applyClientCacheConfigurers();
+ }
+
+ /* (non-Javadoc) */
+ private void applyClientCacheConfigurers() {
+ applyClientCacheConfigurers(this.compositeClientCacheConfigurer);
}
/**
- * Fetches an existing GemFire ClientCache instance from the ClientCacheFactory.
+ * Null-safe operation to apply the given array of {@link ClientCacheConfigurer ClientCacheConfigurers}
+ * to this {@link ClientCacheFactoryBean}.
*
- * @param is Class type extension of GemFireCache.
- * @return the existing GemFire ClientCache instance if available.
- * @throws org.apache.geode.cache.CacheClosedException if an existing GemFire Cache instance does not exist.
- * @throws java.lang.IllegalStateException if the GemFire cache instance is not a ClientCache.
- * @see org.apache.geode.cache.GemFireCache
+ * @param clientCacheConfigurers array of {@link ClientCacheConfigurer ClientCacheConfigurers} applied to
+ * this {@link ClientCacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ * @see #applyClientCacheConfigurers(Iterable)
+ */
+ protected void applyClientCacheConfigurers(ClientCacheConfigurer... clientCacheConfigurers) {
+ applyClientCacheConfigurers(Arrays.asList(nullSafeArray(clientCacheConfigurers, ClientCacheConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers}
+ * to this {@link ClientCacheFactoryBean}.
+ *
+ * @param clientCacheConfigurers {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers}
+ * applied to this {@link ClientCacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ * @see java.lang.Iterable
+ */
+ protected void applyClientCacheConfigurers(Iterable clientCacheConfigurers) {
+ stream(nullSafeIterable(clientCacheConfigurers).spliterator(), false)
+ .forEach(clientCacheConfigurer -> clientCacheConfigurer.configure(getBeanName(), this));
+ }
+
+ /**
+ * Fetches an existing {@link ClientCache} instance from the {@link ClientCacheFactory}.
+ *
+ * @param parameterized {@link Class} type extension of {@link GemFireCache}.
+ * @return an existing {@link ClientCache} instance if available.
+ * @throws org.apache.geode.cache.CacheClosedException if an existing {@link ClientCache} instance does not exist.
* @see org.apache.geode.cache.client.ClientCacheFactory#getAnyInstance()
+ * @see org.apache.geode.cache.GemFireCache
+ * @see #getCache()
*/
@Override
@SuppressWarnings("unchecked")
protected T fetchCache() {
- ClientCache cache = getCache();
- return (T) (cache != null ? cache : ClientCacheFactory.getAnyInstance());
+ return (T) Optional.ofNullable(getCache()).orElseGet(ClientCacheFactory::getAnyInstance);
}
/**
- * Resolves the GemFire System properties used to configure the GemFire ClientCache instance.
+ * Resolves the Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link ClientCache}.
*
- * @return a Properties object containing GemFire System properties used to configure
- * the GemFire ClientCache instance.
+ * @return the resolved Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link ClientCache}.
+ * @see org.apache.geode.distributed.DistributedSystem#getProperties()
+ * @see #getDistributedSystem()
*/
@Override
protected Properties resolveProperties() {
- Properties gemfireProperties = super.resolveProperties();
+ Properties gemfireProperties = super.resolveProperties();
DistributedSystem distributedSystem = getDistributedSystem();
if (GemfireUtils.isConnected(distributedSystem)) {
@@ -137,24 +201,32 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
gemfireProperties = distributedSystemProperties;
}
- GemfireUtils.configureDurableClient(gemfireProperties, durableClientId, durableClientTimeout);
+ GemfireUtils.configureDurableClient(gemfireProperties, getDurableClientId(), getDurableClientTimeout());
return gemfireProperties;
}
- /* (non-Javadoc) */
+ /**
+ * Returns the {@link DistributedSystem} formed from cache initialization.
+ *
+ * @param {@link Class} type of the {@link DistributedSystem}.
+ * @return an instance of the {@link DistributedSystem}.
+ * @see org.apache.geode.distributed.DistributedSystem
+ */
T getDistributedSystem() {
return GemfireUtils.getDistributedSystem();
}
/**
- * Creates an instance of GemFire factory initialized with the given GemFire System Properties
- * to create an instance of a GemFire cache.
+ * Constructs a new instance of {@link ClientCacheFactory} initialized with the given Pivotal GemFire/Apache Geode
+ * {@link Properties} used to create an instance of a {@link ClientCache}.
*
- * @param gemfireProperties a Properties object containing GemFire System properties.
- * @return an instance of a GemFire factory used to create a GemFire cache instance.
- * @see java.util.Properties
+ * @param gemfireProperties {@link Properties} used by the {@link ClientCacheFactory}
+ * to configure the {@link ClientCache}.
+ * @return a new instance of {@link ClientCacheFactory} initialized with the given Pivotal GemFire/Apache Geode
+ * {@link Properties}.
* @see org.apache.geode.cache.client.ClientCacheFactory
+ * @see java.util.Properties
*/
@Override
protected Object createFactory(Properties gemfireProperties) {
@@ -162,61 +234,52 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
/**
- * Initializes the GemFire factory used to create the GemFire client cache instance. Sets PDX options
- * specified by the user.
+ * Prepares and initializes the {@link ClientCacheFactory} used to create the {@link ClientCache}.
*
- * @param factory the GemFire factory used to create an instance of the GemFire client cache.
- * @return the initialized GemFire client cache factory.
- * @see #isPdxOptionsSpecified()
+ * Sets PDX options specified by the user.
+ *
+ * @param factory {@link ClientCacheFactory} used to create the {@link ClientCache}.
+ * @return the prepared and initialized {@link ClientCacheFactory}.
+ * @see #initializePdx(ClientCacheFactory)
*/
@Override
- protected Object prepareFactory(final Object factory) {
+ protected Object prepareFactory(Object factory) {
return initializePool(initializePdx((ClientCacheFactory) factory));
}
/**
- * Initialize the PDX settings on the {@link ClientCacheFactory}.
+ * Configure PDX for the {@link ClientCacheFactory}.
*
- * @param clientCacheFactory the GemFire {@link ClientCacheFactory} used to configure and create
- * a GemFire {@link ClientCache}.
+ * @param clientCacheFactory {@link ClientCacheFactory} used to configure PDX.
+ * @return the given {@link ClientCacheFactory}
* @see org.apache.geode.cache.client.ClientCacheFactory
*/
ClientCacheFactory initializePdx(ClientCacheFactory clientCacheFactory) {
- if (isPdxOptionsSpecified()) {
- if (getPdxSerializer() != null) {
- Assert.isInstanceOf(PdxSerializer.class, getPdxSerializer(),
- String.format("[%1$s] of type [%2$s] is not a PdxSerializer;", getPdxSerializer(),
- ObjectUtils.nullSafeClassName(getPdxSerializer())));
- clientCacheFactory.setPdxSerializer((PdxSerializer) getPdxSerializer());
- }
- if (getPdxDiskStoreName() != null) {
- clientCacheFactory.setPdxDiskStore(getPdxDiskStoreName());
- }
- if (getPdxIgnoreUnreadFields() != null) {
- clientCacheFactory.setPdxIgnoreUnreadFields(getPdxIgnoreUnreadFields());
- }
- if (getPdxPersistent() != null) {
- clientCacheFactory.setPdxPersistent(getPdxPersistent());
- }
- if (getPdxReadSerialized() != null) {
- clientCacheFactory.setPdxReadSerialized(getPdxReadSerialized());
- }
- }
+ Optional.ofNullable(getPdxSerializer()).ifPresent(clientCacheFactory::setPdxSerializer);
+
+ Optional.ofNullable(getPdxDiskStoreName()).filter(StringUtils::hasText)
+ .ifPresent(clientCacheFactory::setPdxDiskStore);
+
+ Optional.ofNullable(getPdxIgnoreUnreadFields()).ifPresent(clientCacheFactory::setPdxIgnoreUnreadFields);
+
+ Optional.ofNullable(getPdxPersistent()).ifPresent(clientCacheFactory::setPdxPersistent);
+
+ Optional.ofNullable(getPdxReadSerialized()).ifPresent(clientCacheFactory::setPdxReadSerialized);
return clientCacheFactory;
}
/**
- * Initialize the {@link Pool} settings on the {@link ClientCacheFactory} with a given {@link Pool} instance
- * or named {@link Pool}.
+ * Configure the {@literal DEFAULT} {@link Pool} configuration settings with the {@link ClientCacheFactory}
+ * using a given {@link Pool} instance or a named {@link Pool}.
*
- * @param clientCacheFactory the GemFire {@link ClientCacheFactory} used to configure and create
- * a GemFire {@link ClientCache}.
+ * @param clientCacheFactory {@link ClientCacheFactory} use to configure the {@literal DEFAULT} {@link Pool}.
* @see org.apache.geode.cache.client.ClientCacheFactory
* @see org.apache.geode.cache.client.Pool
*/
ClientCacheFactory initializePool(ClientCacheFactory clientCacheFactory) {
+
DefaultableDelegatingPoolAdapter pool = DefaultableDelegatingPoolAdapter.from(
DelegatingPoolAdapter.from(resolvePool())).preferDefault();
@@ -239,48 +302,51 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
clientCacheFactory.setPoolSubscriptionRedundancy(pool.getSubscriptionRedundancy(getSubscriptionRedundancy()));
clientCacheFactory.setPoolThreadLocalConnections(pool.getThreadLocalConnections(getThreadLocalConnections()));
- boolean noServers = getServers().isEmpty();
- boolean hasServers = !noServers;
+ final AtomicBoolean noServers = new AtomicBoolean(getServers().isEmpty());
+
+ boolean hasServers = !noServers.get();
boolean noLocators = getLocators().isEmpty();
boolean hasLocators = !noLocators;
if (hasServers || noLocators) {
Iterable servers = pool.getServers(getServers().toInetSocketAddresses());
- for (InetSocketAddress server : servers) {
+ stream(servers.spliterator(), false).forEach(server -> {
clientCacheFactory.addPoolServer(server.getHostName(), server.getPort());
- noServers = false;
- }
+ noServers.set(false);
+ });
}
- if (hasLocators || noServers) {
+ if (hasLocators || noServers.get()) {
Iterable locators = pool.getLocators(getLocators().toInetSocketAddresses());
- for (InetSocketAddress locator : locators) {
- clientCacheFactory.addPoolLocator(locator.getHostName(), locator.getPort());
- }
+ stream(locators.spliterator(), false).forEach(locator ->
+ clientCacheFactory.addPoolLocator(locator.getHostName(), locator.getPort()));
}
return clientCacheFactory;
}
/**
- * Resolves the appropriate GemFire {@link Pool} from Spring configuration that will be used to configure
- * the GemFire {@link ClientCache}.
+ * Resolves an appropriate {@link Pool} from the Spring container that will be used to configure
+ * the {@link ClientCache}.
*
- * @return the resolved GemFire {@link Pool}.
- * @see org.apache.geode.cache.client.PoolManager#find(String)
+ * @return the resolved {@link Pool}.
* @see org.apache.geode.cache.client.Pool
+ * @see #findPool(String)
*/
Pool resolvePool() {
Pool localPool = getPool();
if (localPool == null) {
- String poolName = SpringUtils.defaultIfNull(getPoolName(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
+
+ String poolName = Optional.ofNullable(getPoolName()).filter(StringUtils::hasText)
+ .orElse(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
localPool = findPool(poolName);
if (localPool == null) {
+
BeanFactory beanFactory = getBeanFactory();
if (beanFactory instanceof ListableBeanFactory) {
@@ -295,8 +361,8 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
}
catch (BeansException e) {
- log.info(String.format("unable to resolve bean of type [%1$s] with name [%2$s]",
- PoolFactoryBean.class.getName(), poolName));
+ logInfo("Unable to resolve bean of type [%1$s] with name [%2$s]",
+ PoolFactoryBean.class.getName(), poolName);
}
}
}
@@ -306,11 +372,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
/**
- * Attempts to find a GemFire {@link Pool} with the given name.
+ * Attempts to find a {@link Pool} with the given {@link String name}.
*
- * @param name a String indicating the name of the GemFire {@link Pool} to find.
- * @return a {@link Pool} instance with the given name registered in GemFire or null if no {@link Pool}
- * with name exists.
+ * @param name {@link String} containing the name of the {@link Pool} to find.
+ * @return a {@link Pool} instance with the given {@link String name} registered in GemFire/Geode
+ * or {@literal null} if no {@link Pool} with the given {@link String name} exists.
* @see org.apache.geode.cache.client.PoolManager#find(String)
* @see org.apache.geode.cache.client.Pool
*/
@@ -319,11 +385,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
/**
- * Creates a new GemFire cache instance using the provided factory.
+ * Creates a new {@link ClientCache} instance using the provided factory.
*
- * @param parameterized Class type extension of {@link GemFireCache}.
- * @param factory the appropriate GemFire factory used to create a cache instance.
- * @return an instance of the GemFire cache.
+ * @param parameterized {@link Class} type extension of {@link GemFireCache}.
+ * @param factory instance of {@link ClientCacheFactory}.
+ * @return a new instance of {@link ClientCache} created by the provided factory.
* @see org.apache.geode.cache.client.ClientCacheFactory#create()
* @see org.apache.geode.cache.GemFireCache
*/
@@ -334,39 +400,48 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
/**
- * Inform the GemFire cluster that this client cache is ready to receive events iff the client is non-durable.
+ * Inform the Pivotal GemFire/Apache Geode cluster that this cache client is ready to receive events
+ * iff the client is non-durable.
*
- * @param event the ApplicationContextEvent fired when the ApplicationContext is refreshed.
+ * @param event {@link ApplicationContextEvent} fired when the {@link ApplicationContext} is refreshed.
* @see org.apache.geode.cache.client.ClientCache#readyForEvents()
- * @see #getReadyForEvents()
- * @see #getObject()
+ * @see #isReadyForEvents()
+ * @see #fetchCache()
*/
@Override
- public void onApplicationEvent(final ContextRefreshedEvent event) {
+ public void onApplicationEvent(ContextRefreshedEvent event) {
if (isReadyForEvents()) {
try {
- ((ClientCache) fetchCache()).readyForEvents();
+ this.fetchCache().readyForEvents();
}
- catch (IllegalStateException ignore) {
+ catch (IllegalStateException | CacheClosedException ignore) {
// thrown if clientCache.readyForEvents() is called on a non-durable client
}
- catch (CacheClosedException ignore) {
- // cache is closed or was shutdown so ready-for-events is moot
- }
}
}
- /* (non-Javadoc) */
+ /**
+ * Null-safe internal method used to close the {@link ClientCache} and preserve durability.
+ *
+ * @param cache {@link GemFireCache} to close.
+ * @see org.apache.geode.cache.client.ClientCache#close(boolean)
+ * @see #isKeepAlive()
+ */
@Override
protected void close(GemFireCache cache) {
((ClientCache) cache).close(isKeepAlive());
}
- /* (non-Javadoc) */
+ /**
+ * Returns the {@link Class} type of the {@link GemFireCache} produced by this {@link ClientCacheFactoryBean}.
+ *
+ * @return the {@link Class} type of the {@link GemFireCache} produced by this {@link ClientCacheFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
@Override
+ @SuppressWarnings("unchecked")
public Class extends GemFireCache> getObjectType() {
- ClientCache cache = getCache();
- return (cache != null ? cache.getClass() : ClientCache.class);
+ return Optional.ofNullable(getCache()).map(Object::getClass).orElse((Class) ClientCache.class);
}
/* (non-Javadoc) */
@@ -389,6 +464,42 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
this.servers.add(servers);
}
+ /**
+ * Null-safe operation to set an array of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
+ * additional configuration to this {@link ClientCacheFactoryBean} when using Annotation-based configuration.
+ *
+ * @param clientCacheConfigurers array of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
+ * additional configuration to this {@link ClientCacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ * @see #setClientCacheConfigurers(List)
+ */
+ public void setClientCacheConfigurers(ClientCacheConfigurer... clientCacheConfigurers) {
+ setClientCacheConfigurers(Arrays.asList(nullSafeArray(clientCacheConfigurers, ClientCacheConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers} to apply
+ * additional configuration to this {@link ClientCacheFactoryBean} when using Annotation-based configuration.
+ *
+ * @param peerCacheConfigurers {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
+ * additional configuration to this {@link ClientCacheFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ */
+ public void setClientCacheConfigurers(List peerCacheConfigurers) {
+ this.clientCacheConfigurers = Optional.ofNullable(peerCacheConfigurers).orElseGet(Collections::emptyList);
+ }
+
+ /**
+ * Returns a reference to the Composite {@link ClientCacheConfigurer} used to apply additional configuration
+ * to this {@link ClientCacheFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link ClientCacheConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
+ */
+ public ClientCacheConfigurer getCompositeClientCacheConfigurer() {
+ return this.compositeClientCacheConfigurer;
+ }
+
/**
* Set the GemFire System property 'durable-client-id' to indicate to the server that this client is durable.
*
@@ -405,7 +516,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
* @return a String value indicating the durable client id.
*/
public String getDurableClientId() {
- return durableClientId;
+ return this.durableClientId;
}
/**
@@ -427,13 +538,13 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
* the durable client's queue around.
*/
public Integer getDurableClientTimeout() {
- return durableClientTimeout;
+ return this.durableClientTimeout;
}
/* (non-Javadoc) */
@Override
- public final void setEnableAutoReconnect(final Boolean enableAutoReconnect) {
- throw new UnsupportedOperationException("Auto-reconnect does not apply to clients.");
+ public final void setEnableAutoReconnect(Boolean enableAutoReconnect) {
+ throw new UnsupportedOperationException("Auto-reconnect does not apply to clients");
}
/* (non-Javadoc) */
@@ -566,7 +677,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
* to the GemFire cluster.
*/
public Pool getPool() {
- return pool;
+ return this.pool;
}
/**
@@ -772,7 +883,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
/* (non-Javadoc) */
@Override
public final void setUseClusterConfiguration(Boolean useClusterConfiguration) {
- throw new UnsupportedOperationException("Shared, cluster-based configuration is not applicable for clients.");
+ throw new UnsupportedOperationException("Cluster-based Configuration is not applicable for clients");
}
/* (non-Javadoc) */
diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java
index 1cf137f5..db5c44eb 100644
--- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java
@@ -16,10 +16,18 @@
package org.springframework.data.gemfire.client;
+import static java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.StreamSupport;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheWriter;
@@ -33,28 +41,22 @@ import org.apache.geode.cache.client.ClientRegionFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
-import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.DataPolicyConverter;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
+import org.springframework.data.gemfire.config.annotation.RegionConfigurer;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
- * Spring {@link FactoryBean} used to create a GemFire client cache {@link Region}.
+ * Spring {@link FactoryBean} used to construct, configure and initialize a client {@link Region}.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
- * @see org.springframework.beans.factory.BeanFactory
- * @see org.springframework.beans.factory.BeanFactoryAware
- * @see org.springframework.beans.factory.DisposableBean
- * @see org.springframework.data.gemfire.RegionLookupFactoryBean
* @see org.apache.geode.cache.DataPolicy
* @see org.apache.geode.cache.EvictionAttributes
* @see org.apache.geode.cache.GemFireCache
@@ -64,18 +66,18 @@ import org.springframework.util.StringUtils;
* @see org.apache.geode.cache.client.ClientRegionFactory
* @see org.apache.geode.cache.client.ClientRegionShortcut
* @see org.apache.geode.cache.client.Pool
+ * @see org.springframework.beans.factory.DisposableBean
+ * @see org.springframework.beans.factory.FactoryBean
+ * @see org.springframework.data.gemfire.DataPolicyConverter
+ * @see org.springframework.data.gemfire.RegionLookupFactoryBean
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
*/
@SuppressWarnings("unused")
-public class ClientRegionFactoryBean extends RegionLookupFactoryBean
- implements BeanFactoryAware, DisposableBean {
-
- private static final Log log = LogFactory.getLog(ClientRegionFactoryBean.class);
+public class ClientRegionFactoryBean extends RegionLookupFactoryBean implements DisposableBean {
private boolean close = false;
private boolean destroy = false;
- private BeanFactory beanFactory;
-
private Boolean persistent;
private CacheListener[] cacheListeners;
@@ -87,7 +89,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
private Class keyConstraint;
private Class valueConstraint;
- private ClientRegionShortcut shortcut = null;
+ private ClientRegionShortcut shortcut;
private DataPolicy dataPolicy;
@@ -95,97 +97,183 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
private Interest[] interests;
+ private List regionConfigurers = Collections.emptyList();
+
private RegionAttributes attributes;
- private Resource snapshot;
+ private RegionConfigurer compositeRegionConfigurer = new RegionConfigurer() {
+
+ @Override
+ public void configure(String beanName, ClientRegionFactoryBean, ?> bean) {
+ nullSafeCollection(regionConfigurers)
+ .forEach(regionConfigurer -> regionConfigurer.configure(beanName, bean));
+ }
+ };
private String diskStoreName;
private String poolName;
/**
- * @inheritDoc
+ * Creates a new {@link Region} with the given {@link String name}.
+ *
+ * @param gemfireCache reference to the {@link GemFireCache}.
+ * @param regionName {@link String name} of the new {@link Region}.
+ * @return a new {@link Region} with the given {@link String name}.
+ * @see org.apache.geode.cache.GemFireCache
+ * @see org.apache.geode.cache.Region
*/
@Override
- public void afterPropertiesSet() throws Exception {
- super.afterPropertiesSet();
- postProcess(getRegion());
- }
+ protected Region createRegion(GemFireCache gemfireCache, String regionName) throws Exception {
- /**
- * @inheritDoc
- */
- @Override
- protected Region lookupRegion(GemFireCache cache, String regionName) throws Exception {
- Assert.isTrue(GemfireUtils.isClient(cache), "A ClientCache is required to create a client Region");
+ applyRegionConfigurers(regionName);
+
+ ClientCache cache = resolveCache(gemfireCache);
ClientRegionFactory clientRegionFactory =
- ((ClientCache) cache).createClientRegionFactory(resolveClientRegionShortcut());
+ configure(createClientRegionFactory(cache, resolveClientRegionShortcut()));
- setAttributes(clientRegionFactory);
- addCacheListeners(clientRegionFactory);
- setDiskStoreName(clientRegionFactory);
- setEvictionAttributes(clientRegionFactory);
- setPoolName(clientRegionFactory);
-
- if (keyConstraint != null) {
- clientRegionFactory.setKeyConstraint(keyConstraint);
- }
-
- if (valueConstraint != null) {
- clientRegionFactory.setValueConstraint(valueConstraint);
- }
-
- return logCreateRegionEvent(create(clientRegionFactory, regionName));
- }
-
- /* (non-Javadoc) */
- private Region create(ClientRegionFactory clientRegionFactory, String regionName) {
- return (getParent() != null ? clientRegionFactory.createSubregion(getParent(), regionName)
- : clientRegionFactory.create(regionName));
- }
-
- /* (non-Javadoc) */
- private Region logCreateRegionEvent(Region region) {
- if (log.isInfoEnabled()) {
- if (getParent() != null) {
- log.info(String.format("Created new client cache Sub-Region [%1$s] under parent Region [%2$s].",
- region.getName(), getParent().getName()));
- }
- else {
- log.info(String.format("Created new client cache Region [%s].", region.getName()));
- }
- }
+ @SuppressWarnings("all")
+ Region region = newRegion(clientRegionFactory, getParent(), regionName);
return region;
}
+ /* (non-Javadoc) */
+ private void applyRegionConfigurers(String regionName) {
+ applyRegionConfigurers(regionName, getCompositeRegionConfigurer());
+ }
+
/**
- * Resolves the {@link ClientRegionShortcut} used to configure the data policy of the client {@link Region}.
+ * Null-safe operation to apply the given array of {@link RegionConfigurer RegionConfigurers}
+ * to this {@link ClientRegionFactoryBean}.
*
- * @return a {@link ClientRegionShortcut} used to configure the data policy of the client {@link Region}.
+ * @param regionName {@link String} containing the name of the {@link Region}.
+ * @param regionConfigurers array of {@link RegionConfigurer RegionConfigurers} applied
+ * to this {@link ClientRegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ * @see #applyRegionConfigurers(String, Iterable)
+ */
+ protected void applyRegionConfigurers(String regionName, RegionConfigurer... regionConfigurers) {
+ applyRegionConfigurers(regionName, Arrays.asList(nullSafeArray(regionConfigurers, RegionConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link RegionConfigurer RegionConfigurers}
+ * to this {@link ClientRegionFactoryBean}.
+ *
+ * @param regionName {@link String} containing the name of the {@link Region}.
+ * @param regionConfigurers {@link Iterable} of {@link RegionConfigurer RegionConfigurers} applied
+ * to this {@link ClientRegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ protected void applyRegionConfigurers(String regionName, Iterable regionConfigurers) {
+ StreamSupport.stream(nullSafeIterable(regionConfigurers).spliterator(), false)
+ .forEach(regionConfigurer -> regionConfigurer.configure(regionName, this));
+ }
+
+ /**
+ * Assert the settings for {@link ClientRegionShortcut} and the {@literal persistent} attribute
+ * in <gfe:*-region> elements are compatible.
+ *
+ * @param resolvedShortcut {@link ClientRegionShortcut} resolved from the SDG XML namespace.
+ * @see org.springframework.data.gemfire.client.ClientRegionShortcutWrapper
* @see org.apache.geode.cache.client.ClientRegionShortcut
+ * @see #isNotPersistent()
+ * @see #isPersistent()
+ */
+ private void assertClientRegionShortcutAndPersistentAttributeAreCompatible(ClientRegionShortcut resolvedShortcut) {
+
+ final boolean persistentNotSpecified = (this.persistent == null);
+
+ if (ClientRegionShortcutWrapper.valueOf(resolvedShortcut).isPersistent()) {
+ Assert.isTrue(persistentNotSpecified || isPersistent(),
+ String.format("Client Region Shortcut [%s] is not valid when persistent is false", resolvedShortcut));
+ }
+ else {
+ Assert.isTrue(persistentNotSpecified || isNotPersistent(),
+ String.format("Client Region Shortcut [%s] is not valid when persistent is true", resolvedShortcut));
+ }
+ }
+
+ /**
+ * Assert the settings for {@link DataPolicy} and the persistent attribute
+ * in <gfe:*-region> elements are compatible.
+ *
+ * @param resolvedDataPolicy {@link DataPolicy} resolved from the SDG XML namespace.
+ * @see org.apache.geode.cache.DataPolicy
+ * @see #isNotPersistent()
+ * @see #isPersistent()
+ */
+ private void assertDataPolicyAndPersistentAttributeAreCompatible(DataPolicy resolvedDataPolicy) {
+
+ if (resolvedDataPolicy.withPersistence()) {
+ Assert.isTrue(isPersistentUnspecified() || isPersistent(),
+ String.format("Data Policy [%s] is not valid when persistent is false", resolvedDataPolicy));
+ }
+ else {
+ Assert.isTrue(isPersistentUnspecified() || isNotPersistent(),
+ String.format("Data Policy [%s] is not valid when persistent is true", resolvedDataPolicy));
+ }
+ }
+
+ /* (non-Javadoc) */
+ private Region newRegion(ClientRegionFactory clientRegionFactory,
+ Region, ?> parentRegion, String regionName) {
+
+ return Optional.ofNullable(parentRegion)
+ .map(parent -> {
+ logInfo("Creating client Subregion [%1$s] with parent Region [%2$s]",
+ regionName, parent.getName());
+
+ return clientRegionFactory.createSubregion(parent, regionName);
+ })
+ .orElseGet(() -> {
+ logInfo("Created client Region [%s]", regionName);
+
+ return clientRegionFactory.create(regionName);
+ });
+ }
+
+ /* (non-Javadoc) */
+ private ClientCache resolveCache(GemFireCache gemfireCache) {
+
+ return Optional.ofNullable(gemfireCache)
+ .filter(GemfireUtils::isClient)
+ .map(cache -> (ClientCache) cache)
+ .orElseThrow(() -> newIllegalArgumentException("ClientCache is required"));
+ }
+
+ /**
+ * Resolves the {@link ClientRegionShortcut} used to configure the {@link DataPolicy} of the client {@link Region}.
+ *
+ * @return a {@link ClientRegionShortcut} used to configure the {@link DataPolicy} of the client {@link Region}.
+ * @see org.apache.geode.cache.client.ClientRegionShortcut
+ * @see org.apache.geode.cache.DataPolicy
*/
ClientRegionShortcut resolveClientRegionShortcut() {
ClientRegionShortcut resolvedShortcut = this.shortcut;
if (resolvedShortcut == null) {
- if (this.dataPolicy != null) {
- assertDataPolicyAndPersistentAttributeAreCompatible(this.dataPolicy);
- if (DataPolicy.EMPTY.equals(this.dataPolicy)) {
+ DataPolicy dataPolicy = this.dataPolicy;
+
+ if (dataPolicy != null) {
+
+ assertDataPolicyAndPersistentAttributeAreCompatible(dataPolicy);
+
+ if (DataPolicy.EMPTY.equals(dataPolicy)) {
resolvedShortcut = ClientRegionShortcut.PROXY;
}
- else if (DataPolicy.NORMAL.equals(this.dataPolicy)) {
+ else if (DataPolicy.NORMAL.equals(dataPolicy)) {
resolvedShortcut = ClientRegionShortcut.CACHING_PROXY;
}
- else if (DataPolicy.PERSISTENT_REPLICATE.equals(this.dataPolicy)) {
+ else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) {
resolvedShortcut = ClientRegionShortcut.LOCAL_PERSISTENT;
}
else {
// NOTE the DataPolicy validation is based on the ClientRegionShortcut initialization logic
// in org.apache.geode.internal.cache.GemFireCacheImpl.initializeClientRegionShortcuts
- throw new IllegalArgumentException(String.format("Data Policy '%s' is invalid for Client Regions",
- this.dataPolicy));
+ throw newIllegalArgumentException("Data Policy [%s] is not valid for the client Region", dataPolicy);
}
}
else {
@@ -194,145 +282,20 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
}
}
- // NOTE the ClientRegionShortcut and Persistent attribute will be compatible if the shortcut
- // was derived from the Data Policy.
+ // NOTE the ClientRegionShortcut and Persistent attribute will be compatible
+ // if the shortcut was derived from the Data Policy.
assertClientRegionShortcutAndPersistentAttributeAreCompatible(resolvedShortcut);
return resolvedShortcut;
}
- /**
- * Validates that the settings for ClientRegionShortcut and the 'persistent' attribute in <gfe:*-region> elements
- * are compatible.
- *
- * @param resolvedShortcut the GemFire ClientRegionShortcut resolved form the Spring GemFire XML namespace
- * configuration meta-data.
- * @see #isPersistent()
- * @see #isNotPersistent()
- * @see org.apache.geode.cache.client.ClientRegionShortcut
- */
- private void assertClientRegionShortcutAndPersistentAttributeAreCompatible(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 '%s' is invalid when persistent is false", resolvedShortcut));
- }
- else {
- Assert.isTrue(persistentNotSpecified || isNotPersistent(), String.format(
- "Client Region Shortcut '%s' is invalid when persistent is true", resolvedShortcut));
- }
- }
-
- /**
- * Validates that the settings for Data Policy and the 'persistent' attribute in <gfe:*-region> elements
- * are compatible.
- *
- * @param resolvedDataPolicy the GemFire Data Policy resolved form the Spring GemFire XML namespace configuration
- * meta-data.
- * @see #isPersistent()
- * @see #isNotPersistent()
- * @see org.apache.geode.cache.DataPolicy
- */
- private void assertDataPolicyAndPersistentAttributeAreCompatible(DataPolicy resolvedDataPolicy) {
- if (resolvedDataPolicy.withPersistence()) {
- Assert.isTrue(isPersistentUnspecified() || isPersistent(), String.format(
- "Data Policy '%s' is invalid when persistent is false", resolvedDataPolicy));
- }
- else {
- // NOTE otherwise, the Data Policy is without persistence, so...
- Assert.isTrue(isPersistentUnspecified() || isNotPersistent(), String.format(
- "Data Policy '%s' is invalid when persistent is true", resolvedDataPolicy));
- }
- }
-
- /* (non-Javadoc) */
- private ClientRegionFactory setAttributes(ClientRegionFactory clientRegionFactory) {
- RegionAttributes localAttributes = this.attributes;
-
- if (localAttributes != null) {
- clientRegionFactory.setCloningEnabled(localAttributes.getCloningEnabled());
- clientRegionFactory.setCompressor(localAttributes.getCompressor());
- clientRegionFactory.setConcurrencyChecksEnabled(localAttributes.getConcurrencyChecksEnabled());
- clientRegionFactory.setConcurrencyLevel(localAttributes.getConcurrencyLevel());
- clientRegionFactory.setCustomEntryIdleTimeout(localAttributes.getCustomEntryIdleTimeout());
- clientRegionFactory.setCustomEntryTimeToLive(localAttributes.getCustomEntryTimeToLive());
- clientRegionFactory.setDiskStoreName(localAttributes.getDiskStoreName());
- clientRegionFactory.setDiskSynchronous(localAttributes.isDiskSynchronous());
- clientRegionFactory.setEntryIdleTimeout(localAttributes.getEntryIdleTimeout());
- clientRegionFactory.setEntryTimeToLive(localAttributes.getEntryTimeToLive());
- clientRegionFactory.setEvictionAttributes(localAttributes.getEvictionAttributes());
- clientRegionFactory.setInitialCapacity(localAttributes.getInitialCapacity());
- clientRegionFactory.setKeyConstraint(localAttributes.getKeyConstraint());
- clientRegionFactory.setLoadFactor(localAttributes.getLoadFactor());
- clientRegionFactory.setPoolName(localAttributes.getPoolName());
- clientRegionFactory.setRegionIdleTimeout(localAttributes.getRegionIdleTimeout());
- clientRegionFactory.setRegionTimeToLive(localAttributes.getRegionTimeToLive());
- clientRegionFactory.setStatisticsEnabled(localAttributes.getStatisticsEnabled());
- clientRegionFactory.setValueConstraint(localAttributes.getValueConstraint());
- }
-
- return clientRegionFactory;
- }
-
- /* (non-Javadoc) */
- @SuppressWarnings("unchecked")
- private ClientRegionFactory addCacheListeners(ClientRegionFactory clientRegionFactory) {
- for (CacheListener cacheListener : this.attributesCacheListeners()) {
- clientRegionFactory.addCacheListener(cacheListener);
- }
-
- for (CacheListener cacheListener : nullSafeArray(this.cacheListeners, CacheListener.class)) {
- clientRegionFactory.addCacheListener(cacheListener);
- }
-
- return clientRegionFactory;
- }
-
- /* (non-Javadoc) */
- @SuppressWarnings("unchecked")
- private CacheListener[] attributesCacheListeners() {
- CacheListener[] cacheListeners = (this.attributes != null ? this.attributes.getCacheListeners() : null);
- return nullSafeArray(cacheListeners, CacheListener.class);
- }
-
- /* (non-Javadoc) */
- private ClientRegionFactory setDiskStoreName(ClientRegionFactory clientRegionFactory) {
- if (StringUtils.hasText(this.diskStoreName)) {
- clientRegionFactory.setDiskStoreName(this.diskStoreName);
- }
-
- return clientRegionFactory;
- }
-
- /* (non-Javadoc) */
- private ClientRegionFactory setEvictionAttributes(ClientRegionFactory clientRegionFactory) {
- if (this.evictionAttributes != null) {
- clientRegionFactory.setEvictionAttributes(this.evictionAttributes);
- }
-
- return clientRegionFactory;
- }
-
- /* (non-Javadoc) */
- private ClientRegionFactory setPoolName(ClientRegionFactory clientRegionFactory) {
- String poolName = resolvePoolName();
-
- if (StringUtils.hasText(poolName)) {
- clientRegionFactory.setPoolName(eagerlyInitializePool(poolName));
- }
-
- return clientRegionFactory;
- }
-
/* (non-Javadoc) */
private String resolvePoolName() {
String poolName = this.poolName;
if (!StringUtils.hasText(poolName)) {
String defaultPoolName = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
- poolName = (this.beanFactory.containsBean(defaultPoolName) ? defaultPoolName : poolName);
+ poolName = (getBeanFactory().containsBean(defaultPoolName) ? defaultPoolName : poolName);
}
return poolName;
@@ -340,35 +303,117 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
/* (non-Javadoc) */
private String eagerlyInitializePool(String poolName) {
- try {
- if (this.beanFactory.isTypeMatch(poolName, Pool.class)) {
- if (log.isDebugEnabled()) {
- log.debug(String.format("Found bean definition for Pool [%1$s]; Eagerly initializing...", poolName));
- }
- this.beanFactory.getBean(poolName, Pool.class);
+ try {
+ if (getBeanFactory().isTypeMatch(poolName, Pool.class)) {
+ logDebug("Found bean definition for Pool [%s]; Eagerly initializing...", poolName);
+ getBeanFactory().getBean(poolName, Pool.class);
}
}
catch (BeansException ignore) {
- log.warn(ignore.getMessage());
+ getLog().warn(ignore.getMessage(), ignore.getCause());
}
return poolName;
}
- /* (non-Javadoc) */
- protected void postProcess(Region region) throws Exception {
- loadSnapshot(region);
- registerInterests(region);
- setCacheLoader(region);
- setCacheWriter(region);
+ /**
+ * Constructs a new instance of {@link ClientRegionFactory} using the given {@link ClientCache}
+ * and {@link ClientRegionShortcut}.
+ *
+ * @param cache reference to the {@link ClientCache}.
+ * @param shortcut {@link ClientRegionShortcut} used to specify the client {@link Region} {@link DataPolicy}.
+ * @return a new instance of {@link ClientRegionFactory}.
+ * @see org.apache.geode.cache.client.ClientCache#createClientRegionFactory(ClientRegionShortcut)
+ * @see org.apache.geode.cache.client.ClientRegionShortcut
+ * @see org.apache.geode.cache.client.ClientRegionFactory
+ */
+ protected ClientRegionFactory createClientRegionFactory(ClientCache cache, ClientRegionShortcut shortcut) {
+ return cache.createClientRegionFactory(shortcut);
}
- /* (non-Javadoc) */
- private Region loadSnapshot(Region region) throws Exception {
- if (snapshot != null) {
- region.loadSnapshot(snapshot.getInputStream());
- }
+ /**
+ * Configures the given {@link ClientRegionFactoryBean} from the configuration settings
+ * of this {@link ClientRegionFactoryBean}.
+ *
+ * @param clientRegionFactory {@link ClientRegionFactory} to configure.
+ * @return the given {@link ClientRegionFactory}.
+ * @see org.apache.geode.cache.client.ClientRegionFactory
+ */
+ protected ClientRegionFactory configure(ClientRegionFactory clientRegionFactory) {
+
+ Optional.ofNullable(this.attributes).ifPresent(attributes -> {
+
+ stream(nullSafeArray(attributes.getCacheListeners(), CacheListener.class))
+ .forEach(clientRegionFactory::addCacheListener);
+
+ clientRegionFactory.setCloningEnabled(attributes.getCloningEnabled());
+ clientRegionFactory.setCompressor(attributes.getCompressor());
+ clientRegionFactory.setConcurrencyChecksEnabled(attributes.getConcurrencyChecksEnabled());
+ clientRegionFactory.setConcurrencyLevel(attributes.getConcurrencyLevel());
+ clientRegionFactory.setCustomEntryIdleTimeout(attributes.getCustomEntryIdleTimeout());
+ clientRegionFactory.setCustomEntryTimeToLive(attributes.getCustomEntryTimeToLive());
+ clientRegionFactory.setDiskStoreName(attributes.getDiskStoreName());
+ clientRegionFactory.setDiskSynchronous(attributes.isDiskSynchronous());
+ clientRegionFactory.setEntryIdleTimeout(attributes.getEntryIdleTimeout());
+ clientRegionFactory.setEntryTimeToLive(attributes.getEntryTimeToLive());
+ clientRegionFactory.setEvictionAttributes(attributes.getEvictionAttributes());
+ clientRegionFactory.setInitialCapacity(attributes.getInitialCapacity());
+ clientRegionFactory.setKeyConstraint(attributes.getKeyConstraint());
+ clientRegionFactory.setLoadFactor(attributes.getLoadFactor());
+ clientRegionFactory.setPoolName(attributes.getPoolName());
+ clientRegionFactory.setRegionIdleTimeout(attributes.getRegionIdleTimeout());
+ clientRegionFactory.setRegionTimeToLive(attributes.getRegionTimeToLive());
+ clientRegionFactory.setStatisticsEnabled(attributes.getStatisticsEnabled());
+ clientRegionFactory.setValueConstraint(attributes.getValueConstraint());
+ });
+
+ stream(nullSafeArray(this.cacheListeners, CacheListener.class)).forEach(clientRegionFactory::addCacheListener);
+
+ Optional.ofNullable(this.diskStoreName).filter(StringUtils::hasText)
+ .ifPresent(clientRegionFactory::setDiskStoreName);
+
+ Optional.ofNullable(this.evictionAttributes).ifPresent(clientRegionFactory::setEvictionAttributes);
+
+ Optional.ofNullable(this.keyConstraint).ifPresent(clientRegionFactory::setKeyConstraint);
+
+ Optional.ofNullable(resolvePoolName()).filter(StringUtils::hasText)
+ .ifPresent(poolName -> clientRegionFactory.setPoolName(eagerlyInitializePool(poolName)));
+
+ Optional.ofNullable(this.valueConstraint).ifPresent(clientRegionFactory::setValueConstraint);
+
+ return clientRegionFactory;
+ }
+
+ /**
+ * Post-process the given {@link ClientRegionFactory} setup by this {@link ClientRegionFactoryBean}.
+ *
+ * @param clientRegionFactory {@link ClientRegionFactory} to process.
+ * @return the given {@link ClientRegionFactory}.
+ * @see org.apache.geode.cache.client.ClientRegionFactory
+ */
+ protected ClientRegionFactory postProcess(ClientRegionFactory clientRegionFactory) {
+ return clientRegionFactory;
+ }
+
+ /**
+ * Post-process the {@link Region} created by this {@link ClientRegionFactoryBean}.
+ *
+ * @param region {@link Region} to process.
+ * @see org.apache.geode.cache.Region
+ */
+ @Override
+ protected Region postProcess(Region region) {
+
+ super.postProcess(region);
+
+ registerInterests(region);
+
+ Optional.ofNullable(this.cacheLoader)
+ .ifPresent(cacheLoader -> region.getAttributesMutator().setCacheLoader(cacheLoader));
+
+ Optional.ofNullable(this.cacheWriter)
+ .ifPresent(cacheWriter -> region.getAttributesMutator().setCacheWriter(cacheWriter));
return region;
}
@@ -376,47 +421,32 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
private Region registerInterests(Region region) {
- for (Interest interest : nullSafeArray(interests, Interest.class)) {
+
+ stream(nullSafeArray(this.interests, Interest.class)).forEach(interest -> {
if (interest.isRegexType()) {
region.registerInterestRegex((String) interest.getKey(), interest.getPolicy(),
interest.isDurable(), interest.isReceiveValues());
}
else {
- region.registerInterest(interest.getKey(), interest.getPolicy(), interest.isDurable(),
- interest.isReceiveValues());
+ region.registerInterest(((Interest) interest).getKey(), interest.getPolicy(),
+ interest.isDurable(), interest.isReceiveValues());
}
- }
-
- return region;
- }
-
- /* (non-Javadoc) */
- private Region setCacheLoader(Region region) {
- if (cacheLoader != null) {
- region.getAttributesMutator().setCacheLoader(this.cacheLoader);
- }
-
- return region;
- }
-
- /* (non-Javadoc) */
- private Region setCacheWriter(Region region) {
- if (cacheWriter != null) {
- region.getAttributesMutator().setCacheWriter(this.cacheWriter);
- }
+ });
return region;
}
/**
- * @inheritDoc
+ * Closes and destroys the {@link Region}.
+ *
+ * @throws Exception if destroy fails.
+ * @see org.springframework.beans.factory.DisposableBean
*/
@Override
public void destroy() throws Exception {
- Region region = getObject();
- if (region != null) {
- if (close) {
+ Optional.ofNullable(getObject()).ifPresent(region -> {
+ if (isClose()) {
if (!region.getRegionService().isClosed()) {
try {
region.close();
@@ -426,10 +456,21 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
}
}
- if (destroy) {
+ if (isDestroy()) {
region.destroyRegion();
}
- }
+ });
+ }
+
+ /**
+ * Returns a reference to the Composite {@link RegionConfigurer} used to apply additional configuration
+ * to this {@link ClientRegionFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link RegionConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ protected RegionConfigurer getCompositeRegionConfigurer() {
+ return this.compositeRegionConfigurer;
}
/**
@@ -446,29 +487,6 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
this.attributes = attributes;
}
- @Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.beanFactory = beanFactory;
- }
-
- /* (non-Javadoc) */
- 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.
- *
- * @param close whether to close or not the region
- * @see #setDestroy(boolean)
- */
- public void setClose(boolean close) {
- this.close = close;
- this.destroy = (this.destroy && !close); // retain previous value iff close is false.
- }
-
/**
* Sets the cache listeners used for the region used by this factory. Used
* only when a new region is created.Overrides the settings specified
@@ -500,6 +518,24 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
this.cacheWriter = cacheWriter;
}
+ /* (non-Javadoc) */
+ final boolean isClose() {
+ return this.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.
+ *
+ * @param close whether to close or not the region
+ * @see #setDestroy(boolean)
+ */
+ public void setClose(boolean close) {
+ this.close = close;
+ this.destroy = (this.destroy && !close); // retain previous value iff close is false.
+ }
+
/**
* Sets the Data Policy. Used only when a new Region is created.
*
@@ -521,13 +557,13 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
@Deprecated
public void setDataPolicyName(String dataPolicyName) {
DataPolicy resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicyName);
- Assert.notNull(resolvedDataPolicy, String.format("Data Policy '%1$s' is invalid.", dataPolicyName));
+ Assert.notNull(resolvedDataPolicy, String.format("Data Policy [%1$s] is not valid", dataPolicyName));
setDataPolicy(resolvedDataPolicy);
}
/* (non-Javadoc) */
final boolean isDestroy() {
- return destroy;
+ return this.destroy;
}
/**
@@ -598,8 +634,8 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
* @see org.apache.geode.cache.client.Pool
*/
public void setPool(Pool pool) {
- Assert.notNull(pool, "Pool cannot be null");
- setPoolName(pool.getName());
+ setPoolName(Optional.ofNullable(pool).map(Pool::getName)
+ .orElseThrow(() -> newIllegalArgumentException("Pool cannot be null")));
}
/**
@@ -608,8 +644,33 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
* @param poolName String specifying the name of the GemFire client {@link Pool}.
*/
public void setPoolName(String poolName) {
- Assert.hasText(poolName, "Pool name is required");
- this.poolName = poolName;
+ this.poolName = Optional.ofNullable(poolName).filter(StringUtils::hasText)
+ .orElseThrow(() -> newIllegalArgumentException("Pool name is required"));
+ }
+
+ /**
+ * Null-safe operation to set an array of {@link RegionConfigurer RegionConfigurers} used to apply
+ * additional configuration to this {@link ClientRegionFactoryBean} when using Annotation-based configuration.
+ *
+ * @param regionConfigurers array of {@link RegionConfigurer RegionConfigurers} used to apply
+ * additional configuration to this {@link ClientRegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ * @see #setRegionConfigurers(List)
+ */
+ public void setRegionConfigurers(RegionConfigurer... regionConfigurers) {
+ setRegionConfigurers(Arrays.asList(nullSafeArray(regionConfigurers, RegionConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link RegionConfigurer RegionConfigurers} used to apply
+ * additional configuration to this {@link ClientRegionFactoryBean} when using Annotation-based configuration.
+ *
+ * @param regionConfigurers {@link Iterable} of {@link RegionConfigurer RegionConfigurers} used to apply
+ * additional configuration to this {@link ClientRegionFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
+ */
+ public void setRegionConfigurers(List regionConfigurers) {
+ this.regionConfigurers = Optional.ofNullable(regionConfigurers).orElseGet(Collections::emptyList);
}
/**
@@ -621,18 +682,6 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean
this.shortcut = shortcut;
}
- /**
- * Specifies the data snapshots used for loading a newly created {@link Region}.
- * The snapshot will be used only when a new {@link Region} is created.
- * If the {@link Region} already exists, no loading will be performed.
- *
- * @param snapshot {@link Resource} referencing the snapshot used to load the {@link Region} with data.
- * @see org.springframework.core.io.Resource
- */
- public void setSnapshot(Resource snapshot) {
- this.snapshot = snapshot;
- }
-
public void setValueConstraint(Class valueConstraint) {
this.valueConstraint = valueConstraint;
}
diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionShortcutWrapper.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionShortcutWrapper.java
index 20eb3e22..9a1fe03f 100644
--- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionShortcutWrapper.java
+++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionShortcutWrapper.java
@@ -31,6 +31,7 @@ import org.springframework.util.ObjectUtils;
*/
@SuppressWarnings("unused")
public enum ClientRegionShortcutWrapper {
+
CACHING_PROXY(ClientRegionShortcut.CACHING_PROXY, DataPolicy.NORMAL),
CACHING_PROXY_HEAP_LRU(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU, DataPolicy.NORMAL),
CACHING_PROXY_OVERFLOW(ClientRegionShortcut.CACHING_PROXY_OVERFLOW, DataPolicy.NORMAL),
@@ -46,11 +47,6 @@ public enum ClientRegionShortcutWrapper {
private final DataPolicy dataPolicy;
- ClientRegionShortcutWrapper(ClientRegionShortcut clientRegionShortcut, DataPolicy dataPolicy) {
- this.clientRegionShortcut = clientRegionShortcut;
- this.dataPolicy = dataPolicy;
- }
-
public static ClientRegionShortcutWrapper valueOf(ClientRegionShortcut clientRegionShortcut) {
for (ClientRegionShortcutWrapper wrapper : values()) {
if (ObjectUtils.nullSafeEquals(wrapper.getClientRegionShortcut(), clientRegionShortcut)) {
@@ -61,6 +57,11 @@ public enum ClientRegionShortcutWrapper {
return ClientRegionShortcutWrapper.UNSPECIFIED;
}
+ ClientRegionShortcutWrapper(ClientRegionShortcut clientRegionShortcut, DataPolicy dataPolicy) {
+ this.clientRegionShortcut = clientRegionShortcut;
+ this.dataPolicy = dataPolicy;
+ }
+
public ClientRegionShortcut getClientRegionShortcut() {
return this.clientRegionShortcut;
}
diff --git a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java
index 5602f1c9..c79fb8ec 100644
--- a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java
@@ -16,74 +16,68 @@
package org.springframework.data.gemfire.client;
+import static java.util.stream.StreamSupport.stream;
+import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
+import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
+import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
+
import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolFactory;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.distributed.DistributedSystem;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.GemfireUtils;
+import org.springframework.data.gemfire.config.annotation.PoolConfigurer;
+import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.data.gemfire.support.ConnectionEndpointList;
import org.springframework.data.gemfire.util.DistributedSystemUtils;
-import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
- * FactoryBean for easy declaration and configuration of a GemFire {@link Pool}. If a new {@link Pool} is created,
- * its lifecycle is bound to that of this declaring factory.
+ * Spring {@link FactoryBean} to construct, configure and initialize a {@link Pool}.
*
- * Note, if a {@link Pool} having the configured name already exists, then the existing {@link Pool} will be returned
- * as is without any modifications and its lifecycle will be unaffected by this factory.
+ * If a new {@link Pool} is created, its lifecycle is bound to that of this declaring {@link FactoryBean}
+ * and indirectly, the Spring container.
+ *
+ * If a {@link Pool} having the configured {@link String name} already exists, then the existing {@link Pool}
+ * will be returned as is without any modifications and its lifecycle will be unaffected by this {@link FactoryBean}.
*
* @author Costin Leau
* @author John Blum
* @see java.net.InetSocketAddress
- * @see org.springframework.beans.factory.BeanNameAware
- * @see org.springframework.beans.factory.DisposableBean
- * @see org.springframework.beans.factory.FactoryBean
- * @see org.springframework.beans.factory.InitializingBean
- * @see org.springframework.data.gemfire.support.ConnectionEndpoint
- * @see org.springframework.data.gemfire.support.ConnectionEndpointList
+ * @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.client.PoolFactory
* @see org.apache.geode.cache.client.PoolManager
+ * @see org.apache.geode.distributed.DistributedSystem
+ * @see org.springframework.beans.factory.DisposableBean
+ * @see org.springframework.beans.factory.InitializingBean
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ * @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
+ * @see org.springframework.data.gemfire.support.ConnectionEndpoint
+ * @see org.springframework.data.gemfire.support.ConnectionEndpointList
*/
@SuppressWarnings("unused")
-public class PoolFactoryBean implements FactoryBean, InitializingBean, DisposableBean,
- BeanNameAware, BeanFactoryAware {
+public class PoolFactoryBean extends AbstractFactoryBeanSupport implements DisposableBean, InitializingBean {
protected static final int DEFAULT_LOCATOR_PORT = DistributedSystemUtils.DEFAULT_LOCATOR_PORT;
protected static final int DEFAULT_SERVER_PORT = DistributedSystemUtils.DEFAULT_CACHE_SERVER_PORT;
- private static final Log log = LogFactory.getLog(PoolFactoryBean.class);
-
// indicates whether the Pool has been created internally (by this FactoryBean) or not
volatile boolean springBasedPool = true;
- private BeanFactory beanFactory;
-
- private ConnectionEndpointList locators = new ConnectionEndpointList();
- private ConnectionEndpointList servers = new ConnectionEndpointList();
-
- private volatile Pool pool;
-
- private String beanName;
- private String name;
-
// GemFire Pool Configuration Settings
private boolean keepAlive = false;
private boolean multiUserAuthentication = PoolFactory.DEFAULT_MULTIUSER_AUTHENTICATION;
@@ -106,109 +100,150 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
private long idleTimeout = PoolFactory.DEFAULT_IDLE_TIMEOUT;
private long pingInterval = PoolFactory.DEFAULT_PING_INTERVAL;
+ private ConnectionEndpointList locators = new ConnectionEndpointList();
+ private ConnectionEndpointList servers = new ConnectionEndpointList();
+
+ private List poolConfigurers = Collections.emptyList();
+
+ private volatile Pool pool;
+
+ private PoolConfigurer compositePoolConfigurer = (beanName, bean) ->
+ nullSafeCollection(poolConfigurers).forEach(poolConfigurer -> poolConfigurer.configure(beanName, bean));
+
+ private String name;
private String serverGroup = PoolFactory.DEFAULT_SERVER_GROUP;
/**
- * Constructs and initializes a GemFire {@link Pool}.
+ * Prepares the construction, configuration and initialization of a new {@link Pool}.
*
- * @throws Exception if the {@link Pool} creation and initialization fails.
- * @see org.apache.geode.cache.client.Pool
- * @see org.apache.geode.cache.client.PoolFactory
+ * @throws Exception if {@link Pool} initialization fails.
* @see org.apache.geode.cache.client.PoolManager
- * @see #createPoolFactory()
+ * @see org.apache.geode.cache.client.PoolFactory
+ * @see org.apache.geode.cache.client.Pool
*/
@Override
public void afterPropertiesSet() throws Exception {
- if (!StringUtils.hasText(name)) {
- Assert.hasText(beanName, "Pool 'name' is required");
- this.name = beanName;
- }
-
- // check for an existing, configured Pool with name first
- Pool existingPool = PoolManager.find(name);
-
- if (existingPool != null) {
- if (log.isDebugEnabled()) {
- log.debug(String.format("A Pool with name [%1$s] already exists; using existing Pool.", name));
- }
-
- this.springBasedPool = false;
- this.pool = existingPool;
- }
- else {
- if (log.isDebugEnabled()) {
- log.debug(String.format("No Pool with name [%1$s] was found. Creating new Pool.", name));
- }
-
- this.springBasedPool = true;
- }
+ init(Optional.ofNullable(PoolManager.find(validatePoolName())));
}
- /**
- * Destroys the GemFire {@link Pool} if created by this {@link PoolFactoryBean} and releases all system resources
- * used by the {@link Pool}.
- *
- * @throws Exception if the {@link Pool} destruction caused an error.
- * @see DisposableBean#destroy()
- */
- @Override
- public void destroy() throws Exception {
- if (springBasedPool && pool != null && !pool.isDestroyed()) {
- pool.releaseThreadLocalConnection();
- pool.destroy(keepAlive);
- pool = null;
+ /* (non-Javadoc) */
+ @SuppressWarnings("all")
+ private void init(Optional existingPool) {
- if (log.isDebugEnabled()) {
- log.debug(String.format("Destroyed Pool [%1$s]", name));
- }
+ if (existingPool.isPresent()) {
+ this.pool = existingPool.get();
+ this.springBasedPool = false;
+
+ logDebug(() -> String.format(
+ "Pool with name [%s] already exists; Using existing Pool; Pool Configurers [%d] will not be applied",
+ existingPool.get().getName(), this.poolConfigurers.size()));
+ }
+ else {
+ this.springBasedPool = true;
+ applyPoolConfigurers();
+
+ logDebug("No Pool with name [%s] was found; Creating new Pool", getName());
}
}
/* (non-Javadoc) */
- @Override
- public Pool getObject() throws Exception {
- if (this.pool == null) {
- eagerlyInitializeClientCacheIfNotPresent();
-
- PoolFactory poolFactory = createPoolFactory();
-
- poolFactory.setFreeConnectionTimeout(freeConnectionTimeout);
- poolFactory.setIdleTimeout(idleTimeout);
- poolFactory.setLoadConditioningInterval(loadConditioningInterval);
- poolFactory.setMaxConnections(maxConnections);
- poolFactory.setMinConnections(minConnections);
- poolFactory.setMultiuserAuthentication(multiUserAuthentication);
- poolFactory.setPingInterval(pingInterval);
- poolFactory.setPRSingleHopEnabled(prSingleHopEnabled);
- poolFactory.setReadTimeout(readTimeout);
- poolFactory.setRetryAttempts(retryAttempts);
- poolFactory.setServerGroup(serverGroup);
- poolFactory.setSocketBufferSize(socketBufferSize);
- poolFactory.setStatisticInterval(statisticInterval);
- poolFactory.setSubscriptionAckInterval(subscriptionAckInterval);
- poolFactory.setSubscriptionEnabled(subscriptionEnabled);
- poolFactory.setSubscriptionMessageTrackingTimeout(subscriptionMessageTrackingTimeout);
- poolFactory.setSubscriptionRedundancy(subscriptionRedundancy);
- poolFactory.setThreadLocalConnections(threadLocalConnections);
-
- for (ConnectionEndpoint locator : this.locators) {
- poolFactory.addLocator(locator.getHost(), locator.getPort());
- }
-
- for (ConnectionEndpoint server : this.servers) {
- poolFactory.addServer(server.getHost(), server.getPort());
- }
-
- pool = poolFactory.create(name);
- }
-
- return pool;
+ private void applyPoolConfigurers() {
+ applyPoolConfigurers(getCompositePoolConfigurer());
}
/**
- * Determines whether the GemFire DistributedSystem exists yet or not.
+ * Null-safe operation to apply the given array of {@link PoolConfigurer PoolConfigurers}
+ * to this {@link PoolFactoryBean}.
*
- * @return a boolean value indicating whether the single, GemFire DistributedSystem has been created already.
+ * @param poolConfigurers array of {@link PoolConfigurer PoolConfigurers} applied to this {@link PoolFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ * @see #applyPoolConfigurers(Iterable)
+ */
+ protected void applyPoolConfigurers(PoolConfigurer... poolConfigurers) {
+ applyPoolConfigurers(Arrays.asList(nullSafeArray(poolConfigurers, PoolConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to apply the given {@link Iterable} of {@link PoolConfigurer PoolConfigurers}
+ * to this {@link PoolFactoryBean}.
+ *
+ * @param poolConfigurers {@link Iterable} of {@link PoolConfigurer PoolConfigurers}
+ * applied to this {@link PoolFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ */
+ protected void applyPoolConfigurers(Iterable poolConfigurers) {
+ stream(nullSafeIterable(poolConfigurers).spliterator(), false)
+ .forEach(poolConfigurer -> poolConfigurer.configure(getName(), this));
+ }
+
+ /* (non-Javadoc) */
+ private String validatePoolName() {
+
+ if (!StringUtils.hasText(getName())) {
+ setName(Optional.ofNullable(getBeanName()).filter(StringUtils::hasText)
+ .orElseThrow(() -> newIllegalArgumentException("Pool name is required")));
+ }
+
+ return getName();
+ }
+
+ /**
+ * Releases all system resources and destroys the {@link Pool} when created by this {@link PoolFactoryBean}.
+ *
+ * @throws Exception if the {@link Pool} destruction caused an error.
+ * @see org.springframework.beans.factory.DisposableBean#destroy()
+ */
+ @Override
+ public void destroy() throws Exception {
+
+ Optional.ofNullable(this.pool)
+ .filter(pool -> this.springBasedPool)
+ .filter(pool -> !pool.isDestroyed())
+ .ifPresent(pool -> {
+ pool.releaseThreadLocalConnection();
+ pool.destroy(this.keepAlive);
+ setPool(null);
+ logDebug("Destroyed Pool [%s]", pool.getName());
+ });
+ }
+
+ /**
+ * Returns a reference to the Composite {@link PoolConfigurer} used to apply additional configuration
+ * to this {@link PoolFactoryBean} on Spring container initialization.
+ *
+ * @return the Composite {@link PoolConfigurer}.
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ */
+ protected PoolConfigurer getCompositePoolConfigurer() {
+ return this.compositePoolConfigurer;
+ }
+
+ /**
+ * Returns an object reference to the {@link Pool} created by this {@link PoolFactoryBean}.
+ *
+ * @return an object reference to the {@link Pool} created by this {@link PoolFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObject()
+ * @see org.apache.geode.cache.client.Pool
+ */
+ @Override
+ public Pool getObject() throws Exception {
+
+ return Optional.ofNullable(this.pool).orElseGet(() -> {
+
+ eagerlyInitializeClientCacheIfNotPresent();
+
+ PoolFactory poolFactory = configure(createPoolFactory());
+
+ this.pool = create(poolFactory, getName());
+
+ return this.pool;
+ });
+ }
+
+ /**
+ * Determines whether the {@link DistributedSystem} exists yet or not.
+ *
+ * @return a boolean value indicating whether the single, {@link DistributedSystem} has already been created.
* @see org.springframework.data.gemfire.GemfireUtils#getDistributedSystem()
* @see org.springframework.data.gemfire.GemfireUtils#isConnected(DistributedSystem)
* @see org.apache.geode.distributed.DistributedSystem
@@ -218,22 +253,22 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
}
/**
- * Attempts to eagerly initialize the GemFire {@link ClientCache} if not already present so that the single
- * {@link org.apache.geode.distributed.DistributedSystem} will exists, which is required to create
- * a {@link Pool} instance.
+ * Attempts to eagerly initialize the {@link ClientCache} if not already present so that a single
+ * {@link DistributedSystem} will exist, which is required to create a {@link Pool} instance.
*
* @see org.springframework.beans.factory.BeanFactory#getBean(Class)
* @see org.apache.geode.cache.client.ClientCache
+ * @see org.apache.geode.distributed.DistributedSystem
+ * @see #isDistributedSystemPresent()
*/
- void eagerlyInitializeClientCacheIfNotPresent() {
+ private void eagerlyInitializeClientCacheIfNotPresent() {
if (!isDistributedSystemPresent()) {
getBeanFactory().getBean(ClientCache.class);
}
}
/**
- * Creates an instance of the GemFire {@link PoolFactory} interface to construct, configure and initialize
- * a GemFire {@link Pool}.
+ * Creates an instance of the {@link PoolFactory} interface to construct, configure and initialize a {@link Pool}.
*
* @return a {@link PoolFactory} implementation to create a {@link Pool}.
* @see org.apache.geode.cache.client.PoolManager#createFactory()
@@ -243,16 +278,68 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
return PoolManager.createFactory();
}
- /* (non-Javadoc) */
- @Override
- public Class> getObjectType() {
- return (this.pool != null ? this.pool.getClass() : Pool.class);
+ /**
+ * Configures the given {@link PoolFactory} from this {@link PoolFactoryBean}.
+ *
+ * @param poolFactory {@link PoolFactory} to configure.
+ * @return the given {@link PoolFactory}.
+ * @see org.apache.geode.cache.client.PoolFactory
+ */
+ protected PoolFactory configure(PoolFactory poolFactory) {
+
+ Optional.ofNullable(poolFactory).ifPresent(it -> {
+ it.setFreeConnectionTimeout(this.freeConnectionTimeout);
+ it.setIdleTimeout(this.idleTimeout);
+ it.setLoadConditioningInterval(this.loadConditioningInterval);
+ it.setMaxConnections(this.maxConnections);
+ it.setMinConnections(this.minConnections);
+ it.setMultiuserAuthentication(this.multiUserAuthentication);
+ it.setPingInterval(this.pingInterval);
+ it.setPRSingleHopEnabled(this.prSingleHopEnabled);
+ it.setReadTimeout(this.readTimeout);
+ it.setRetryAttempts(this.retryAttempts);
+ it.setServerGroup(this.serverGroup);
+ it.setSocketBufferSize(this.socketBufferSize);
+ it.setStatisticInterval(this.statisticInterval);
+ it.setSubscriptionAckInterval(this.subscriptionAckInterval);
+ it.setSubscriptionEnabled(this.subscriptionEnabled);
+ it.setSubscriptionMessageTrackingTimeout(this.subscriptionMessageTrackingTimeout);
+ it.setSubscriptionRedundancy(this.subscriptionRedundancy);
+ it.setThreadLocalConnections(this.threadLocalConnections);
+
+ nullSafeCollection(this.locators).forEach(locator ->
+ it.addLocator(locator.getHost(), locator.getPort()));
+
+ nullSafeCollection(this.servers).forEach(server ->
+ it.addServer(server.getHost(), server.getPort()));
+ });
+
+ return poolFactory;
}
- /* (non-Javadoc) */
+ /**
+ * Creates a {@link Pool} with the given {@link String name} using the provided {@link PoolFactory}.
+ *
+ * @param poolFactory {@link PoolFactory} used to create the {@link Pool}.
+ * @param poolName {@link String name} of the new {@link Pool}.
+ * @return a new instance of {@link Pool} with the given {@link String name}.
+ * @see org.apache.geode.cache.client.PoolFactory#create(String)
+ * @see org.apache.geode.cache.client.Pool
+ */
+ protected Pool create(PoolFactory poolFactory, String poolName) {
+ return poolFactory.create(poolName);
+ }
+
+ /**
+ * Returns the {@link Class} type of the {@link Pool} produced by this {@link PoolFactoryBean}.
+ *
+ * @return the {@link Class} type of the {@link Pool} produced by this {@link PoolFactoryBean}.
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
@Override
- public boolean isSingleton() {
- return true;
+ @SuppressWarnings("unchecked")
+ public Class> getObjectType() {
+ return Optional.ofNullable(this.pool).map(Pool::getClass).orElse((Class) Pool.class);
}
/* (non-Javadoc) */
@@ -275,29 +362,14 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
this.servers.add(servers);
}
- /* (non-Javadoc) */
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.beanFactory = beanFactory;
- }
-
- /* (non-Javadoc) */
- protected BeanFactory getBeanFactory() {
- return beanFactory;
- }
-
- /* (non-Javadoc) */
- public void setBeanName(String name) {
- this.beanName = name;
- }
-
/* (non-Javadoc) */
public void setName(String name) {
this.name = name;
}
/* (non-Javadoc) */
- String getName() {
- return name;
+ protected String getName() {
+ return this.name;
}
/* (non-Javadoc) */
@@ -357,9 +429,8 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
@Override
public String getName() {
- String name = PoolFactoryBean.this.name;
- name = (StringUtils.hasText(name) ? name : PoolFactoryBean.this.beanName);
- return name;
+ return Optional.ofNullable(PoolFactoryBean.this.getName()).filter(StringUtils::hasText)
+ .orElseGet(PoolFactoryBean.this::getBeanName);
}
@Override
@@ -524,6 +595,31 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
this.pingInterval = pingInterval;
}
+ /**
+ * Null-safe operation to set an array of {@link PoolConfigurer PoolConfigurers} used to apply
+ * additional configuration to this {@link PoolFactoryBean} when using Annotation-based configuration.
+ *
+ * @param poolConfigurers array of {@link PoolConfigurer PoolConfigurers} used to apply
+ * additional configuration to this {@link PoolFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ * @see #setPoolConfigurers(List)
+ */
+ public void setPoolConfigurers(PoolConfigurer... poolConfigurers) {
+ setPoolConfigurers(Arrays.asList(nullSafeArray(poolConfigurers, PoolConfigurer.class)));
+ }
+
+ /**
+ * Null-safe operation to set an {@link Iterable} of {@link PoolConfigurer PoolConfigurers} used to apply
+ * additional configuration to this {@link PoolFactoryBean} when using Annotation-based configuration.
+ *
+ * @param poolConfigurers {@link Iterable} of {@link PoolConfigurer PoolConfigurers} used to apply
+ * additional configuration to this {@link PoolFactoryBean}.
+ * @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
+ */
+ public void setPoolConfigurers(List poolConfigurers) {
+ this.poolConfigurers = Optional.ofNullable(poolConfigurers).orElseGet(Collections::emptyList);
+ }
+
/* (non-Javadoc) */
public void setPrSingleHopEnabled(boolean prSingleHopEnabled) {
this.prSingleHopEnabled = prSingleHopEnabled;
@@ -595,11 +691,17 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis
this.threadLocalConnections = threadLocalConnections;
}
- /* (non-Javadoc; internal framework use only) */
+ /*
+ * (non-Javadoc)
+ * internal framework use only
+ */
public final void setLocatorsConfiguration(Object locatorsConfiguration) {
}
- /* (non-Javadoc; internal framework use only) */
+ /*
+ * (non-Javadoc)
+ * internal framework use only
+ */
public final void setServersConfiguration(Object serversConfiguration) {
}
}
diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java
index 04d8a881..2498c20d 100644
--- a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java
+++ b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java
@@ -20,29 +20,26 @@ package org.springframework.data.gemfire.config.annotation;
import static org.springframework.data.gemfire.CacheFactoryBean.DynamicRegionSupport;
import static org.springframework.data.gemfire.CacheFactoryBean.JndiDataSource;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
-import static org.springframework.data.gemfire.util.SpringUtils.defaultIfNull;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.geode.cache.Cache;
import org.apache.geode.cache.TransactionListener;
import org.apache.geode.cache.TransactionWriter;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.util.GatewayConflictResolver;
import org.apache.geode.pdx.PdxSerializer;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
@@ -50,6 +47,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
+import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor;
import org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener;
import org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor;
@@ -57,33 +55,48 @@ import org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFact
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.data.gemfire.util.PropertiesBuilder;
-import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link AbstractCacheConfiguration} is an abstract base class for configuring either a Pivotal GemFire/Apache Geode
- * client or peer-based cache instance using Spring's Java-based, Annotation
- * {@link org.springframework.context.annotation.Configuration} support.
+ * client or peer-based cache instance using Spring's Java-based, Annotation {@link Configuration} support.
*
- * This class encapsulates configuration settings common to both GemFire peer
- * {@link org.apache.geode.cache.Cache caches} and
- * {@link org.apache.geode.cache.client.ClientCache client caches}.
+ * This class encapsulates configuration settings common to both Pivotal GemFire/Apache Geode
+ * {@link org.apache.geode.cache.Cache peer caches}
+ * and {@link org.apache.geode.cache.client.ClientCache client caches}.
*
* @author John Blum
- * @see org.springframework.beans.factory.BeanClassLoaderAware
+ * @see java.lang.annotation.Annotation
+ * @see java.util.Properties
+ * @see org.apache.geode.cache.Cache
+ * @see org.apache.geode.cache.GemFireCache
+ * @see org.apache.geode.cache.client.ClientCache
+ * @see org.apache.geode.cache.server.CacheServer
+ * @see org.apache.geode.pdx.PdxSerializer
* @see org.springframework.beans.factory.BeanFactory
- * @see org.springframework.beans.factory.BeanFactoryAware
+ * @see org.springframework.beans.factory.config.BeanDefinition
+ * @see org.springframework.beans.factory.support.BeanDefinitionBuilder
+ * @see org.springframework.beans.factory.support.BeanDefinitionRegistry
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
+ * @see org.springframework.core.convert.ConversionService
* @see org.springframework.core.io.Resource
* @see org.springframework.core.type.AnnotationMetadata
* @see org.springframework.data.gemfire.CacheFactoryBean
+ * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
+ * @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
+ * @see org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor
+ * @see org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener
+ * @see org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor
+ * @see org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor
+ * @see org.springframework.data.gemfire.mapping.GemfireMappingContext
+ * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
* @since 1.9.0
*/
@Configuration
@SuppressWarnings("unused")
-public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware, BeanFactoryAware, ImportAware {
+public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
private static final AtomicBoolean CUSTOM_EDITORS_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED = new AtomicBoolean(false);
@@ -104,14 +117,10 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
private boolean copyOnRead = DEFAULT_COPY_ON_READ;
private boolean useBeanFactoryLocator = DEFAULT_USE_BEAN_FACTORY_LOCATOR;
- private BeanFactory beanFactory;
-
private Boolean pdxIgnoreUnreadFields;
private Boolean pdxPersistent;
private Boolean pdxReadSerialized;
- private ClassLoader beanClassLoader;
-
private DynamicRegionSupport dynamicRegionSupport;
private Integer mcastPort = 0;
@@ -142,59 +151,30 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
private TransactionWriter transactionWriter;
/**
- * Determines whether the given {@link Object} has value. The {@link Object} is valuable
- * if it is not {@literal null}.
+ * Returns a {@link Properties} object containing Pivotal GemFire/Apache Geode properties used to configure
+ * the Pivotal GemFire/Apache Geode cache.
*
- * @param value {@link Object} to evaluate.
- * @return a boolean value indicating whether the given {@link Object} has value.
- */
- protected static boolean hasValue(Object value) {
- return (value != null);
- }
-
- /**
- * Determines whether the given {@link Number} has value. The {@link Number} is valuable
- * if it is not {@literal null} and is not equal to 0.0d.
+ * The {@literal name} of the Pivotal GemFire/Apache Geode member/node in the cluster is set to a default,
+ * pre-defined and descriptive value depending on the type of configuration meta-data applied.
*
- * @param value {@link Number} to evaluate.
- * @return a boolean value indicating whether the given {@link Number} has value.
- */
- protected static boolean hasValue(Number value) {
- return (value != null && value.doubleValue() != 0.0d);
- }
-
- /**
- * Determines whether the given {@link String} has value. The {@link String} is valuable
- * if it is not {@literal null} or empty.
+ * {@literal mcast-port} is set to {@literal 0} and {@literal locators} is set to an {@link String empty String},
+ * which is necessary for {@link ClientCache cache client}-based applications. These values can be changed
+ * and set accoridingly for {@link Cache peer cache} and {@link CacheServer cache server} applications.
*
- * @param value {@link String} to evaluate.
- * @return a boolean value indicating whether the given {@link String} is valuable.
- */
- protected static boolean hasValue(String value) {
- return StringUtils.hasText(value);
- }
-
- /**
- * Returns a {@link Properties} object containing GemFire System properties used to configure the GemFire cache.
+ * Finally, the {@literal log-level} property defaults to {@literal config}.
*
- * The name of the GemFire member/node in the cluster is set to a default, pre-defined, descriptive value
- * depending on the type of configuration meta-data applied.
- *
- * Both 'mcast-port' and 'locators' are to set 0 and empty String respectively, which is necessary
- * for {@link org.apache.geode.cache.client.ClientCache cache client}-based applications. These values
- * can be changed for peer cache and cache server applications.
- *
- * Finally, GemFire's {@literal log-level} System property defaults to {@literal config}.
- *
- * @return a {@link Properties} object containing GemFire System properties used to configure the GemFire cache.
+ * @return a {@link Properties} object containing Pivotal GemFire/Apache Geode properties used to configure
+ * the Pivotal GemFire/Apache Geode cache instance.
* @see GemFire Properties
* @see java.util.Properties
- * @see #name()
- * @see #logLevel()
* @see #locators()
+ * @see #logLevel()
+ * @see #mcastPort()
+ * @see #name()
*/
@Bean
protected Properties gemfireProperties() {
+
PropertiesBuilder gemfireProperties = PropertiesBuilder.create();
gemfireProperties.setProperty("name", name());
@@ -202,100 +182,87 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("locators", locators());
gemfireProperties.setProperty("start-locator", startLocator());
- gemfireProperties.add(customGemFireProperties);
+ gemfireProperties.add(this.customGemFireProperties);
return gemfireProperties.build();
}
- /**
- * {@inheritDoc}
- */
- @Override
- public void setBeanClassLoader(ClassLoader beanClassLoader) {
- this.beanClassLoader = beanClassLoader;
- }
-
- /**
- * Returns a reference to the {@link ClassLoader} use by the Spring {@link BeanFactory} to load classes
- * for bean definitions.
- *
- * @return the {@link ClassLoader} used by the Spring {@link BeanFactory} to load classes for bean definitions.
- * @see #setBeanClassLoader(ClassLoader)
- */
- protected ClassLoader beanClassLoader() {
- return beanClassLoader;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.beanFactory = beanFactory;
- }
-
- /**
- * Returns a reference to the Spring {@link BeanFactory} in the current application context.
- *
- * @return a reference to the Spring {@link BeanFactory}.
- * @throws IllegalStateException if the Spring {@link BeanFactory} was not properly initialized.
- * @see org.springframework.beans.factory.BeanFactory
- */
- protected BeanFactory beanFactory() {
- Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized");
- return this.beanFactory;
- }
-
/**
* {@inheritDoc}
*/
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
+
configureInfrastructure(importMetadata);
configureCache(importMetadata);
configurePdx(importMetadata);
- configureOther(importMetadata);
+ configureTheRest(importMetadata);
}
/**
- * Configures Spring container infrastructure components used by Spring Data GemFire
- * to enable GemFire to function properly inside a Spring context.
+ * Configures Spring container infrastructure components and beans used by Spring Data GemFire
+ * to enable Pivotal GemFire or Apache Geode to function properly inside a Spring context.
*
* @param importMetadata {@link AnnotationMetadata} containing annotation meta-data
- * for the Spring GemFire cache application class.
+ * for the Spring Data GemFire cache application class.
* @see org.springframework.core.type.AnnotationMetadata
*/
protected void configureInfrastructure(AnnotationMetadata importMetadata) {
+
registerCustomEditorBeanFactoryPostProcessor(importMetadata);
registerDefinedIndexesApplicationListener(importMetadata);
registerDiskStoreDirectoryBeanPostProcessor(importMetadata);
}
+ /* (non-Javadoc) */
+ private void registerCustomEditorBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
+
+ if (CUSTOM_EDITORS_REGISTERED.compareAndSet(false, true)) {
+ register(BeanDefinitionBuilder.rootBeanDefinition(CustomEditorBeanFactoryPostProcessor.class)
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
+ }
+ }
+
+ /* (non-Javadoc) */
+ private void registerDefinedIndexesApplicationListener(AnnotationMetadata importMetadata) {
+
+ if (DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED.compareAndSet(false, true)) {
+ register(BeanDefinitionBuilder.rootBeanDefinition(DefinedIndexesApplicationListener.class)
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
+ }
+ }
+
+ /* (non-Javadoc) */
+ private void registerDiskStoreDirectoryBeanPostProcessor(AnnotationMetadata importMetadata) {
+
+ if (DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
+ register(BeanDefinitionBuilder.rootBeanDefinition(DiskStoreDirectoryBeanPostProcessor.class)
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
+ }
+ }
+
/**
- * Configures the GemFire cache settings.
+ * Configures Pivotal GemFire/Apache Geode cache specific settings.
*
- * @param importMetadata {@link AnnotationMetadata} containing the cache meta-data used to configure
- * the GemFire cache.
+ * @param importMetadata {@link AnnotationMetadata} containing the cache meta-data used to configure the cache.
* @see org.springframework.core.type.AnnotationMetadata
*/
protected void configureCache(AnnotationMetadata importMetadata) {
+
if (isClientPeerOrServerCacheApplication(importMetadata)) {
+
Map cacheMetadataAttributes =
importMetadata.getAnnotationAttributes(getAnnotationTypeName());
setCopyOnRead(Boolean.TRUE.equals(cacheMetadataAttributes.get("copyOnRead")));
- Float criticalHeapPercentage = (Float) cacheMetadataAttributes.get("criticalHeapPercentage");
+ Optional.ofNullable((Float) cacheMetadataAttributes.get("criticalHeapPercentage"))
+ .filter(AbstractAnnotationConfigSupport::hasValue)
+ .ifPresent(this::setCriticalHeapPercentage);
- if (hasValue(criticalHeapPercentage)) {
- setCriticalHeapPercentage(criticalHeapPercentage);
- }
-
- Float evictionHeapPercentage = (Float) cacheMetadataAttributes.get("evictionHeapPercentage");
-
- if (hasValue(evictionHeapPercentage)) {
- setEvictionHeapPercentage(evictionHeapPercentage);
- }
+ Optional.ofNullable((Float) cacheMetadataAttributes.get("evictionHeapPercentage"))
+ .filter(AbstractAnnotationConfigSupport::hasValue)
+ .ifPresent(this::setEvictionHeapPercentage);
setLogLevel((String) cacheMetadataAttributes.get("logLevel"));
setName((String) cacheMetadataAttributes.get("name"));
@@ -304,17 +271,19 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
}
/**
- * Configures GemFire's PDX Serialization components.
+ * Configures Pivotal GemFire/Apache Geode cache PDX Serialization.
*
- * @param importMetadata {@link AnnotationMetadata} containing PDX meta-data used to configure
- * the GemFire cache with PDX de/serialization capabilities.
+ * @param importMetadata {@link AnnotationMetadata} containing PDX meta-data used to configure the cache
+ * with PDX de/serialization capabilities.
* @see org.springframework.core.type.AnnotationMetadata
* @see GemFire PDX Serialization
*/
protected void configurePdx(AnnotationMetadata importMetadata) {
+
String enablePdxTypeName = EnablePdx.class.getName();
if (importMetadata.hasAnnotation(enablePdxTypeName)) {
+
Map enablePdxAttributes = importMetadata.getAnnotationAttributes(enablePdxTypeName);
setPdxDiskStoreName((String) enablePdxAttributes.get("diskStoreName"));
@@ -328,97 +297,158 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
}
/**
- * Callback method to configure other, specific GemFire cache configuration settings.
+ * Resolves the {@link PdxSerializer} used to configure the cache for PDX De/Serialization.
*
- * @param importMetadata {@link AnnotationMetadata} containing meta-data used to configure
- * the GemFire cache.
- * @see org.springframework.core.type.AnnotationMetadata
- */
- protected void configureOther(AnnotationMetadata importMetadata) {
- }
-
- /* (non-Javadoc) */
- protected PdxSerializer resolvePdxSerializer(String pdxSerializerBeanName) {
- BeanFactory beanFactory = beanFactory();
- PdxSerializer pdxSerializer = pdxSerializer();
-
- return (beanFactory.containsBean(pdxSerializerBeanName)
- ? beanFactory.getBean(pdxSerializerBeanName, PdxSerializer.class)
- : (pdxSerializer != null ? pdxSerializer : newPdxSerializer()));
- }
-
- /* (non-Javadoc) */
- @SuppressWarnings("unchecked")
- protected T newPdxSerializer() {
- BeanFactory beanFactory = beanFactory();
-
- ConversionService conversionService = (beanFactory instanceof ConfigurableBeanFactory
- ? ((ConfigurableBeanFactory) beanFactory).getConversionService() : null);
-
- return (T) MappingPdxSerializer.create(this.mappingContext, conversionService);
- }
-
- /* (non-Javadoc) */
- protected void registerCustomEditorBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
- if (CUSTOM_EDITORS_REGISTERED.compareAndSet(false, true)) {
- register(BeanDefinitionBuilder.rootBeanDefinition(CustomEditorBeanFactoryPostProcessor.class)
- .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
- }
- }
-
- /* (non-Javadoc) */
- protected void registerDefinedIndexesApplicationListener(AnnotationMetadata importMetadata) {
- if (DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED.compareAndSet(false, true)) {
- register(BeanDefinitionBuilder.rootBeanDefinition(DefinedIndexesApplicationListener.class)
- .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
- }
- }
-
- /* (non-Javadoc) */
- protected void registerDiskStoreDirectoryBeanPostProcessor(AnnotationMetadata importMetadata) {
- if (DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
- register(BeanDefinitionBuilder.rootBeanDefinition(DiskStoreDirectoryBeanPostProcessor.class)
- .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
- }
- }
-
- /* (non-Javadoc) */
- protected void registerPdxDiskStoreAwareBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
- if (StringUtils.hasText(pdxDiskStoreName())) {
- if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
- register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class)
- .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
- .addConstructorArgValue(pdxDiskStoreName())
- .getBeanDefinition());
- }
- }
- }
-
- /**
- * Registers the given {@link BeanDefinition} with the {@link BeanDefinitionRegistry} using a generated bean name.
- *
- * @param beanDefinition {@link AbstractBeanDefinition} to register.
- * @return the given {@link BeanDefinition}.
- * @see org.springframework.beans.factory.support.AbstractBeanDefinition
- * @see org.springframework.beans.factory.support.BeanDefinitionRegistry
- * @see org.springframework.beans.factory.support.BeanDefinitionReaderUtils
- * #registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)
+ * @param pdxSerializerBeanName {@link String} containing the name of a Spring bean
+ * implementing the {@link PdxSerializer} interface.
+ * @return the resolved {@link PdxSerializer} from configuration.
+ * @see org.apache.geode.pdx.PdxSerializer
+ * @see #newPdxSerializer(BeanFactory)
* @see #beanFactory()
*/
- protected AbstractBeanDefinition register(AbstractBeanDefinition beanDefinition) {
- if (beanFactory() instanceof BeanDefinitionRegistry) {
- BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition,
- ((BeanDefinitionRegistry) beanFactory()));
- }
+ protected PdxSerializer resolvePdxSerializer(String pdxSerializerBeanName) {
- return beanDefinition;
+ BeanFactory beanFactory = beanFactory();
+
+ return Optional.ofNullable(pdxSerializerBeanName)
+ .filter(beanFactory::containsBean)
+ .map(beanName -> beanFactory.getBean(beanName, PdxSerializer.class))
+ .orElseGet(() -> Optional.ofNullable(pdxSerializer()).orElseGet(() -> newPdxSerializer(beanFactory)));
}
/**
- * Returns the GemFire cache application {@link java.lang.annotation.Annotation} type pertaining to
- * this configuration.
+ * Constructs a new instance of {@link PdxSerializer}.
*
- * @return the GemFire cache application {@link java.lang.annotation.Annotation} type used by this application.
+ * @param {@link Class} type of the {@link PdxSerializer}.
+ * @return a new instance of {@link PdxSerializer}.
+ * @see org.apache.geode.pdx.PdxSerializer
+ * @see #newPdxSerializer(BeanFactory)
+ */
+ @SuppressWarnings("unchecked")
+ protected T newPdxSerializer() {
+ return newPdxSerializer(beanFactory());
+ }
+
+ /**
+ * Constructs a new instance of {@link MappingPdxSerializer}.
+ *
+ * @param {@link Class} type of the {@link PdxSerializer}; this method returns a {@link MappingPdxSerializer}.
+ * @param beanFactory {@link BeanFactory} used to get an instance of {@link ConversionService}
+ * used by the {@link MappingPdxSerializer}.
+ * @return a new instance of {@link MappingPdxSerializer}.
+ * @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
+ * @see org.springframework.beans.factory.BeanFactory
+ * @see org.apache.geode.pdx.PdxSerializer
+ */
+ @SuppressWarnings("unchecked")
+ protected T newPdxSerializer(BeanFactory beanFactory) {
+
+ Optional conversionService = Optional.ofNullable(beanFactory)
+ .filter(it -> it instanceof ConfigurableBeanFactory)
+ .map(it -> ((ConfigurableBeanFactory) it).getConversionService());
+
+ return (T) MappingPdxSerializer.create(this.mappingContext, conversionService.orElse(null));
+ }
+
+ /* (non-Javadoc) */
+ private void registerPdxDiskStoreAwareBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
+
+ Optional.ofNullable(pdxDiskStoreName())
+ .filter(StringUtils::hasText)
+ .ifPresent(pdxDiskStoreName -> {
+ if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
+ register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class)
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
+ .addConstructorArgValue(pdxDiskStoreName)
+ .getBeanDefinition());
+ }
+ });
+ }
+
+ /**
+ * Callback method allowing developers to configure other cache or application specific configuration settings.
+ *
+ * @param importMetadata {@link AnnotationMetadata} containing meta-data used to configure the cache or application.
+ * @see org.springframework.core.type.AnnotationMetadata
+ */
+ protected void configureTheRest(AnnotationMetadata importMetadata) {
+ }
+
+ /**
+ * Constructs a new, initialized instance of {@link CacheFactoryBean} based on the Spring application's
+ * cache type preference (i.e. client or peer), which is expressed via the appropriate annotation.
+ *
+ * Use the {@link ClientCacheApplication} Annotation to construct a {@link ClientCache cache client} application.
+ *
+ * Use the {@link PeerCacheApplication} Annotation to construct a {@link Cache peer cache} application.
+ *
+ * @param