diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java index fc8f842d..70ab070d 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java @@ -89,8 +89,7 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware private static final AtomicBoolean CUSTOM_EDITORS_REGISTERED = new AtomicBoolean(false); private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED = new AtomicBoolean(false); private static final AtomicBoolean DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false); - private static final AtomicBoolean PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED = - new AtomicBoolean(false); + private static final AtomicBoolean PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false); protected static final boolean DEFAULT_CLOSE = true; protected static final boolean DEFAULT_COPY_ON_READ = false; @@ -271,7 +270,6 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware registerCustomEditorBeanFactoryPostProcessor(importMetadata); registerDefinedIndexesApplicationListener(importMetadata); registerDiskStoreDirectoryBeanPostProcessor(importMetadata); - registerPdxDiskStoreAwareBeanFactoryPostProcessor(importMetadata); } /** @@ -302,6 +300,7 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware setLogLevel((String) cacheMetadataAttributes.get("logLevel")); setName((String) cacheMetadataAttributes.get("name")); + setUseBeanFactoryLocator(Boolean.TRUE.equals(cacheMetadataAttributes.get("useBeanFactoryLocator"))); } } @@ -324,6 +323,8 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware setPdxPersistent(Boolean.TRUE.equals(enablePdxAttributes.get("persistent"))); setPdxReadSerialized(Boolean.TRUE.equals(enablePdxAttributes.get("readSerialized"))); setPdxSerializer(resolvePdxSerializer((String) enablePdxAttributes.get("serializerBeanName"))); + + registerPdxDiskStoreAwareBeanFactoryPostProcessor(importMetadata); } } @@ -384,9 +385,13 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware /* (non-Javadoc) */ protected void registerPdxDiskStoreAwareBeanFactoryPostProcessor(AnnotationMetadata importMetadata) { - if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) { - register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition()); + if (StringUtils.hasText(pdxDiskStoreName())) { + if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) { + register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addConstructorArgValue(pdxDiskStoreName()) + .getBeanDefinition()); + } } } @@ -504,7 +509,7 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware * @see #getAnnotationType() */ protected boolean isTypedCacheApplication(Class annotationType, - AnnotationMetadata importMetadata) { + AnnotationMetadata importMetadata) { return (annotationType.equals(getAnnotationType()) && importMetadata.hasAnnotation(getAnnotationTypeName())); } 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 index 54130c3e..842be303 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java @@ -28,6 +28,8 @@ import com.gemstone.gemfire.cache.control.ResourceManager; import com.gemstone.gemfire.cache.server.CacheServer; import com.gemstone.gemfire.cache.server.ClientSubscriptionConfig; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; @@ -42,7 +44,7 @@ import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; * @author John Blum * @see org.springframework.context.annotation.Configuration * @see org.springframework.context.annotation.Import - * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfiguration + * @see org.springframework.data.gemfire.config.annotation.CacheServerConfiguration * @see com.gemstone.gemfire.cache.control.ResourceManager * @see com.gemstone.gemfire.cache.server.CacheServer * @see com.gemstone.gemfire.cache.server.ClientSubscriptionConfig @@ -234,6 +236,15 @@ public @interface CacheServerApplication { */ SubscriptionEvictionPolicy subscriptionEvictionPolicy() default SubscriptionEvictionPolicy.NONE; + /** + * Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup + * the Spring {@link BeanFactory} to auto-wire and configure/initialize GemFire components + * created in a non-Spring managed, GemFire context. + * + * Defaults to {@literal false}. + */ + boolean useBeanFactoryLocator() default false; + /** * Configures whether this GemFire cache member node would pull it's configuration meta-data * from the cluster-based Cluster Configuration service. 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 index e21dc268..efe7ffec 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java @@ -27,6 +27,8 @@ import java.lang.annotation.Target; import com.gemstone.gemfire.cache.client.PoolFactory; import com.gemstone.gemfire.cache.control.ResourceManager; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.gemfire.GemfireUtils; @@ -257,6 +259,15 @@ public @interface ClientCacheApplication { */ boolean threadLocalConnections() default PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS; + /** + * Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup + * the Spring {@link BeanFactory} to auto-wire and configure/initialize GemFire components + * created in a non-Spring managed, GemFire context. + * + * Defaults to {@literal false}. + */ + boolean useBeanFactoryLocator() default false; + @interface Locator { String host() default "localhost"; 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 index 57313f43..70fdf36f 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java @@ -26,6 +26,8 @@ import java.lang.annotation.Target; import com.gemstone.gemfire.cache.control.ResourceManager; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -129,6 +131,15 @@ public @interface PeerCacheApplication { */ int searchTimeout() default 300; + /** + * Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup + * the Spring {@link BeanFactory} to auto-wire and configure/initialize GemFire components + * created in a non-Spring managed, GemFire context. + * + * Defaults to {@literal false}. + */ + boolean useBeanFactoryLocator() default false; + /** * Configures whether this GemFire cache member node would pull it's configuration meta-data * from the cluster-based Cluster Configuration service. 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 index b103ff61..f8081b92 100644 --- 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 @@ -21,9 +21,13 @@ import java.util.Map; import java.util.Properties; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.NamedBeanHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -39,12 +43,14 @@ import org.springframework.util.StringUtils; * of Pivotal GemFire and Apache Geode embedded services. * * @author John Blum + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar * @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration * @since 1.9.0 */ @SuppressWarnings("unused") -public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanDefinitionRegistrar { +public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanDefinitionRegistrar, BeanFactoryAware { public static final Integer DEFAULT_PORT = 0; public static final String DEFAULT_HOST = "localhost"; @@ -53,6 +59,8 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD @SuppressWarnings("all") private AbstractCacheConfiguration cacheConfiguration; + private BeanFactory beanFactory; + /** * Returns a reference to an instance of the {@link AbstractCacheConfiguration} class used to configure * a GemFire (Singleton, client or peer) cache instance along with it's associated, embedded services. @@ -99,12 +107,32 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD return getAnnotationType().getSimpleName(); } + /** + * @inheritDoc + */ + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + /** + * Returns a reference to the Spring {@link BeanFactory}. + * + * @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 getBeanFactory() { + Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized"); + return this.beanFactory; + } + /** * {@inheritDoc} */ @Override public final void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, - BeanDefinitionRegistry registry) { + BeanDefinitionRegistry registry) { if (isAnnotationPresent(importingClassMetadata)) { Map annotationAttributes = getAnnotationAttributes(importingClassMetadata); @@ -116,12 +144,12 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD /* (non-Javadoc) */ @SuppressWarnings("unused") protected void registerBeanDefinitions(AnnotationMetadata importingClassMetaData, - Map annotationAttributes, BeanDefinitionRegistry registry) { + Map annotationAttributes, BeanDefinitionRegistry registry) { } /* (non-Javadoc) */ protected void setGemFireProperties(AnnotationMetadata importingClassMetadata, - Map annotationAttributes, BeanDefinitionRegistry registry) { + Map annotationAttributes, BeanDefinitionRegistry registry) { Properties gemfireProperties = toGemFireProperties(annotationAttributes); @@ -155,7 +183,7 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD /* (non-Javadoc) */ protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry, - Properties customGemFireProperties) { + Properties customGemFireProperties) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( GemFirePropertiesBeanPostProcessor.class); @@ -185,6 +213,38 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD return String.format("%1$s.%2$s", getClass().getName(), nameQualifier); } + /** + * Resolves a Spring managed bean with the given {@link Class} type from the Spring {@link BeanFactory}. + * + * It is assumed that the given typed bean is the only bean of this {@link Class} type. If more than 1 bean + * of the given {@link Class} type is found, then the Spring {@link BeanFactory} will throw + * a {@link org.springframework.beans.factory.NoUniqueBeanDefinitionException}. + * + * If the {@link BeanFactory} is an instance of {@link AutowireCapableBeanFactory}, then the returned bean + * will also be configured. + * + * @param {@link Class} type of the registered Spring managed bean. + * @param beanType required {@link Class} type of the registered Spring managed bean. + * @return a Spring managed bean instance for the given, required {@link Class} type, or {@literal null} + * if no bean instance of the given, required {@link Class} type could be found. + * @throws BeansException if the Spring manage bean of the required {@link Class} type could not be resolved. + * @see #getBeanFactory() + */ + @SuppressWarnings("unchecked") + protected T resolveBean(Class beanType) { + BeanFactory beanFactory = getBeanFactory(); + + if (beanFactory instanceof AutowireCapableBeanFactory) { + AutowireCapableBeanFactory autowiringBeanFactory = (AutowireCapableBeanFactory) beanFactory; + NamedBeanHolder beanHolder = autowiringBeanFactory.resolveNamedBean(beanType); + + return (T) autowiringBeanFactory.configureBean(beanHolder.getBeanInstance(), beanHolder.getBeanName()); + } + else { + return beanFactory.getBean(beanType); + } + } + /* (non-Javadoc) */ protected String resolveHost(String hostname) { return resolveHost(hostname, DEFAULT_HOST); @@ -217,7 +277,14 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD private final Properties gemfireProperties; - /* (non-Javadoc) */ + /** + * Construct an instance of the {@link GemFirePropertiesBeanPostProcessor} initialized with + * the given GemFire {@link Properties}. + * + * @param gemfireProperties {@link Properties} used to configure GemFire. + * @throws IllegalArgumentException if the {@link Properties} are null or empty. + * @see java.util.Properties + */ protected GemFirePropertiesBeanPostProcessor(Properties gemfireProperties) { Assert.notEmpty(gemfireProperties, "GemFire Properties must not be null or empty"); this.gemfireProperties = gemfireProperties;