diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index cf8a77e8..bf78725e 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -23,6 +23,23 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import com.gemstone.gemfire.GemFireCheckedException; +import com.gemstone.gemfire.GemFireException; +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheClosedException; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.DynamicRegionFactory; +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.TransactionListener; +import com.gemstone.gemfire.cache.TransactionWriter; +import com.gemstone.gemfire.cache.util.GatewayConflictResolver; +import com.gemstone.gemfire.distributed.DistributedMember; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.internal.datasource.ConfigProperty; +import com.gemstone.gemfire.internal.jndi.JNDIInvoker; +import com.gemstone.gemfire.pdx.PdxSerializable; +import com.gemstone.gemfire.pdx.PdxSerializer; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; @@ -41,23 +58,6 @@ import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; -import com.gemstone.gemfire.GemFireCheckedException; -import com.gemstone.gemfire.GemFireException; -import com.gemstone.gemfire.cache.Cache; -import com.gemstone.gemfire.cache.CacheClosedException; -import com.gemstone.gemfire.cache.CacheFactory; -import com.gemstone.gemfire.cache.DynamicRegionFactory; -import com.gemstone.gemfire.cache.GemFireCache; -import com.gemstone.gemfire.cache.TransactionListener; -import com.gemstone.gemfire.cache.TransactionWriter; -import com.gemstone.gemfire.cache.util.GatewayConflictResolver; -import com.gemstone.gemfire.distributed.DistributedMember; -import com.gemstone.gemfire.distributed.DistributedSystem; -import com.gemstone.gemfire.internal.datasource.ConfigProperty; -import com.gemstone.gemfire.internal.jndi.JNDIInvoker; -import com.gemstone.gemfire.pdx.PdxSerializable; -import com.gemstone.gemfire.pdx.PdxSerializer; - /** * 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. @@ -67,7 +67,7 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * {@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. - * + * * @author Costin Leau * @author David Turanski * @author John Blum @@ -645,7 +645,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, /** * Sets the cache properties. - * + * * @param properties the properties to set */ public void setProperties(Properties properties) { @@ -1026,40 +1026,40 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, private String diskDirectory; private String poolName; - public String getDiskDir() { - return diskDirectory; - } - public void setDiskDir(String diskDirectory) { this.diskDirectory = diskDirectory; } - public Boolean getPersistent() { - return persistent; + public String getDiskDir() { + return diskDirectory; } public void setPersistent(Boolean persistent) { this.persistent = persistent; } - public String getPoolName() { - return poolName; + public Boolean getPersistent() { + return persistent; } public void setPoolName(String poolName) { this.poolName = poolName; } - public Boolean getRegisterInterest() { - return registerInterest; + public String getPoolName() { + return poolName; } public void setRegisterInterest(Boolean registerInterest) { this.registerInterest = registerInterest; } + public Boolean getRegisterInterest() { + return registerInterest; + } + public void initializeDynamicRegionFactory() { - File localDiskDirectory = (this.diskDirectory == null ? null : new File(this.diskDirectory)); + File localDiskDirectory = (this.diskDirectory != null ? new File(this.diskDirectory) : null); DynamicRegionFactory.Config config = new DynamicRegionFactory.Config(localDiskDirectory, poolName, persistent, registerInterest); 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 142de7cf..971bd5e7 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -20,6 +20,15 @@ import java.net.InetSocketAddress; import java.util.Map; import java.util.Properties; +import com.gemstone.gemfire.cache.CacheClosedException; +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.Pool; +import com.gemstone.gemfire.cache.client.PoolManager; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.pdx.PdxSerializer; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; @@ -36,18 +45,9 @@ import org.springframework.data.gemfire.util.SpringUtils; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; -import com.gemstone.gemfire.cache.CacheClosedException; -import com.gemstone.gemfire.cache.GemFireCache; -import com.gemstone.gemfire.cache.client.ClientCache; -import com.gemstone.gemfire.cache.client.ClientCacheFactory; -import com.gemstone.gemfire.cache.client.Pool; -import com.gemstone.gemfire.cache.client.PoolManager; -import com.gemstone.gemfire.distributed.DistributedSystem; -import com.gemstone.gemfire.pdx.PdxSerializer; - /** * FactoryBean dedicated to creating GemFire client caches. - * + * * @author Costin Leau * @author Lyndon Adams * @author John Blum @@ -98,7 +98,6 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat private String durableClientId; private String poolName; private String serverGroup; - private String serverName; @Override protected void postProcessBeforeCacheInitialization(Properties gemfireProperties) { @@ -782,5 +781,4 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat public final Boolean getUseClusterConfiguration() { return Boolean.FALSE; } - } 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 new file mode 100644 index 00000000..75ef85ea --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java @@ -0,0 +1,662 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import static org.springframework.data.gemfire.CacheFactoryBean.DynamicRegionSupport; +import static org.springframework.data.gemfire.CacheFactoryBean.JndiDataSource; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import com.gemstone.gemfire.cache.TransactionListener; +import com.gemstone.gemfire.cache.TransactionWriter; +import com.gemstone.gemfire.cache.util.GatewayConflictResolver; +import com.gemstone.gemfire.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.context.annotation.Bean; +import org.springframework.context.annotation.ImportAware; +import org.springframework.core.io.Resource; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * 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 + * {@link org.springframework.context.annotation.Configuration} support. + * + * This class contain configuration settings common to both GemFire peer {@link com.gemstone.gemfire.cache.Cache caches} + * and {@link com.gemstone.gemfire.cache.client.ClientCache client caches}. + * + * @author John Blum + * @see org.springframework.beans.factory.BeanClassLoaderAware + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.BeanFactoryAware + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.ImportAware + * @since 1.9.0 + */ +@SuppressWarnings("unused") +public abstract class AbstractCacheConfiguration implements BeanFactoryAware, BeanClassLoaderAware, ImportAware { + + protected static final boolean DEFAULT_CLOSE = true; + protected static final boolean DEFAULT_COPY_ON_READ = false; + protected static final boolean DEFAULT_USE_BEAN_FACTORY_LOCATOR = false; + + protected static final String DEFAULT_LOG_LEVEL = "config"; + protected static final String DEFAULT_NAME = "SpringDataGemFireApplication"; + + private boolean close = DEFAULT_CLOSE; + 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 Float criticalHeapPercentage; + private Float evictionHeapPercentage; + + private GatewayConflictResolver gatewayConflictResolver; + + private List jndiDataSources; + private List transactionListeners; + + private PdxSerializer pdxSerializer; + + private Resource cacheXml; + + private String locators = ""; + private String logLevel = DEFAULT_LOG_LEVEL; + private String name; + private String pdxDiskStoreName; + + private TransactionWriter transactionWriter; + + /** + * Returns the given {@link List} if not null or an empty {@link List}. + * + * @param Class type of the elements in the {@link List}. + * @param list {@link List} on which to perform the null check. + * @return the given {@link List} if not null or an empty {@link List}. + * @see java.util.Collections#emptyList() + * @see java.util.List + */ + protected static List nullSafeList(List list) { + return (list != null ? list : Collections.emptyList()); + } + + /** + * Returns the given {@link Set} if not null or an empty {@link Set}. + * + * @param Class type of the elements in the {@link Set}. + * @param set {@link Set} on which to perform the null check. + * @return the given {@link Set} if not null or an empty {@link Set}. + * @see java.util.Collections#emptySet() + * @see java.util.Set + */ + protected static Set nullSafeSet(Set set) { + return (set != null ? set : Collections.emptySet()); + } + + /** + * Returns the given {@code value} if not null or the {@code defaultValue}. + * + * @param Class type of the given {@code value} and {@code defaultValue}. + * @param value value on which to perform the null check. + * @param defaultValue value to return by default if the {@code value} is null. + * @return the given {@code value} if not null or the {@code defaultValue}. + */ + protected static T nullSafeValue(T value, T defaultValue) { + return (value != null ? value : defaultValue); + } + + /** + * Determines whether the give {@link Object} has value. The {@link Object} is valuable + * if it is not {@literal null}. + * + * @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. + * + * @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. + * + * @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. + * + * The name of the GemFire member/node is set to a pre-dined, descriptive default value depending + * on the type configuraiton applied. + * + * Both 'mcast-port' and 'locators' are to set 0 and empty String respectively, which is necessary + * for {@link com.gemstone.gemfire.cache.client.ClientCache cache client}-based applications. + * + * Finally GemFire's "log-level" System property defaults to "config". + * + * @return a {@link Properties} object containing GemFire System properties used to configure + * the GemFire cache. + * @see GemFire Properties + * @see java.util.Properties + * @see #name() + * @see #logLevel() + * @see #locators() + */ + @Bean + protected Properties gemfireProperties() { + Properties gemfireProperties = new Properties(); + gemfireProperties.setProperty("name", name()); + gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("log-level", logLevel()); + gemfireProperties.setProperty("locators", locators()); + return gemfireProperties; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.BeanClassLoaderAware + */ + @Override + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } + + /** + * Gets 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; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.BeanFactoryAware + */ + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + /** + * Gets 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; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.annotation.ImportAware + * @see org.springframework.core.type.AnnotationMetadata + */ + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + configureCache(importMetadata); + configurePdx(importMetadata); + configureOther(importMetadata); + } + + /** + * Configures the GemFire cache settings. + * + * @param importMetadata {@link AnnotationMetadata} containing the cache meta-data used to configure + * the GemFire 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"); + + if (hasValue(criticalHeapPercentage)) { + setCriticalHeapPercentage(criticalHeapPercentage); + } + + Float evictionHeapPercentage = (Float) cacheMetadataAttributes.get("evictionHeapPercentage"); + + if (hasValue(evictionHeapPercentage)) { + setEvictionHeapPercentage(evictionHeapPercentage); + } + + setLogLevel((String) cacheMetadataAttributes.get("logLevel")); + setName((String) cacheMetadataAttributes.get("name")); + } + } + + /** + * Configures GemFire's PDX Serialization feature. + * + * @param importMetadata {@link AnnotationMetadata} containing PDX meta-data used to configure + * the GemFire cache with PDX de/serialization capabilities. + * @see org.springframework.core.type.AnnotationMetadata + * @see GemFire PDX Serialization + */ + protected void configurePdx(AnnotationMetadata importMetadata) { + if (importMetadata.hasAnnotation(EnablePdx.class.getName())) { + Map enablePdxAttributes = importMetadata.getAnnotationAttributes(EnablePdx.class.getName()); + + String pdxSerializerBeanName = (String) enablePdxAttributes.get("serializerBeanName"); + + if (beanFactory().containsBean(pdxSerializerBeanName)) { + PdxSerializer pdxSerializer = beanFactory().getBean(pdxSerializerBeanName, PdxSerializer.class); + + setPdxDiskStoreName((String) enablePdxAttributes.get("diskStoreName")); + setPdxIgnoreUnreadFields(Boolean.TRUE.equals(enablePdxAttributes.get("ignoreUnreadFields"))); + setPdxPersistent(Boolean.TRUE.equals(enablePdxAttributes.get("persistent"))); + setPdxReadSerialized(Boolean.TRUE.equals(enablePdxAttributes.get("readSerialized"))); + setPdxSerializer(pdxSerializer); + } + } + } + + /** + * Callback method to configure other, specific GemFire cache configuration settings. + * + * @param importMetadata {@link AnnotationMetadata} containing the cache meta-data used to configure + * the GemFire cache. + * @see org.springframework.core.type.AnnotationMetadata + */ + protected void configureOther(AnnotationMetadata importMetadata) { + } + + /** + * Returns the GemFire cache application {@link java.lang.annotation.Annotation} type pertaining to + * this configuration. + * + * @return the GemFire cache application {@link java.lang.annotation.Annotation} type used by this application. + * @see org.springframework.data.gemfire.config.annotation.CacheServerApplication + * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication + * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication + */ + protected abstract Class getAnnotationType(); + + /** + * Returns the fully-qualified class name of the GemFire cache application {@link java.lang.annotation.Annotation} + * type. + * + * @return the fully-qualified class name of the GemFire cache application {@link java.lang.annotation.Annotation} + * type. + * @see java.lang.Class#getName() + * @see #getAnnotationType() + */ + protected String getAnnotationTypeName() { + return getAnnotationType().getName(); + } + + /** + * Returns the simple class name of the GemFire cache application {@link java.lang.annotation.Annotation} type. + * + * @return the simple class name of the GemFire cache application {@link java.lang.annotation.Annotation} type. + * @see java.lang.Class#getSimpleName() + * @see #getAnnotationType() + */ + protected String getAnnotationTypeSimpleName() { + return getAnnotationType().getSimpleName(); + } + + /** + * Determines whether this is a GemFire {@link com.gemstone.gemfire.cache.server.CacheServer} application, + * which is indicated by the presence of the {@link CacheServerApplication} annotation on a Spring application + * {@link org.springframework.context.annotation.Configuration @Configuration} class. + * + * @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data + * from the annotations used to configure the Spring application. + * @return a boolean value indicating whether this is a GemFire cache server application. + * @see org.springframework.data.gemfire.config.annotation.CacheServerApplication + * @see #getAnnotationTypeName() + * @see #getAnnotationType() + */ + protected boolean isCacheServerApplication(AnnotationMetadata importMetadata) { + return (CacheServerApplication.class.equals(getAnnotationType()) + && importMetadata.hasAnnotation(getAnnotationTypeName())); + } + + /** + * Determines whether this is a GemFire {@link com.gemstone.gemfire.cache.client.ClientCache} application, + * which is indicated by the presence of the {@link ClientCacheApplication} annotation on a Spring application + * {@link org.springframework.context.annotation.Configuration @Configuration} class. + * + * @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data + * from the annotations used to configure the Spring application. + * @return a boolean value indicating whether this is a GemFire cache client application. + * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication + * @see #getAnnotationTypeName() + * @see #getAnnotationType() + */ + protected boolean isClientCacheApplication(AnnotationMetadata importMetadata) { + return (ClientCacheApplication.class.equals(getAnnotationType()) + && importMetadata.hasAnnotation(getAnnotationTypeName())); + } + + /** + * Determines whether this is a GemFire peer {@link com.gemstone.gemfire.cache.Cache} application, + * which is indicated by the presence of the {@link PeerCacheApplication} annotation on a Spring application + * {@link org.springframework.context.annotation.Configuration @Configuration} class. + * + * @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data + * from the annotations used to configure the Spring application. + * @return a boolean value indicating whether this is a GemFire peer cache application. + * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication + * @see #getAnnotationTypeName() + * @see #getAnnotationType() + */ + protected boolean isPeerCacheApplication(AnnotationMetadata importMetadata) { + return (PeerCacheApplication.class.equals(getAnnotationType()) + && importMetadata.hasAnnotation(getAnnotationTypeName())); + } + + /** + * Determine whether this Spring application is a {@link com.gemstone.gemfire.cache.server.CacheServer}, + * {@link com.gemstone.gemfire.cache.client.ClientCache} or a {@link com.gemstone.gemfire.cache.Cache} application + * + * @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data + * from the annotations used to configure the Spring application. + * @return a boolean value indicating whether this is a GemFire cache server, client cache or peer cache, + * Spring application. + * @see #isCacheServerApplication(AnnotationMetadata) + * @see #isClientCacheApplication(AnnotationMetadata) + * @see #isPeerCacheApplication(AnnotationMetadata) + */ + protected boolean isClientPeerOrServerCacheApplication(AnnotationMetadata importMetadata) { + return (isCacheServerApplication(importMetadata) || isClientCacheApplication(importMetadata) + || isPeerCacheApplication(importMetadata)); + } + + /** + * Constructs a new, initialized instance of the {@link CacheFactoryBean} based on the application + * GemFire cache type (i.e. client or peer). + * + * @param Class type of the {@link CacheFactoryBean}. + * @return a new instance of the {@link CacheFactoryBean} for the particular application GemFire cache type + * (i.e client or peer). + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.CacheFactoryBean + * @see #setCommonCacheConfiguration(CacheFactoryBean) + * @see #newCacheFactoryBean() + */ + protected T constructCacheFactoryBean() { + return setCommonCacheConfiguration(this.newCacheFactoryBean()); + } + + /** + * Constructs a new, uninitialized instance of the {@link CacheFactoryBean} based on the application + * GemFire cache type (i.e. client or peer). + * + * @param Class type of the {@link CacheFactoryBean}. + * @return a new instance of the {@link CacheFactoryBean} for the particular application GemFire cache type + * (i.e client or peer). + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.CacheFactoryBean + */ + protected abstract T newCacheFactoryBean(); + + /** + * Configures common GemFire cache configuration settings. + * + * @param Class type of the {@link CacheFactoryBean}. + * @param gemfireCache specific {@link CacheFactoryBean} instance to configure. + * @return the given {@link CacheFactoryBean} after common configuration settings have been applied. + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.CacheFactoryBean + */ + protected T setCommonCacheConfiguration(T gemfireCache) { + gemfireCache.setBeanClassLoader(beanClassLoader()); + gemfireCache.setBeanFactory(beanFactory()); + gemfireCache.setCacheXml(cacheXml()); + gemfireCache.setClose(close()); + gemfireCache.setCopyOnRead(copyOnRead()); + gemfireCache.setCriticalHeapPercentage(criticalHeapPercentage()); + gemfireCache.setDynamicRegionSupport(dynamicRegionSupport()); + gemfireCache.setEvictionHeapPercentage(evictionHeapPercentage()); + gemfireCache.setGatewayConflictResolver(gatewayConflictResolver()); + gemfireCache.setJndiDataSources(jndiDataSources()); + gemfireCache.setProperties(gemfireProperties()); + gemfireCache.setPdxDiskStoreName(pdxDiskStoreName()); + gemfireCache.setPdxIgnoreUnreadFields(pdxIgnoreUnreadFields()); + gemfireCache.setPdxPersistent(pdxPersistent()); + gemfireCache.setPdxReadSerialized(pdxReadSerialized()); + gemfireCache.setPdxSerializer(pdxSerializer()); + gemfireCache.setTransactionListeners(transactionListeners()); + gemfireCache.setTransactionWriter(transactionWriter()); + + return gemfireCache; + } + + /* (non-Javadoc) */ + void setCacheXml(Resource cacheXml) { + this.cacheXml = cacheXml; + } + + protected Resource cacheXml() { + return this.cacheXml; + } + + /* (non-Javadoc) */ + void setClose(boolean close) { + this.close = close; + } + + protected boolean close() { + return this.close; + } + + /* (non-Javadoc) */ + void setCopyOnRead(boolean copyOnRead) { + this.copyOnRead = copyOnRead; + } + + protected boolean copyOnRead() { + return this.copyOnRead; + } + + /* (non-Javadoc) */ + void setCriticalHeapPercentage(Float criticalHeapPercentage) { + this.criticalHeapPercentage = criticalHeapPercentage; + } + + protected Float criticalHeapPercentage() { + return this.criticalHeapPercentage; + } + + /* (non-Javadoc) */ + void setDynamicRegionSupport(DynamicRegionSupport dynamicRegionSupport) { + this.dynamicRegionSupport = dynamicRegionSupport; + } + + protected DynamicRegionSupport dynamicRegionSupport() { + return this.dynamicRegionSupport; + } + + /* (non-Javadoc) */ + void setEvictionHeapPercentage(Float evictionHeapPercentage) { + this.evictionHeapPercentage = evictionHeapPercentage; + } + + protected Float evictionHeapPercentage() { + return this.evictionHeapPercentage; + } + + /* (non-Javadoc) */ + void setGatewayConflictResolver(GatewayConflictResolver gatewayConflictResolver) { + this.gatewayConflictResolver = gatewayConflictResolver; + } + + protected GatewayConflictResolver gatewayConflictResolver() { + return this.gatewayConflictResolver; + } + + /* (non-Javadoc) */ + void setJndiDataSources(List jndiDataSources) { + this.jndiDataSources = jndiDataSources; + } + + protected List jndiDataSources() { + return nullSafeList(this.jndiDataSources); + } + + void setLocators(String locators) { + this.locators = locators; + } + + protected String locators() { + return this.locators; + } + + /* (non-Javadoc) */ + void setLogLevel(String logLevel) { + this.logLevel = logLevel; + } + + protected String logLevel() { + return nullSafeValue(this.logLevel, DEFAULT_LOG_LEVEL); + } + + void setName(String name) { + this.name = name; + } + + protected String name() { + return (StringUtils.hasText(name) ? name : toString()); + } + + /* (non-Javadoc) */ + void setPdxDiskStoreName(String pdxDiskStoreName) { + this.pdxDiskStoreName = pdxDiskStoreName; + } + + protected String pdxDiskStoreName() { + return this.pdxDiskStoreName; + } + + /* (non-Javadoc) */ + void setPdxIgnoreUnreadFields(Boolean pdxIgnoreUnreadFields) { + this.pdxIgnoreUnreadFields = pdxIgnoreUnreadFields; + } + + protected Boolean pdxIgnoreUnreadFields() { + return this.pdxIgnoreUnreadFields; + } + + /* (non-Javadoc) */ + void setPdxPersistent(Boolean pdxPersistent) { + this.pdxPersistent = pdxPersistent; + } + + protected Boolean pdxPersistent() { + return this.pdxPersistent; + } + + /* (non-Javadoc) */ + void setPdxReadSerialized(Boolean pdxReadSerialized) { + this.pdxReadSerialized = pdxReadSerialized; + } + + protected Boolean pdxReadSerialized() { + return this.pdxReadSerialized; + } + + /* (non-Javadoc) */ + void setPdxSerializer(PdxSerializer pdxSerializer) { + this.pdxSerializer = pdxSerializer; + } + + protected PdxSerializer pdxSerializer() { + return this.pdxSerializer; + } + + /* (non-Javadoc) */ + void setTransactionListeners(List transactionListeners) { + this.transactionListeners = transactionListeners; + } + + protected List transactionListeners() { + return nullSafeList(this.transactionListeners); + } + + /* (non-Javadoc) */ + void setTransactionWriter(TransactionWriter transactionWriter) { + this.transactionWriter = transactionWriter; + } + + protected TransactionWriter transactionWriter() { + return this.transactionWriter; + } + + /* (non-Javadoc) */ + void setUseBeanFactoryLocator(boolean useBeanFactoryLocator) { + this.useBeanFactoryLocator = useBeanFactoryLocator; + } + + protected boolean useBeanFactoryLocator() { + return this.useBeanFactoryLocator; + } + + @Override + public String toString() { + return DEFAULT_NAME; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java new file mode 100644 index 00000000..ccd532a1 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java @@ -0,0 +1,245 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.gemstone.gemfire.cache.control.ResourceManager; +import com.gemstone.gemfire.cache.server.CacheServer; +import com.gemstone.gemfire.cache.server.ClientSubscriptionConfig; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; + +/** + * The CacheServerApplication annotation enables an embedded GemFire + * {@link com.gemstone.gemfire.cache.server.CacheServer} instance in a Spring Data GemFire based application. + * + * In addition, this also implies an embedded GemFire peer {@link com.gemstone.gemfire.cache.Cache} must exist + * and therefore will be configured, constructed and initialized as a Spring bean in the application context. + * + * @author John Blum + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.Import + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfiguration + * @see com.gemstone.gemfire.cache.control.ResourceManager + * @see com.gemstone.gemfire.cache.server.CacheServer + * @see com.gemstone.gemfire.cache.server.ClientSubscriptionConfig + * @since 1.9.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Configuration +@Import(CacheServerConfiguration.class) +@SuppressWarnings("unused") +public @interface CacheServerApplication { + + /** + * Configures whether the {@link CacheServer} should start automatically at runtime. + * + * Default is {@code true). + */ + boolean autoStartup() default true; + + /** + * Configures the ip address or host name that this cache server will listen on. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_BIND_ADDRESS + */ + String bindAddress() default "localhost"; + + /** + * Indicates whether the "copy on read" is enabled for this cache. + * + * Default is {@code false}. + */ + boolean copyOnRead() default false; + + /** + * Configures the percentage of heap at or above which the cache is considered in danger of becoming inoperable. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_CRITICAL_HEAP_PERCENTAGE + */ + float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_HEAP_PERCENTAGE; + + /** + * By default, a GemFire member (both locators and servers) will attempt to reconnect and reinitialize the cache + * after it has been forced out of the distributed system by a network partition event or has otherwise been + * shunned by other members. Use this property to enable the auto-reconnect behavior. + * + * Default is {@code false}. + */ + boolean enableAutoReconnect() default false; + + /** + * Configures the percentage of heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_EVICTION_HEAP_PERCENTAGE + */ + float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_HEAP_PERCENTAGE; + + /** + * Configures the ip address or host name that server locators will tell clients that this cache server + * is listening on. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_HOSTNAME_FOR_CLIENTS + */ + String hostnameForClients() default CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS; + + /** + * Configures the frequency in milliseconds to poll the load probe on this cache server. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_LOAD_POLL_INTERVAL + */ + long loadPollInterval() default CacheServer.DEFAULT_LOAD_POLL_INTERVAL; + + /** + * Configures the list of GemFire Locators defining the cluster to which this GemFire cache data node + * should connect. + */ + String locators() default ""; + + /** + * Configures the length, in seconds, of distributed lock leases obtained by this cache. + * + * Default is {@literal 120} seconds. + */ + int lockLease() default 120; + + /** + * Configures the number of seconds a cache operation will wait to obtain a distributed lock lease. + * + * Default is {@literal 60} seconds + */ + int lockTimeout() default 60; + + /** + * Configures the log level used to output log messages at GemFire cache runtime. + * + * Default is {@literal config}. + */ + String logLevel() default CacheServerConfiguration.DEFAULT_LOG_LEVEL; + + /** + * Configures the maximum allowed client connections. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_MAX_CONNECTIONS + */ + int maxConnections() default CacheServer.DEFAULT_MAX_CONNECTIONS; + + /** + * Configures he maximum number of messages that can be enqueued in a client-queue. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_MAXIMUM_MESSAGE_COUNT + */ + int maxMessageCount() default CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT; + + /** + * Configures the maximum number of threads allowed in this cache server to service client requests. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_MAX_THREADS + */ + int maxThreads() default CacheServer.DEFAULT_MAX_THREADS; + + /** + * Configures the maximum amount of time between client pings. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS + */ + int maxTimeBetweenPings() default CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS; + + /** + * Configures the frequency (in seconds) at which a message will be sent by the primary cache-server to all + * the secondary cache-server nodes to remove the events which have already been dispatched from the queue. + * + * Default is {@literal 1} second. + */ + int messageSyncInterval() default 1; + + /** + * Configures the time (in seconds ) after which a message in the client queue will expire. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_MESSAGE_TIME_TO_LIVE + */ + int messageTimeToLive() default CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE; + + /** + * Configures the name of this GemFire member in the cluster (distributed system). + * + * Default is {@literal SpringBasedCacheClientApplication}. + */ + String name() default CacheServerConfiguration.DEFAULT_NAME; + + /** + * Configures the port on which this cache server listens for clients. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_PORT + */ + int port() default CacheServer.DEFAULT_PORT; + + /** + * Configures the number of seconds a cache get operation can spend searching for a value before it times out. + * + * Default is {@literal 300} seconds. + */ + int searchTimeout() default 300; + + /** + * Configures the configured buffer size of the socket connection for this CacheServer. + * + * @see com.gemstone.gemfire.cache.server.CacheServer#DEFAULT_SOCKET_BUFFER_SIZE + */ + int socketBufferSize() default CacheServer.DEFAULT_SOCKET_BUFFER_SIZE; + + /** + * Configures the capacity of the client queue. + * + * @see com.gemstone.gemfire.cache.server.ClientSubscriptionConfig#DEFAULT_CAPACITY + */ + int subscriptionCapacity() default ClientSubscriptionConfig.DEFAULT_CAPACITY; + + /** + * Configures the disk store name for overflow. + */ + String subscriptionDiskStoreName() default ""; + + /** + * Configures the eviction policy that is executed when capacity of the client queue is reached. + * + * Defaults to {@link SubscriptionEvictionPolicy#NONE}. + */ + SubscriptionEvictionPolicy subscriptionEvictionPolicy() default SubscriptionEvictionPolicy.NONE; + + /** + * Configures whether this GemFire cache member node would pull it's configuration meta-data + * from the cluster-based Cluster Configuration service. + * + * Default is {@code false}. + */ + boolean useClusterConfiguration() default false; + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java new file mode 100644 index 00000000..cbefe88b --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java @@ -0,0 +1,289 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.Map; +import java.util.Set; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.InterestRegistrationListener; +import com.gemstone.gemfire.cache.server.CacheServer; +import com.gemstone.gemfire.cache.server.ClientSubscriptionConfig; +import com.gemstone.gemfire.cache.server.ServerLoadProbe; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.server.CacheServerFactoryBean; +import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; + +/** + * Spring {@link Configuration} class used to configure, construct and initialize and GemFire {@link CacheServer} + * instance in a Spring application context. + * + * @author John Blum + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfiguration + * @see com.gemstone.gemfire.cache.Cache + * @see com.gemstone.gemfire.cache.server.CacheServer + * @since 1.9.0 + */ +@Configuration +@SuppressWarnings("unused") +public class CacheServerConfiguration extends PeerCacheConfiguration { + + protected static final boolean DEFAULT_AUTO_STARTUP = true; + + protected static final String DEFAULT_NAME = "SpringBasedCacheServerApplication"; + + private boolean autoStartup = DEFAULT_AUTO_STARTUP; + + private Integer maxConnections; + private Integer maxMessageCount; + private Integer maxThreads; + private Integer maxTimeBetweenPings; + private Integer messageTimeToLive; + private Integer port; + private Integer socketBufferSize; + private Integer subscriptionCapacity; + + private Long loadPollInterval; + + private ServerLoadProbe serverLoadProbe; + + private Set interestRegistrationListeners; + + private String bindAddress; + private String hostnameForClients; + private String subscriptionDiskStoreName; + + private SubscriptionEvictionPolicy subscriptionEvictionPolicy; + + @Bean + public CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache) { + CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean(); + + gemfireCacheServer.setCache(gemfireCache); + gemfireCacheServer.setAutoStartup(autoStartup()); + gemfireCacheServer.setBindAddress(bindAddress()); + gemfireCacheServer.setHostNameForClients(hostnameForClients()); + gemfireCacheServer.setListeners(interestRegistrationListeners()); + gemfireCacheServer.setLoadPollInterval(loadPollInterval()); + gemfireCacheServer.setMaxConnections(maxConnections()); + gemfireCacheServer.setMaxMessageCount(maxMessageCount()); + gemfireCacheServer.setMaxThreads(maxThreads()); + gemfireCacheServer.setMaxTimeBetweenPings(maxTimeBetweenPings()); + gemfireCacheServer.setMessageTimeToLive(messageTimeToLive()); + gemfireCacheServer.setPort(port()); + gemfireCacheServer.setServerLoadProbe(serverLoadProbe()); + gemfireCacheServer.setSocketBufferSize(socketBufferSize()); + gemfireCacheServer.setSubscriptionCapacity(subscriptionCapacity()); + gemfireCacheServer.setSubscriptionDiskStore(subscriptionDiskStoreName()); + gemfireCacheServer.setSubscriptionEvictionPolicy(subscriptionEvictionPolicy()); + + return gemfireCacheServer; + } + + /** + * Configures GemFire {@link CacheServer} specific settings. + * + * @param importMetadata {@link AnnotationMetadata} containing cache server meta-data used to configure + * the GemFire {@link CacheServer}. + * @see org.springframework.core.type.AnnotationMetadata + */ + @Override + protected void configureOther(AnnotationMetadata importMetadata) { + super.configureCache(importMetadata); + + if (isCacheServerApplication(importMetadata)) { + Map cacheServerApplicationMetadata = + importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + + setAutoStartup(Boolean.TRUE.equals(cacheServerApplicationMetadata.get("autoStartup"))); + setBindAddress((String) cacheServerApplicationMetadata.get("bindAddress")); + setHostnameForClients((String) cacheServerApplicationMetadata.get("hostnameForClients")); + setLoadPollInterval((Long) cacheServerApplicationMetadata.get("loadPollInterval")); + setMaxConnections((Integer) cacheServerApplicationMetadata.get("maxConnections")); + setMaxMessageCount((Integer) cacheServerApplicationMetadata.get("maxMessageCount")); + setMaxThreads((Integer) cacheServerApplicationMetadata.get("maxThreads")); + setMaxTimeBetweenPings((Integer) cacheServerApplicationMetadata.get("maxTimeBetweenPings")); + setMessageTimeToLive((Integer) cacheServerApplicationMetadata.get("messageTimeToLive")); + setPort((Integer) cacheServerApplicationMetadata.get("port")); + setSocketBufferSize((Integer) cacheServerApplicationMetadata.get("socketBufferSize")); + setSubscriptionCapacity((Integer) cacheServerApplicationMetadata.get("subscriptionCapacity")); + setSubscriptionDiskStoreName((String) cacheServerApplicationMetadata.get("subscriptionDiskStoreName")); + setSubscriptionEvictionPolicy((SubscriptionEvictionPolicy) cacheServerApplicationMetadata.get( + "subscriptionEvictionPolicy")); + } + } + + @Override + protected Class getAnnotationType() { + return CacheServerApplication.class; + } + + /* (non-Javadoc) */ + void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + protected boolean autoStartup() { + return this.autoStartup; + } + + /* (non-Javadoc) */ + void setBindAddress(String bindAddress) { + this.bindAddress = bindAddress; + } + + protected String bindAddress() { + return nullSafeValue(this.bindAddress, CacheServer.DEFAULT_BIND_ADDRESS); + } + + /* (non-Javadoc) */ + void setHostnameForClients(String hostnameForClients) { + this.hostnameForClients = hostnameForClients; + } + + protected String hostnameForClients() { + return nullSafeValue(this.hostnameForClients, CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS); + } + + /* (non-Javadoc) */ + void setInterestRegistrationListeners(Set interestRegistrationListeners) { + this.interestRegistrationListeners = interestRegistrationListeners; + } + + protected Set interestRegistrationListeners() { + return nullSafeSet(this.interestRegistrationListeners); + } + + /* (non-Javadoc) */ + void setLoadPollInterval(Long loadPollInterval) { + this.loadPollInterval = loadPollInterval; + } + + protected Long loadPollInterval() { + return nullSafeValue(this.loadPollInterval, CacheServer.DEFAULT_LOAD_POLL_INTERVAL); + } + + /* (non-Javadoc) */ + void setMaxConnections(Integer maxConnections) { + this.maxConnections = maxConnections; + } + + protected Integer maxConnections() { + return nullSafeValue(this.maxConnections, CacheServer.DEFAULT_MAX_CONNECTIONS); + } + + /* (non-Javadoc) */ + void setMaxMessageCount(Integer maxMessageCount) { + this.maxMessageCount = maxMessageCount; + } + + protected Integer maxMessageCount() { + return nullSafeValue(this.maxMessageCount, CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT); + } + + /* (non-Javadoc) */ + void setMaxThreads(Integer maxThreads) { + this.maxThreads = maxThreads; + } + + protected Integer maxThreads() { + return nullSafeValue(this.maxThreads, CacheServer.DEFAULT_MAX_THREADS); + } + + /* (non-Javadoc) */ + void setMaxTimeBetweenPings(Integer maxTimeBetweenPings) { + this.maxTimeBetweenPings = maxTimeBetweenPings; + } + + protected Integer maxTimeBetweenPings() { + return nullSafeValue(this.maxTimeBetweenPings, CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS); + } + + /* (non-Javadoc) */ + void setMessageTimeToLive(Integer messageTimeToLive) { + this.messageTimeToLive = messageTimeToLive; + } + + protected Integer messageTimeToLive() { + return nullSafeValue(this.messageTimeToLive, CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE); + } + + /* (non-Javadoc) */ + void setPort(Integer port) { + this.port = port; + } + + protected Integer port() { + return nullSafeValue(this.port, CacheServer.DEFAULT_PORT); + } + + /* (non-Javadoc) */ + void setServerLoadProbe(ServerLoadProbe serverLoadProbe) { + this.serverLoadProbe = serverLoadProbe; + } + + protected ServerLoadProbe serverLoadProbe() { + return nullSafeValue(this.serverLoadProbe, CacheServer.DEFAULT_LOAD_PROBE); + } + + /* (non-Javadoc) */ + void setSocketBufferSize(Integer socketBufferSize) { + this.socketBufferSize = socketBufferSize; + } + + protected Integer socketBufferSize() { + return nullSafeValue(this.socketBufferSize, CacheServer.DEFAULT_SOCKET_BUFFER_SIZE); + } + + /* (non-Javadoc) */ + void setSubscriptionCapacity(Integer subscriptionCapacity) { + this.subscriptionCapacity = subscriptionCapacity; + } + + protected Integer subscriptionCapacity() { + return nullSafeValue(this.subscriptionCapacity, ClientSubscriptionConfig.DEFAULT_CAPACITY); + } + + /* (non-Javadoc) */ + void setSubscriptionDiskStoreName(String subscriptionDiskStoreName) { + this.subscriptionDiskStoreName = subscriptionDiskStoreName; + } + + protected String subscriptionDiskStoreName() { + return this.subscriptionDiskStoreName; + } + + /* (non-Javadoc) */ + void setSubscriptionEvictionPolicy(SubscriptionEvictionPolicy subscriptionEvictionPolicy) { + this.subscriptionEvictionPolicy = subscriptionEvictionPolicy; + } + + protected SubscriptionEvictionPolicy subscriptionEvictionPolicy() { + return nullSafeValue(this.subscriptionEvictionPolicy, SubscriptionEvictionPolicy.DEFAULT); + } + + @Override + public String toString() { + return DEFAULT_NAME; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java new file mode 100644 index 00000000..cedde0a1 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java @@ -0,0 +1,275 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.gemstone.gemfire.cache.client.PoolFactory; +import com.gemstone.gemfire.cache.control.ResourceManager; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.gemfire.GemfireUtils; + +/** + * The ClientCacheApplication annotation enables a Spring Data GemFire based application to become + * a GemFire cache client (i.e. {@link com.gemstone.gemfire.cache.client.ClientCache}). + * + * @author John Blum + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.Import + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfiguration + * @see com.gemstone.gemfire.cache.client.PoolFactory + * @see com.gemstone.gemfire.cache.control.ResourceManager + * @since 1.9.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Configuration +@Import(ClientCacheConfiguration.class) +@SuppressWarnings("unused") +public @interface ClientCacheApplication { + + /** + * Indicates whether the "copy on read" is enabled for this cache. + * + * Default is {@code false}. + */ + boolean copyOnRead() default false; + + /** + * Configures the percentage of heap at or above which the cache is considered in danger of becoming inoperable. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_CRITICAL_HEAP_PERCENTAGE + */ + float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_HEAP_PERCENTAGE; + + /** + * Used only for clients in a client/server installation. If set, this indicates that the client is durable + * and identifies the client. The ID is used by servers to reestablish any messaging that was interrupted + * by client downtime. + */ + String durableClientId() default ""; + + /** + * Used only for clients in a client/server installation. Number of seconds this client can remain disconnected + * from its server and have the server continue to accumulate durable events for it. + */ + int durableClientTimeout() default 300; + + /** + * Configures the percentage of heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_EVICTION_HEAP_PERCENTAGE + */ + float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_HEAP_PERCENTAGE; + + /** + * Configures the free connection timeout for this pool. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_FREE_CONNECTION_TIMEOUT + */ + int freeConnectionTimeout() default PoolFactory.DEFAULT_FREE_CONNECTION_TIMEOUT; + + /** + * Conifgures the amount of time a connection can be idle before expiring the connection. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_IDLE_TIMEOUT + */ + long idleTimeout() default PoolFactory.DEFAULT_IDLE_TIMEOUT; + + /** + * Configures whether to keep the client queues alive on the server when the client is disconnected + * + * Default is {@code false}. + */ + boolean keepAlive() default false; + + /** + * Configures the load conditioning interval for this pool. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_LOAD_CONDITIONING_INTERVAL + */ + int loadConditionInterval() default PoolFactory.DEFAULT_LOAD_CONDITIONING_INTERVAL; + + /** + * Configures the GemFire {@link com.gemstone.gemfire.distributed.Locator}s to which + * this cache client will connect. + */ + Locator[] locators() default {}; + + /** + * Configures the log level used to output log messages at GemFire cache runtime. + * + * Default is {@literal config}. + */ + String logLevel() default ClientCacheConfiguration.DEFAULT_LOG_LEVEL; + + /** + * Configures the max number of client to server connections that the pool will create. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_MAX_CONNECTIONS + */ + int maxConnections() default PoolFactory.DEFAULT_MAX_CONNECTIONS; + + /** + * Configures the minimum number of connections to keep available at all times. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_MIN_CONNECTIONS + */ + int minConnections() default PoolFactory.DEFAULT_MIN_CONNECTIONS; + + /** + * If set to true then the created pool can be used by multiple users. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_MULTIUSER_AUTHENTICATION + */ + boolean multiUserAuthentication() default PoolFactory.DEFAULT_MULTIUSER_AUTHENTICATION; + + /** + * Configures the name of this GemFire member in the cluster (distributed system). + * + * Default is {@literal SpringBasedCacheClientApplication}. + */ + String name() default ClientCacheConfiguration.DEFAULT_NAME; + + /** + * Configures how often to ping servers to verify that they are still alive. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_PING_INTERVAL + */ + long pingInterval() default PoolFactory.DEFAULT_PING_INTERVAL; + + /** + * By default {@code prSingleHopEnabled} is {@code true} in which case the client is aware of the location + * of partitions on servers hosting Regions with {@link com.gemstone.gemfire.cache.DataPolicy#PARTITION}. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_PR_SINGLE_HOP_ENABLED + */ + boolean prSingleHopEnabled() default PoolFactory.DEFAULT_PR_SINGLE_HOP_ENABLED; + + /** + * Configures the number of milliseconds to wait for a response from a server before timing out the operation + * and trying another server (if any are available). + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_READ_TIMEOUT + */ + int readTimeout() default PoolFactory.DEFAULT_READ_TIMEOUT; + + /** + * Notifies the server that this durable client is ready to receive updates. + * + * Default is {@code false}. + */ + boolean readyForEvents() default false; + + /** + * Configures the number of times to retry a request after timeout/exception. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_RETRY_ATTEMPTS + */ + int retryAttempts() default PoolFactory.DEFAULT_RETRY_ATTEMPTS; + + /** + * Configures the group that all servers this pool connects to must belong to. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SERVER_GROUP + */ + String serverGroup() default PoolFactory.DEFAULT_SERVER_GROUP; + + /** + * Configures the GemFire {@link com.gemstone.gemfire.cache.server.CacheServer}s to which + * this cache client will connect. + */ + Server[] servers() default {}; + + /** + * Configures the socket buffer size for each connection made in this pool. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SOCKET_BUFFER_SIZE + */ + int socketBufferSize() default PoolFactory.DEFAULT_SOCKET_BUFFER_SIZE; + + /** + * Configures how often to send client statistics to the server. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_STATISTIC_INTERVAL + */ + int statisticInterval() default PoolFactory.DEFAULT_STATISTIC_INTERVAL; + + /** + * Configures the interval in milliseconds to wait before sending acknowledgements to the cache server + * for events received from the server subscriptions. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SUBSCRIPTION_ACK_INTERVAL + */ + int subscriptionAckInterval() default PoolFactory.DEFAULT_SUBSCRIPTION_ACK_INTERVAL; + + /** + * If set to true then the created pool will have server-to-client subscriptions enabled. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SUBSCRIPTION_ENABLED + */ + boolean subscriptionEnabled() default PoolFactory.DEFAULT_SUBSCRIPTION_ENABLED; + + /** + * Configures the messageTrackingTimeout attribute which is the time-to-live period, in milliseconds, + * for subscription events the client has received from the server. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SUBSCRIPTION_MESSAGE_TRACKING_TIMEOUT + */ + int subscriptionMessageTrackingTimeout() default PoolFactory.DEFAULT_SUBSCRIPTION_MESSAGE_TRACKING_TIMEOUT; + + /** + * Configures the redundancy level for this pools server-to-client subscriptions. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_SUBSCRIPTION_REDUNDANCY + */ + int subscriptionRedundancy() default PoolFactory.DEFAULT_SUBSCRIPTION_REDUNDANCY; + + /** + * Configures the thread local connections policy for this pool. + * + * @see com.gemstone.gemfire.cache.client.PoolFactory#DEFAULT_THREAD_LOCAL_CONNECTIONS + */ + boolean threadLocalConnections() default PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS; + + @interface Locator { + + String host() default "localhost"; + + int port() default GemfireUtils.DEFAULT_LOCATOR_PORT; + + } + + @interface Server { + + String host() default "localhost"; + + int port() default GemfireUtils.DEFAULT_CACHE_SERVER_PORT; + + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java new file mode 100644 index 00000000..fdc5e5fb --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java @@ -0,0 +1,412 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; +import org.springframework.data.gemfire.support.ConnectionEndpoint; + +/** + * Spring {@link Configuration} class used to configure, construct and initialize + * a GemFire {@link com.gemstone.gemfire.cache.client.ClientCache} instance in a Spring application context. + * + * @author John Blum + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration + * @see com.gemstone.gemfire.cache.client.ClientCache + * @since 1.0.0 + */ +@Configuration +@SuppressWarnings("unused") +public class ClientCacheConfiguration extends AbstractCacheConfiguration { + + protected static final boolean DEFAULT_READY_FOR_EVENTS = false; + + protected static final String DEFAULT_NAME = "SpringBasedCacheClientApplication"; + + private boolean readyForEvents = DEFAULT_READY_FOR_EVENTS; + + private Boolean keepAlive; + private Boolean multiUserAuthentication; + private Boolean prSingleHopEnabled; + private Boolean subscriptionEnabled; + private Boolean threadLocalConnections; + + private Integer durableClientTimeout; + private Integer freeConnectionTimeout; + private Integer loadConditioningInterval; + private Integer maxConnections; + private Integer minConnections; + private Integer readTimeout; + private Integer retryAttempts; + private Integer socketBufferSize; + private Integer statisticsInterval; + private Integer subscriptionAckInterval; + private Integer subscriptionMessageTrackingTimeout; + private Integer subscriptionRedundancy; + + private Iterable locators; + private Iterable servers; + + private Long idleTimeout; + private Long pingInterval; + + private String durableClientId; + private String serverGroup; + + @Bean + public ClientCacheFactoryBean gemfireCache() { + ClientCacheFactoryBean gemfireCache = constructCacheFactoryBean(); + + gemfireCache.setDurableClientId(durableClientId()); + gemfireCache.setDurableClientTimeout(durableClientTimeout()); + gemfireCache.setFreeConnectionTimeout(freeConnectionTimeout()); + gemfireCache.setIdleTimeout(idleTimeout()); + gemfireCache.setKeepAlive(keepAlive()); + gemfireCache.setLocators(poolLocators()); + gemfireCache.setLoadConditioningInterval(loadConditioningInterval()); + gemfireCache.setMaxConnections(maxConnections()); + gemfireCache.setMinConnections(minConnections()); + gemfireCache.setMultiUserAuthentication(multiUserAuthentication()); + gemfireCache.setPingInterval(pingInterval()); + gemfireCache.setPrSingleHopEnabled(prSingleHopEnabled()); + gemfireCache.setReadTimeout(readTimeout()); + gemfireCache.setReadyForEvents(readyForEvents()); + gemfireCache.setRetryAttempts(retryAttempts()); + gemfireCache.setServerGroup(serverGroup()); + gemfireCache.setServers(poolServers()); + gemfireCache.setSocketBufferSize(socketBufferSize()); + gemfireCache.setStatisticsInterval(statisticsInterval()); + gemfireCache.setSubscriptionAckInterval(subscriptionAckInterval()); + gemfireCache.setSubscriptionEnabled(subscriptionEnabled()); + gemfireCache.setSubscriptionMessageTrackingTimeout(subscriptionMessageTrackingTimeout()); + gemfireCache.setSubscriptionRedundancy(subscriptionRedundancy()); + gemfireCache.setThreadLocalConnections(threadLocalConnections()); + + return gemfireCache; + } + + @Override + @SuppressWarnings("unchecked") + protected T newCacheFactoryBean() { + return (T) new ClientCacheFactoryBean(); + } + + /** + * Configures GemFire {@link com.gemstone.gemfire.cache.client.ClientCache} specific settings. + * + * @param importMetadata {@link AnnotationMetadata} containing client cache meta-data used to configure + * the GemFire {@link com.gemstone.gemfire.cache.client.ClientCache}. + * @see org.springframework.core.type.AnnotationMetadata + */ + @Override + protected void configureCache(AnnotationMetadata importMetadata) { + super.configureCache(importMetadata); + + if (isClientCacheApplication(importMetadata)) { + Map clientCacheApplicationAttributes = + importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + + setDurableClientId((String) clientCacheApplicationAttributes.get("durableClientId")); + setDurableClientTimeout((Integer) clientCacheApplicationAttributes.get("durableClientTimeout")); + setFreeConnectionTimeout((Integer) clientCacheApplicationAttributes.get("freeConnectionTimeout")); + setIdleTimeout((Long) clientCacheApplicationAttributes.get("idleTimeout")); + setKeepAlive(Boolean.TRUE.equals(clientCacheApplicationAttributes.get("keepAlive"))); + setLoadConditioningInterval((Integer) clientCacheApplicationAttributes.get("loadConditionInterval")); + setMaxConnections((Integer) clientCacheApplicationAttributes.get("maxConnections")); + setMinConnections((Integer) clientCacheApplicationAttributes.get("minConnections")); + setMultiUserAuthentication(Boolean.TRUE.equals(clientCacheApplicationAttributes.get("multiUserAuthentication"))); + setPingInterval((Long) clientCacheApplicationAttributes.get("pingInterval")); + setPrSingleHopEnabled(Boolean.TRUE.equals(clientCacheApplicationAttributes.get("prSingleHopEnabled"))); + setReadTimeout((Integer) clientCacheApplicationAttributes.get("readTimeout")); + setReadyForEvents(Boolean.TRUE.equals(clientCacheApplicationAttributes.get("readyForEvents"))); + setRetryAttempts((Integer) clientCacheApplicationAttributes.get("retryAttempts")); + setServerGroup((String) clientCacheApplicationAttributes.get("serverGroup")); + setSocketBufferSize((Integer) clientCacheApplicationAttributes.get("socketBufferSize")); + setStatisticsInterval((Integer) clientCacheApplicationAttributes.get("statisticInterval")); + setSubscriptionAckInterval((Integer) clientCacheApplicationAttributes.get("subscriptionAckInterval")); + setSubscriptionEnabled(Boolean.TRUE.equals(clientCacheApplicationAttributes.get("subscriptionEnabled"))); + setSubscriptionMessageTrackingTimeout((Integer) clientCacheApplicationAttributes.get("subscriptionMessageTrackingTimeout")); + setSubscriptionRedundancy((Integer) clientCacheApplicationAttributes.get("subscriptionRedundancy")); + setThreadLocalConnections(Boolean.TRUE.equals( + clientCacheApplicationAttributes.get("threadLocalConnections"))); + + configureLocatorOrServer(clientCacheApplicationAttributes); + } + } + + /* (non-Javadoc) */ + void configureLocatorOrServer(Map clientCacheApplicationAttributes) { + AnnotationAttributes[] locators = + (AnnotationAttributes[]) clientCacheApplicationAttributes.get("locators"); + + List poolLocators = new ArrayList(locators.length); + + for (AnnotationAttributes locator : locators) { + poolLocators.add(new ConnectionEndpoint((String) locator.get("host"), (Integer) locator.get("port"))); + } + + setPoolLocators(poolLocators); + + AnnotationAttributes[] servers = + (AnnotationAttributes[]) clientCacheApplicationAttributes.get("servers"); + + List poolServers = new ArrayList(servers.length); + + for (AnnotationAttributes server : servers) { + poolServers.add(new ConnectionEndpoint((String) server.get("host"), (Integer) server.get("port"))); + } + + setPoolServers(poolServers); + } + + @Override + protected Class getAnnotationType() { + return ClientCacheApplication.class; + } + + /* (non-Javadoc) */ + void setDurableClientId(String durableClientId) { + this.durableClientId = durableClientId; + } + + protected String durableClientId() { + return this.durableClientId; + } + + /* (non-Javadoc) */ + void setDurableClientTimeout(Integer durableClientTimeout) { + this.durableClientTimeout = durableClientTimeout; + } + + protected Integer durableClientTimeout() { + return this.durableClientTimeout; + } + + /* (non-Javadoc) */ + void setFreeConnectionTimeout(Integer freeConnectionTimeout) { + this.freeConnectionTimeout = freeConnectionTimeout; + } + + protected Integer freeConnectionTimeout() { + return this.freeConnectionTimeout; + } + + /* (non-Javadoc) */ + void setIdleTimeout(Long idleTimeout) { + this.idleTimeout = idleTimeout; + } + + protected Long idleTimeout() { + return this.idleTimeout; + } + + /* (non-Javadoc) */ + void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + protected Boolean keepAlive() { + return this.keepAlive; + } + + /* (non-Javadoc) */ + void setLoadConditioningInterval(Integer loadConditioningInterval) { + this.loadConditioningInterval = loadConditioningInterval; + } + + protected Integer loadConditioningInterval() { + return this.loadConditioningInterval; + } + + /* (non-Javadoc) */ + void setMaxConnections(Integer maxConnections) { + this.maxConnections = maxConnections; + } + + protected Integer maxConnections() { + return this.maxConnections; + } + + /* (non-Javadoc) */ + void setMinConnections(Integer minConnections) { + this.minConnections = minConnections; + } + + protected Integer minConnections() { + return this.minConnections; + } + + /* (non-Javadoc) */ + void setMultiUserAuthentication(Boolean multiUserAuthentication) { + this.multiUserAuthentication = multiUserAuthentication; + } + + protected Boolean multiUserAuthentication() { + return this.multiUserAuthentication; + } + + /* (non-Javadoc) */ + void setPingInterval(Long pingInterval) { + this.pingInterval = pingInterval; + } + + protected Long pingInterval() { + return this.pingInterval; + } + + /* (non-Javadoc) */ + void setPoolLocators(Iterable locators) { + this.locators = locators; + } + + protected Iterable poolLocators() { + return this.locators; + } + + /* (non-Javadoc) */ + void setPoolServers(Iterable servers) { + this.servers = servers; + } + + protected Iterable poolServers() { + return this.servers; + } + + /* (non-Javadoc) */ + void setPrSingleHopEnabled(Boolean prSingleHopEnabled) { + this.prSingleHopEnabled = prSingleHopEnabled; + } + + protected Boolean prSingleHopEnabled() { + return this.prSingleHopEnabled; + } + + /* (non-Javadoc) */ + void setReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + } + + protected Integer readTimeout() { + return this.readTimeout; + } + + /* (non-Javadoc) */ + void setReadyForEvents(boolean readyForEvents) { + this.readyForEvents = readyForEvents; + } + + protected boolean readyForEvents() { + return this.readyForEvents; + } + + /* (non-Javadoc) */ + void setRetryAttempts(Integer retryAttempts) { + this.retryAttempts = retryAttempts; + } + + protected Integer retryAttempts() { + return this.retryAttempts; + } + + /* (non-Javadoc) */ + void setServerGroup(String serverGroup) { + this.serverGroup = serverGroup; + } + + protected String serverGroup() { + return this.serverGroup; + } + + /* (non-Javadoc) */ + void setSocketBufferSize(Integer socketBufferSize) { + this.socketBufferSize = socketBufferSize; + } + + protected Integer socketBufferSize() { + return this.socketBufferSize; + } + + /* (non-Javadoc) */ + void setStatisticsInterval(Integer statisticsInterval) { + this.statisticsInterval = statisticsInterval; + } + + protected Integer statisticsInterval() { + return this.statisticsInterval; + } + + /* (non-Javadoc) */ + void setSubscriptionAckInterval(Integer subscriptionAckInterval) { + this.subscriptionAckInterval = subscriptionAckInterval; + } + + protected Integer subscriptionAckInterval() { + return this.subscriptionAckInterval; + } + + /* (non-Javadoc) */ + void setSubscriptionEnabled(Boolean subscriptionEnabled) { + this.subscriptionEnabled = subscriptionEnabled; + } + + protected Boolean subscriptionEnabled() { + return this.subscriptionEnabled; + } + + /* (non-Javadoc) */ + void setSubscriptionMessageTrackingTimeout(Integer subscriptionMessageTrackingTimeout) { + this.subscriptionMessageTrackingTimeout = subscriptionMessageTrackingTimeout; + } + + protected Integer subscriptionMessageTrackingTimeout() { + return this.subscriptionMessageTrackingTimeout; + } + + /* (non-Javadoc) */ + void setSubscriptionRedundancy(Integer subscriptionRedundancy) { + this.subscriptionRedundancy = subscriptionRedundancy; + } + + protected Integer subscriptionRedundancy() { + return this.subscriptionRedundancy; + } + + /* (non-Javadoc) */ + void setThreadLocalConnections(Boolean threadLocalConnections) { + this.threadLocalConnections = threadLocalConnections; + } + + protected Boolean threadLocalConnections() { + return this.threadLocalConnections; + } + + @Override + public String toString() { + return DEFAULT_NAME; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedLocatorConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedLocatorConfiguration.java new file mode 100644 index 00000000..eae66619 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedLocatorConfiguration.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.Map; +import java.util.Properties; + +import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport; + +/** + * The EmbeddedManagerConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar} + * that applies additional GemFire configuration by way of GemFire System properties to configure + * an embedded GemFire Locator. + * + * @author John Blum + * @see EmbeddedServiceConfigurationSupport + * @since 1.9.0 + */ +public class EmbeddedLocatorConfiguration extends EmbeddedServiceConfigurationSupport { + + @Override + protected Class getAnnotationType() { + return EnableEmbeddedLocator.class; + } + + @Override + protected Properties toGemFireProperties(Map annotationAttributes) { + Properties gemfireProperties = new Properties(); + + setProperty(gemfireProperties, "start-locator", annotationAttributes.get("startLocator")); + + return gemfireProperties; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedManagerConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedManagerConfiguration.java new file mode 100644 index 00000000..c49e9cc1 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EmbeddedManagerConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.Map; +import java.util.Properties; + +import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport; + +/** + * The EmbeddedManagerConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar} + * that applies additional GemFire configuration by way of GemFire System properties to configure + * an embedded GemFire Manager. + * + * @author John Blum + * @see EmbeddedServiceConfigurationSupport + * @since 1.9.0 + */ +public class EmbeddedManagerConfiguration extends EmbeddedServiceConfigurationSupport { + + @Override + protected Class getAnnotationType() { + return EnableEmbeddedManager.class; + } + + @Override + protected Properties toGemFireProperties(Map annotationAttributes) { + Properties gemfireProperties = new Properties(); + + boolean jmxManager = Boolean.valueOf(String.valueOf(annotationAttributes.get("jmxManager"))); + + if (jmxManager) { + setProperty(gemfireProperties, "jmx-manager", Boolean.TRUE.toString()); + setProperty(gemfireProperties, "jmx-manager-access-file", annotationAttributes.get("jmxManagerAccessFile")); + setProperty(gemfireProperties, "jmx-manager-bind-address", annotationAttributes.get("jmxManagerBindAddress")); + setProperty(gemfireProperties, "jmx-manager-hostname-for-clients", annotationAttributes.get("jmxManagerHostnameForClients")); + setProperty(gemfireProperties, "jmx-manager-password-file", annotationAttributes.get("jmxManagerPasswordFile")); + setProperty(gemfireProperties, "jmx-manager-port", annotationAttributes.get("jmxManagerPort")); + setProperty(gemfireProperties, "jmx-manager-start", annotationAttributes.get("jmxManagerStart")); + setProperty(gemfireProperties, "jmx-manager-update-rate", annotationAttributes.get("jmxManagerUpdateRate")); + + boolean jmxManagerSslEnabled = Boolean.valueOf(String.valueOf(annotationAttributes.get("jmxManagerSslEnabled"))); + + if (jmxManagerSslEnabled) { + setProperty(gemfireProperties, "jmx-manager-ssl-enabled", Boolean.TRUE.toString()); + setProperty(gemfireProperties, "jmx-manager-ssl-ciphers", annotationAttributes.get("jmxManagerSslCiphers")); + setProperty(gemfireProperties, "jmx-manager-ssl-protocols", annotationAttributes.get("jmxManagerSslProtocols")); + setProperty(gemfireProperties, "jmx-manager-ssl-require-authentication", annotationAttributes.get("jmxManagerSslRequireAuthentication")); + } + } + + return gemfireProperties; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedLocator.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedLocator.java new file mode 100644 index 00000000..d5652d8f --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedLocator.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * The EnableEmbeddedLocator annotation marks a Spring {@link org.springframework.context.annotation.Configuration} + * class to embed a GemFire Locator service in the GemFire server-side data member node. + * + * @author John Blum + * @see org.springframework.data.gemfire.config.annotation.EmbeddedLocatorConfiguration + * @since 1.9.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(EmbeddedLocatorConfiguration.class) +@SuppressWarnings("unused") +public @interface EnableEmbeddedLocator { + + /** + * If set, automatically starts a Locator in the current process when the member connects to the distributed system + * and stops the Locator when the member disconnects. + * To use, specify the Locator with an optional address or host specification and a required port number, in one of + * these formats: + * + * start-locator=address[port1] + * + * start-locator=port1 + * + * If you only specify the port, the address assigned to the member is used for the Locator. + * If not already there, this locator is automatically added to the list of Locators in this + * set of GemFire Properties. + * + * Defaults to {@literal 10334} (The default GemFire Locator port). + */ + String startLocator() default "localhost[10334]"; + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedManager.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedManager.java new file mode 100644 index 00000000..41f18a21 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEmbeddedManager.java @@ -0,0 +1,156 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * The EnableEmbeddedLocator annotation marks a Spring {@link org.springframework.context.annotation.Configuration} + * class to embed a GemFire Manager service in the GemFire server-side data member node. + * + * @author John Blum + * @see org.springframework.data.gemfire.config.annotation.EmbeddedManagerConfiguration + * @since 1.9.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(EmbeddedManagerConfiguration.class) +@SuppressWarnings("unused") +public @interface EnableEmbeddedManager { + + /** + * If {@code true} then this member is willing to be a JMX Manager. All the other JMX Manager properties will be + * used when it does become a manager. If this property is {@code false} then all other jmx-manager-* properties + * are ignored. + * + * Defaults to {@code true}. + */ + boolean jmxManager() default true; + + /** + * By default, the JMX Manager will allow full access to all mbeans by any client. If this property is set to + * the name of a file then it can restrict clients to only being able to read MBeans; they will not be able + * to modify MBeans. The access level can be configured differently in this file for each user name defined + * in the password file. For more information about the format of this file see Oracle's documentation + * of the {@code com.sun.management.jmxremote.access.file} System property. Ignored if {@literal jmx-manager} + * is false or if {@literal jmx-manager-port} is zero. + */ + String jmxManagerAccessFile() default ""; + + /** + * By default, the JMX Manager (when configured with a port) will listen on all the local host's addresses. + * You can use this property to configure what IP address or host name the JMX Manager will listen on for + * non-HTTP connections. Ignored if JMX Manager is {@code false} or {@literal jmx-manager-port} is zero. + * + * Defaults to {@literal localhost}. + */ + String jmxManagerBindAddress() default "localhost"; + + /** + * Lets you control what hostname will be given to clients that ask the Locator for the location of a JMX Manager. + * By default, the IP address that the JMX Manager reports is used. But for clients on a different network + * this property allows you to configure a different hostname that will be given to clients. Ignored if + * {@literal jmx-manager} is {@code false} or {@literal jmx-manager-port} is zero. + * + * Defaults to {@literal localhost}. + */ + String jmxManagerHostnameForClients() default "localhost"; + + /** + * By default, the JMX Manager will allow clients without credentials to connect. If this property is set to + * the name of a file then only clients that connect with credentials that match an entry in this file will + * be allowed. Most JVMs require that the file is only readable by the owner. For more information about the + * format of this file see Oracle's documentation of the {@literal com.sun.management.jmxremote.password.file} + * System property. Ignored if {@literal jmx-manager} is {@code false} or {@literal jmx-manager-port} is zero. + */ + String jmxManagerPasswordFile() default ""; + + /** + * The port this JMX Manager will listen to for client connections. If this property is set to zero then GemFire + * will not allow remote client connections but you can alternatively use the standard System properties supported + * by the JVM for configuring access from remote JMX clients. Ignored if {@literal jmx-manager} is {@code false}. + * + * Defaults to {@literal 1099}. + */ + int jmxManagerPort() default 1099; + + /** + * Enables or disables SSL for connections to the JMX Manager. If {@code true} and {@literal jmx-manager-port} + * is not zero, then the JMX Manager will only accept SSL connections. If this property is not set, then GemFire + * uses the value of {@literal cluster-ssl-enabled} to determine whether JMX connections should use SSL. + * + * Defaults to {@code false}. + */ + boolean jmxManagerSslEnabled() default false; + + /** + * A space-separated list of the valid SSL ciphers for JMX Manager connections. A setting of 'any' uses any ciphers + * that are enabled by default in the configured JSSE provider. If this property is not set, then GemFire uses + * the value of {@literal cluster-ssl-ciphers} to determine which SSL ciphers are used for JMX connections. + * + * Defaults to {@literal any}. + */ + String jmxManagerSslCiphers() default "any"; + + /** + * A space-separated list of the valid SSL protocols for JMX Manager connections. A setting of 'any' uses any + * protocol that is enabled by default in the configured JSSE provider. If this property is not set, then GemFire + * uses the value of {@literal cluster-ssl-protocols} to determine which SSL protocols are used for JMX connections. + * + * Defaults to {@literal any}. + */ + String jmxManagerSslProtocols() default "any"; + + /** + * Boolean indicating whether to require authentication for JMX Manager connections. If this property is not set, + * then GemFire uses the value of {@literal cluster-ssl-require-authentication} to determine whether JMX connections + * require authentication. + * + * Defaults to {@code true}. + */ + boolean jmxManagerSslRequireAuthentication() default true; + + /** + * If true then this member will start a JMX Manager when it creates a cache. Management tools like Gfsh can be + * configured to connect to the JMX Manager. In most cases you should not set this because a JMX Manager will + * automatically be started when needed on a member that sets {@literal jmx-manager} to {@code true}. Ignored if + * {@literal jmx-manager} is {@code false}. + * + * Defaults to {@code false}. + */ + boolean jmxManagerStart() default false; + + /** + * The rate, in milliseconds, at which this member will push updates to any JMX Managers. Currently this value + * should be greater than or equal to the {@literal statistic-sample-rate}. Setting this value too high will + * cause stale values to be seen by Gfsh and GemFire Pulse. + * + * Defaults to {@code 2000}. + */ + int jmxManagerUpdateRate() default 2000; + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableMemcachedServer.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableMemcachedServer.java new file mode 100644 index 00000000..5f893d22 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableMemcachedServer.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * The EnableMemcachedServer annotation marks a Spring {@link org.springframework.context.annotation.Configuration} + * class to embed the Gemcached service in the GemFire server-side data member node. + * + * The Gemcached service implements the Memcached server protocol enabling Memcached clients to connect and speak + * to Pivotal GemFire or Apache Geode. + * + * @author John Blum + * @see org.springframework.data.gemfire.config.annotation.MemcachedServerConfiguration + * @since 1.9.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(MemcachedServerConfiguration.class) +@SuppressWarnings("unused") +public @interface EnableMemcachedServer { + + /** + * If specified and is non-zero, sets the port number for an embedded Gemcached server + * and starts the Gemcached server. + * + * Default to {@literal 11211}. + */ + int port() default 11211; + + /** + * Sets the protocol used by an embedded Gemcached server. Valid values are BINARY and ASCII. + * If you omit this property, the ASCII protocol is used. + * + * Default to {@link MemcachedProtocol#ASCII}. + */ + MemcachedProtocol protocol() default MemcachedProtocol.ASCII; + + /** + * Valid values for the Memcached Network Protocol on-the-wire transport. + */ + enum MemcachedProtocol { + ASCII, + BINARY + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePdx.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePdx.java new file mode 100644 index 00000000..00ce061b --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePdx.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The EnablePdx class... + * + * @author John Blum + * @since 1.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SuppressWarnings("unused") +public @interface EnablePdx { + + String diskStoreName(); + + boolean ignoreUnreadFields() default false; + + boolean persistent() default false; + + boolean readSerialized() default false; + + String serializerBeanName() default ""; + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/MemcachedServerConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/MemcachedServerConfiguration.java new file mode 100644 index 00000000..4d61550a --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/MemcachedServerConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.Map; +import java.util.Properties; + +import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport; + +/** + * The MemcachedServerConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar} + * that applies additional GemFire configuration by way of GemFire System properties to configure + * an embedded Memcached server. + * + * @author John Blum + * @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport + * @since 1.9.0 + */ +public class MemcachedServerConfiguration extends EmbeddedServiceConfigurationSupport { + + @Override + protected Class getAnnotationType() { + return EnableMemcachedServer.class; + } + + @Override + protected Properties toGemFireProperties(Map annotationAttributes) { + Properties gemfireProperties = new Properties(); + + setProperty(gemfireProperties, "memcached-port", annotationAttributes.get("port")); + setProperty(gemfireProperties, "memcached-protocol", annotationAttributes.get("protocol")); + + return gemfireProperties; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java new file mode 100644 index 00000000..122581a6 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.gemstone.gemfire.cache.control.ResourceManager; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * The PeerCacheApplication annotation enables an embedded GemFire peer {@link com.gemstone.gemfire.cache.Cache} + * instance in a Spring Data GemFire based application. + * + * @author John Blum + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.Import + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfiguration + * @see com.gemstone.gemfire.cache.control.ResourceManager + * @since 1.9.0 + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Configuration +@Import(PeerCacheConfiguration.class) +@SuppressWarnings("unused") +public @interface PeerCacheApplication { + + /** + * Indicates whether the "copy on read" is enabled for this cache. + * + * Default is {@code false}. + */ + boolean copyOnRead() default false; + + /** + * Configures the percentage of heap at or above which the cache is considered in danger of becoming inoperable. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_CRITICAL_HEAP_PERCENTAGE + */ + float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_HEAP_PERCENTAGE; + + /** + * By default, a GemFire member (both locators and servers) will attempt to reconnect and reinitialize the cache + * after it has been forced out of the distributed system by a network partition event or has otherwise been + * shunned by other members. Use this property to enable the auto-reconnect behavior. + * + * Default is {@code false}. + */ + boolean enableAutoReconnect() default false; + + /** + * Configures the percentage of heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * @see com.gemstone.gemfire.cache.control.ResourceManager#DEFAULT_EVICTION_HEAP_PERCENTAGE + */ + float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_HEAP_PERCENTAGE; + + /** + * Configures the list of GemFire Locators defining the cluster to which this GemFire cache data node + * should connect. + */ + String locators() default ""; + + /** + * Configures the length, in seconds, of distributed lock leases obtained by this cache. + * + * Default is {@literal 120} seconds. + */ + int lockLease() default 120; + + /** + * Configures the number of seconds a cache operation will wait to obtain a distributed lock lease. + * + * Default is {@literal 60} seconds. + */ + int lockTimeout() default 60; + + /** + * Configures the log level used to output log messages at GemFire cache runtime. + * + * Default is {@literal config}. + */ + String logLevel() default PeerCacheConfiguration.DEFAULT_LOG_LEVEL; + + /** + * Configures the frequency (in seconds) at which a message will be sent by the primary cache-server to all + * the secondary cache-server nodes to remove the events which have already been dispatched from the queue. + * + * Default is {@literal 1} second. + */ + int messageSyncInterval() default 1; + + /** + * Configures the name of this GemFire member in the cluster (distributed system). + * + * Default is {@literal SpringBasedPeerCacheApplication}. + */ + String name() default PeerCacheConfiguration.DEFAULT_NAME; + + /** + * Configures the number of seconds a cache get operation can spend searching for a value before it times out. + * + * Default is {@literal 300} seconds. + */ + int searchTimeout() default 300; + + /** + * Configures whether this GemFire cache member node would pull it's configuration meta-data + * from the cluster-based Cluster Configuration service. + * + * Default is {@code false}. + */ + boolean useClusterConfiguration() default false; + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java new file mode 100644 index 00000000..f6ff58ee --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java @@ -0,0 +1,169 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.util.Map; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.CacheFactoryBean; + +/** + * Spring {@link Configuration} class used to configure, construct and initialize + * a GemFire {@link com.gemstone.gemfire.cache.Cache} instance in a Spring application context. + * + * @author John Blum + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration + * @see com.gemstone.gemfire.cache.Cache + * @since 1.9.0 + */ +@Configuration +@SuppressWarnings("unused") +public class PeerCacheConfiguration extends AbstractCacheConfiguration { + + protected static final boolean DEFAULT_ENABLE_AUTO_RECONNECT = false; + protected static final boolean DEFAULT_USE_CLUSTER_CONFIGURATION = false; + + protected static final String DEFAULT_NAME = "SpringBasedPeerCacheApplication"; + + private boolean enableAutoReconnect = DEFAULT_ENABLE_AUTO_RECONNECT; + private boolean useClusterConfiguration = DEFAULT_USE_CLUSTER_CONFIGURATION; + + private Integer lockLease; + private Integer lockTimeout; + private Integer messageSyncInterval; + private Integer searchTimeout; + + @Bean + public CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = constructCacheFactoryBean(); + + gemfireCache.setEnableAutoReconnect(enableAutoReconnect()); + gemfireCache.setLockLease(lockLease()); + gemfireCache.setLockTimeout(lockTimeout()); + gemfireCache.setMessageSyncInterval(messageSyncInterval()); + gemfireCache.setSearchTimeout(searchTimeout()); + gemfireCache.setUseBeanFactoryLocator(useBeanFactoryLocator()); + gemfireCache.setUseClusterConfiguration(useClusterConfiguration()); + + return gemfireCache; + } + + @Override + @SuppressWarnings("unchecked") + protected T newCacheFactoryBean() { + return (T) new CacheFactoryBean(); + } + + /** + * Configures GemFire peer {@link com.gemstone.gemfire.cache.Cache} specific settings. + * + * @param importMetadata {@link AnnotationMetadata} containing peer cache meta-data used to configure + * the GemFire peer {@link com.gemstone.gemfire.cache.Cache}. + * @see org.springframework.core.type.AnnotationMetadata + */ + @Override + protected void configureCache(AnnotationMetadata importMetadata) { + super.configureCache(importMetadata); + + if (isPeerCacheApplication(importMetadata) || isCacheServerApplication(importMetadata)) { + Map peerCacheApplicationAttributes = + importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + + setEnableAutoReconnect(Boolean.TRUE.equals(peerCacheApplicationAttributes.get("enableAutoReconnect"))); + setLockLease((Integer) peerCacheApplicationAttributes.get("lockLease")); + setLockTimeout((Integer) peerCacheApplicationAttributes.get("lockTimeout")); + setMessageSyncInterval((Integer) peerCacheApplicationAttributes.get("messageSyncInterval")); + setSearchTimeout((Integer) peerCacheApplicationAttributes.get("searchTimeout")); + setUseClusterConfiguration(Boolean.TRUE.equals(peerCacheApplicationAttributes.get("useClusterConfiguration"))); + + String locators = (String) peerCacheApplicationAttributes.get("locators"); + + if (hasValue(locators)) { + setLocators(locators); + } + } + } + + @Override + protected Class getAnnotationType() { + return PeerCacheApplication.class; + } + + /* (non-Javadoc) */ + void setEnableAutoReconnect(boolean enableAutoReconnect) { + this.enableAutoReconnect = enableAutoReconnect; + } + + protected boolean enableAutoReconnect() { + return this.enableAutoReconnect; + } + + /* (non-Javadoc) */ + void setLockLease(Integer lockLease) { + this.lockLease = lockLease; + } + + protected Integer lockLease() { + return this.lockLease; + } + + /* (non-Javadoc) */ + void setLockTimeout(Integer lockTimeout) { + this.lockTimeout = lockTimeout; + } + + protected Integer lockTimeout() { + return this.lockTimeout; + } + + /* (non-Javadoc) */ + void setMessageSyncInterval(Integer messageSyncInterval) { + this.messageSyncInterval = messageSyncInterval; + } + + protected Integer messageSyncInterval() { + return this.messageSyncInterval; + } + + /* (non-Javadoc) */ + void setSearchTimeout(Integer searchTimeout) { + this.searchTimeout = searchTimeout; + } + + protected Integer searchTimeout() { + return this.searchTimeout; + } + + /* (non-Javadoc) */ + void setUseClusterConfiguration(boolean useClusterConfiguration) { + this.useClusterConfiguration = useClusterConfiguration; + } + + protected boolean useClusterConfiguration() { + return this.useClusterConfiguration; + } + + @Override + public String toString() { + return DEFAULT_NAME; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java new file mode 100644 index 00000000..2f79ebe3 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java @@ -0,0 +1,152 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation.support; + +import java.util.Map; +import java.util.Properties; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.BeanPostProcessor; +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.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.util.CollectionUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * The EmbeddedServiceConfigurationSupport class is an abstract base class supporting the configuration + * of Pivotal GemFire and Apache Geode embedded services. + * + * @author John Blum + * @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar + * @since 1.9.0 + */ +public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanDefinitionRegistrar { + + /* (non-Javadoc) */ + protected static Properties setProperty(Properties properties, String key, Object value) { + if (value != null) { + setProperty(properties, key, value.toString()); + } + + return properties; + } + + /* (non-Javadoc) */ + protected static Properties setProperty(Properties properties, String key, String value) { + Assert.notNull(properties, "Properties must not be null"); + Assert.hasText(key, "Key must not be null or empty"); + + if (StringUtils.hasText(value)) { + properties.setProperty(key, value); + } + + return properties; + } + + /* (non-Javadoc) */ + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + + if (importingClassMetadata.hasAnnotation(getAnnotationTypeName())) { + Map annotationAttributes = + importingClassMetadata.getAnnotationAttributes(getAnnotationTypeName()); + + Properties customizedGemFireProperties = toGemFireProperties(annotationAttributes); + + if (!CollectionUtils.isEmpty(customizedGemFireProperties)) { + registerGemFirePropertiesBeanPostProcessor(registry, customizedGemFireProperties); + } + } + } + + /* (non-Javadoc) */ + protected String getAnnotationTypeName() { + return getAnnotationType().getName(); + } + + /* (non-Javadoc) */ + protected String getAnnotationTypeSimpleName() { + return getAnnotationType().getSimpleName(); + } + + /* (non-Javadoc) */ + protected abstract Class getAnnotationType(); + + /* (non-Javadoc) */ + protected String getBeanName() { + return String.format("%1$s.%2$s", getClass().getName(), getAnnotationTypeSimpleName()); + } + + /* (non-Javadoc) */ + protected BeanDefinitionHolder newBeanDefinitionHolder(BeanDefinitionBuilder builder) { + return new BeanDefinitionHolder(builder.getBeanDefinition(), getBeanName()); + } + + /* (non-Javadoc) */ + protected abstract Properties toGemFireProperties(Map annotationAttributes); + + /* (non-Javadoc) */ + protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry, + Properties customizedGemFireProperties) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + GemFirePropertiesBeanPostProcessor.class); + + builder.addConstructorArgValue(customizedGemFireProperties); + + BeanDefinitionReaderUtils.registerBeanDefinition(newBeanDefinitionHolder(builder), registry); + } + + /** + * Spring {@link BeanPostProcessor} used to post process before initialization in GemFire System properties + * Spring bean defined in the application context. + * + * @see org.springframework.beans.factory.config.BeanPostProcessor + */ + protected static class GemFirePropertiesBeanPostProcessor implements BeanPostProcessor { + + static final String GEMFIRE_PROPERTIES_BEAN_NAME = "gemfireProperties"; + + private final Properties gemfireProperties; + + protected GemFirePropertiesBeanPostProcessor(Properties gemfireProperties) { + Assert.notEmpty(gemfireProperties, "GemFire Properties must not be null or empty"); + this.gemfireProperties = gemfireProperties; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof Properties && GEMFIRE_PROPERTIES_BEAN_NAME.equals(beanName)) { + Properties gemfirePropertiesBean = (Properties) bean; + gemfirePropertiesBean.putAll(gemfireProperties); + } + + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + } +} diff --git a/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java b/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java index 3d9d0c46..2bda2f26 100644 --- a/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java @@ -19,6 +19,12 @@ import java.io.IOException; import java.util.Collections; import java.util.Set; +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.InterestRegistrationListener; +import com.gemstone.gemfire.cache.server.CacheServer; +import com.gemstone.gemfire.cache.server.ClientSubscriptionConfig; +import com.gemstone.gemfire.cache.server.ServerLoadProbe; + import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; @@ -27,12 +33,6 @@ import org.springframework.context.SmartLifecycle; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import com.gemstone.gemfire.cache.Cache; -import com.gemstone.gemfire.cache.InterestRegistrationListener; -import com.gemstone.gemfire.cache.server.CacheServer; -import com.gemstone.gemfire.cache.server.ClientSubscriptionConfig; -import com.gemstone.gemfire.cache.server.ServerLoadProbe; - /** * FactoryBean for easy creation and configuration of GemFire {@link CacheServer} instances. * diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/ClientServerCacheApplicationIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/ClientServerCacheApplicationIntegrationTests.java new file mode 100644 index 00000000..3ab32e38 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/ClientServerCacheApplicationIntegrationTests.java @@ -0,0 +1,201 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.Resource; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheLoader; +import com.gemstone.gemfire.cache.CacheLoaderException; +import com.gemstone.gemfire.cache.LoaderHelper; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.client.ClientRegionShortcut; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.PartitionedRegionFactoryBean; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.process.ProcessExecutor; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.data.gemfire.test.support.IOUtils; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +/** + * Test suite of test cases testing the contract and functionality of the {@link CacheServerApplication} + * and {@link ClientCacheApplication} SDG annotation for configuring and bootstrapping the Pivotal GemFire + * client/server topology + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.config.annotation.CacheServerApplication + * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @see com.gemstone.gemfire.cache.client.ClientCache + * @see com.gemstone.gemfire.cache.server.CacheServer + * @since 1.9.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ClientServerCacheApplicationIntegrationTests.ClientCacheApplicationConfiguration.class) +@SuppressWarnings("all") +public class ClientServerCacheApplicationIntegrationTests { + + private static final int PORT = 12480; + + private static ProcessWrapper gemfireServerProcess; + + @BeforeClass + public static void setupGemFireServer() throws Exception { + String serverName = ClientServerCacheApplicationIntegrationTests.class.getSimpleName(); + + File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase()); + + Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs()); + + List arguments = new ArrayList(); + + arguments.add(String.format("-Dgemfire.name=%1$s", serverName)); + + gemfireServerProcess = ProcessExecutor.launch(serverWorkingDirectory, + CacheServerApplicationConfiguration.class, + arguments.toArray(new String[arguments.size()])); + + waitForServerStart(TimeUnit.SECONDS.toMillis(20)); + + System.out.println("GemFire Cache Server Process for Client/Server cache application should be running..."); + } + + private static void waitForServerStart(final long milliseconds) { + ThreadUtils.timedWait(milliseconds, TimeUnit.SECONDS.toMillis(1), new ThreadUtils.WaitCondition() { + AtomicBoolean connected = new AtomicBoolean(false); + + public boolean waiting() { + Socket socket = null; + + try { + if (!connected.get()) { + socket = new Socket("localhost", PORT); + connected.set(true); + } + } + catch (IOException ignore) { + } + finally { + IOUtils.close(socket); + } + + return !connected.get(); + } + }); + } + + @AfterClass + public static void tearDownGemFireServer() { + gemfireServerProcess.shutdown(); + + if (Boolean.valueOf(System.getProperty("spring.data.gemfire.fork.clean", Boolean.TRUE.toString()))) { + org.springframework.util.FileSystemUtils.deleteRecursively(gemfireServerProcess.getWorkingDirectory()); + } + } + + @Autowired + private ClientCache clientCache; + + @Resource(name = "Echo") + private Region echo; + + @Test + public void clientEchoProxyRegionEchoesKeysForValues() { + assertThat(echo.get("Hello"), is(equalTo("Hello"))); + assertThat(echo.get("Test"), is(equalTo("Test"))); + } + + @CacheServerApplication(name = "ClientServerCacheApplicationIntegrationTests", logLevel = "warn", port = 12480) + public static class CacheServerApplicationConfiguration { + + public static void main(String[] args) { + AnnotationConfigApplicationContext applicationContext = + new AnnotationConfigApplicationContext(CacheServerApplicationConfiguration.class); + + applicationContext.registerShutdownHook(); + } + + @Bean(name = "Echo") + public PartitionedRegionFactoryBean echoRegion(Cache gemfireCache) { + PartitionedRegionFactoryBean echoRegion = + new PartitionedRegionFactoryBean(); + + echoRegion.setCache(gemfireCache); + echoRegion.setCacheLoader(echoCacheLoader()); + echoRegion.setClose(false); + echoRegion.setPersistent(false); + + return echoRegion; + } + + CacheLoader echoCacheLoader() { + return new CacheLoader() { + @Override + public String load(LoaderHelper helper) throws CacheLoaderException { + return helper.getKey(); + } + + @Override + public void close() { + } + }; + } + } + + @ClientCacheApplication(logLevel = "warn", servers = { @ClientCacheApplication.Server(port = 12480)}) + static class ClientCacheApplicationConfiguration { + + @Bean(name = "Echo") + ClientRegionFactoryBean echoRegion(ClientCache gemfireCache) { + ClientRegionFactoryBean echoRegion = new ClientRegionFactoryBean(); + + echoRegion.setCache(gemfireCache); + echoRegion.setClose(false); + echoRegion.setShortcut(ClientRegionShortcut.PROXY); + + return echoRegion; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java new file mode 100644 index 00000000..11da0347 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config.annotation; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import javax.annotation.Resource; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheLoader; +import com.gemstone.gemfire.cache.CacheLoaderException; +import com.gemstone.gemfire.cache.LoaderHelper; +import com.gemstone.gemfire.cache.Region; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.PartitionedRegionFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Test suite of test case testing the contract and functionality of the {@link PeerCacheApplication} SDG annotation + * for configuring and bootstrapping a GemFire peer cache instance. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @see com.gemstone.gemfire.cache.Cache + * @since 1.9.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = PeerCacheApplicationIntegrationTests.PeerCacheApplicationConfiguration.class) +@SuppressWarnings("all") +public class PeerCacheApplicationIntegrationTests { + + @Resource(name = "Echo") + private Region echo; + + @Test + public void echoPartitionRegionEchoesKeysAsValues() { + assertThat(echo.get("Hello"), is(equalTo("Hello"))); + assertThat(echo.get("Test"), is(equalTo("Test"))); + } + + @PeerCacheApplication(name = "PeerCacheApplicationIntegrationTests", logLevel = "warn") + static class PeerCacheApplicationConfiguration { + + @Bean(name = "Echo") + PartitionedRegionFactoryBean echoRegion(Cache gemfireCache) { + PartitionedRegionFactoryBean echoRegion = + new PartitionedRegionFactoryBean(); + + echoRegion.setCache(gemfireCache); + echoRegion.setCacheLoader(echoCacheLoader()); + echoRegion.setClose(false); + echoRegion.setPersistent(false); + + return echoRegion; + } + + CacheLoader echoCacheLoader() { + return new CacheLoader() { + @Override + public String load(LoaderHelper helper) throws CacheLoaderException { + return helper.getKey(); + } + + @Override + public void close() { + } + }; + } + } +}