SGF-587 - Remove the GemfireBeanFactoryLocator and replace with SpringContextBootstrappingInitializer.

This commit is contained in:
John Blum
2017-01-19 18:23:19 -08:00
parent 2583aa6719
commit 3fb3e6c5df
39 changed files with 2297 additions and 1233 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.data.gemfire;
import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.newBeanFactoryLocator;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -54,6 +56,7 @@ import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -139,15 +142,23 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
private TransactionWriter transactionWriter;
/*
* (non-Javadoc)
/**
* @inheritDoc
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
initBeanFactoryLocator();
postProcessBeforeCacheInitialization(resolveProperties());
}
/* (non-Javadoc) */
private void initBeanFactoryLocator() {
if (useBeanFactoryLocator && beanFactoryLocator == null) {
beanFactoryLocator = newBeanFactoryLocator(this.beanFactory, this.beanName);
}
}
/* (non-Javadoc) */
protected void postProcessBeforeCacheInitialization(Properties gemfireProperties) {
if (GemfireUtils.isGemfireVersion8OrAbove()) {
@@ -180,9 +191,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
/* (non-Javadoc) */
Cache init() throws Exception {
initBeanFactoryLocator();
final ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
// use bean ClassLoader to load Spring configured, GemFire Declarable classes
@@ -209,16 +218,6 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
}
/* (non-Javadoc) */
private void initBeanFactoryLocator() {
if (useBeanFactoryLocator && beanFactoryLocator == null) {
beanFactoryLocator = new GemfireBeanFactoryLocator();
beanFactoryLocator.setBeanFactory(beanFactory);
beanFactoryLocator.setBeanName(beanName);
beanFactoryLocator.afterPropertiesSet();
}
}
/**
* If Dynamic Regions are enabled, create and initialize a DynamicRegionFactory before creating the Cache.
*/

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2010-2013 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;
import java.util.Properties;
import org.apache.geode.cache.CacheCallback;
import org.apache.geode.cache.Declarable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.access.BeanFactoryReference;
/**
* Convenience class for Spring-aware GemFire Declarable components. Provides a reference to the current
* Spring ApplicationContext, e.g. for Spring bean lookup or resource loading.
*
* Note that in most cases, one can just declare the same components as Spring beans, through {@link RegionFactoryBean}
* which gives access to the full Spring container capabilities and does not enforce the {@link Declarable} interface
* to be implemented.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.access.BeanFactoryReference
* @see org.apache.geode.cache.CacheCallback
* @see org.apache.geode.cache.Declarable
*/
@SuppressWarnings("unused")
public abstract class DeclarableSupport implements CacheCallback, Declarable {
private String beanFactoryKey = null;
private BeanFactoryReference beanFactoryReference = null;
public DeclarableSupport() {
}
/**
* Gets a reference to the configured Spring BeanFactory.
*
* @return a Spring BeanFactory reference.
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory getBeanFactory() {
return beanFactoryReference.getFactory();
}
/**
* Sets the key under which the enclosing BeanFactory can be found. Needed only if multiple BeanFactories
* are used with GemFire inside the same class loader / class space.
*
* @param key a String specifying the key used to lookup the "enclosing" BeanFactory in the presence
* of multiple BeanFactories.
* @see org.springframework.data.gemfire.GemfireBeanFactoryLocator
*/
public void setFactoryKey(String key) {
this.beanFactoryKey = key;
}
/**
* This Declarable implementation uses the init method as a lifecycle hook to initialize the bean factory locator.
*
* {@inheritDoc}
*
* @see org.springframework.data.gemfire.GemfireBeanFactoryLocator
* @see #setFactoryKey(String)
*/
@Override
public final void init(Properties parameters) {
beanFactoryReference = new GemfireBeanFactoryLocator().useBeanFactory(beanFactoryKey);
initInstance(parameters);
}
/**
* Initialize this Declarable object with the given Properties.
*
* @param props the Properties (parameters) used to initialize this Declarable.
* @see org.apache.geode.cache.Declarable#init(java.util.Properties)
* @see java.util.Properties
* @see #init(Properties)
*/
protected void initInstance(Properties props) {
}
/* (non-Javadoc) */
@Override
public void close() {
beanFactoryReference.release();
beanFactoryReference = null;
}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright 2010-2013 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;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@link BeanFactoryLocator} used for storing Spring application context/bean factory for Gemfire
* user components (or {@link org.apache.geode.cache.Declarable}. As opposed to the "traditional"
* {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator} this implementation does
* not require any configuration file; it rather assume declaration inside an application context
* (usually through {@link org.apache.geode.cache.CacheFactory} which it will store under the name
* and aliases of the bean (so the same "registry" can be used for storing multiple BeanFactories).
* If there is only one BeanFactory registered then a null value can be used with {@link #setBeanName(String)}.
*
* In most cases, one does not need to use this class directly as it is used internally
* by {@link org.apache.geode.cache.CacheFactory}.
*
* @author Costin Leau
* @author John Blum
*/
public class GemfireBeanFactoryLocator implements BeanFactoryLocator, BeanFactoryAware, BeanNameAware, DisposableBean,
InitializingBean {
private static final Log log = LogFactory.getLog(GemfireBeanFactoryLocator.class);
// alias/bean name <-> BeanFactory lookup
private static final ConcurrentMap<String, BeanFactory> beanFactories = new ConcurrentHashMap<String, BeanFactory>();
// default factory to return
private static volatile boolean canUseDefaultBeanFactory = true;
private static volatile BeanFactory defaultFactory = null;
private static class SimpleBeanFactoryReference implements BeanFactoryReference {
private BeanFactory beanFactory;
SimpleBeanFactoryReference(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public BeanFactory getFactory() {
Assert.state(beanFactory != null, "The BeanFactory has already been released or closed");
return beanFactory;
}
public void release() throws FatalBeanException {
beanFactory = null;
}
}
private BeanFactory beanFactory;
private String[] names;
// default factory name
private String factoryName = GemfireBeanFactoryLocator.class.getName();
public void afterPropertiesSet() {
// add the factory as default if possible (if it's the only one)
synchronized (GemfireBeanFactoryLocator.class) {
canUseDefaultBeanFactory = beanFactories.isEmpty();
if (canUseDefaultBeanFactory) {
if (defaultFactory == null) {
defaultFactory = beanFactory;
if (log.isDebugEnabled())
log.debug("default beanFactoryReference=" + defaultFactory);
}
else {
if (log.isDebugEnabled())
log.debug("more then one beanFactory - default not possible to determine");
canUseDefaultBeanFactory = false;
defaultFactory = null;
}
}
}
// add aliases
if (StringUtils.hasText(factoryName)) {
String[] aliases = beanFactory.getAliases(factoryName);
names = ObjectUtils.addObjectToArray(aliases, factoryName);
for (String name : names) {
if (log.isDebugEnabled())
log.debug("adding key=" + name + " w/ reference=" + beanFactory);
if (beanFactories.containsKey(name) && !beanFactory.equals(beanFactories.get(name))
|| beanFactories.putIfAbsent(name, beanFactory) != null) {
throw new IllegalArgumentException("a beanFactoryReference already exists for key " + factoryName);
}
}
}
}
public void destroy() {
if (names != null) {
for (String name : names) {
beanFactories.remove(name);
}
}
if (beanFactory == defaultFactory) {
synchronized (GemfireBeanFactoryLocator.class) {
defaultFactory = null;
canUseDefaultBeanFactory = beanFactories.isEmpty();
}
}
}
public BeanFactoryReference useBeanFactory(final String factoryKey) throws BeansException {
// see if there is a default FactoryBean
BeanFactory factory;
if (!StringUtils.hasText(factoryKey)) {
if (!canUseDefaultBeanFactory)
throw new IllegalArgumentException(
"a non-null factoryKey needs to be specified as there are more then one factoryKeys available; "
+ beanFactories.keySet());
factory = defaultFactory;
}
else {
factory = beanFactories.get(factoryKey);
if (factory == null)
throw new IllegalArgumentException("there is no beanFactory under key " + factoryKey);
}
return new SimpleBeanFactoryReference(factory);
}
public void setBeanName(String name) {
factoryName = name;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -1,324 +0,0 @@
/*
* Copyright 2010-2013 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;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.Declarable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
import org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* The LazyWiringDeclarableSupport class is an implementation of the GemFire Declarable interface that enables support
* for wiring GemFire components with Spring bean dependencies defined in the Spring context.
*
* @author John Blum
* @see java.util.Properties
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.wiring.BeanWiringInfo
* @see org.springframework.beans.factory.wiring.BeanWiringInfoResolver
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.data.gemfire.DeclarableSupport
* @see org.springframework.data.gemfire.WiringDeclarableSupport
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* @see org.apache.geode.cache.Declarable
* @since 1.3.4
*/
@SuppressWarnings("unused")
public abstract class LazyWiringDeclarableSupport implements ApplicationListener<ContextRefreshedEvent>, Declarable,
DisposableBean {
// name of the template bean defined in the Spring context for wiring this Declarable instance.
protected static final String BEAN_NAME_PARAMETER = "bean-name";
// atomic reference to the parameter passed by GemFire when this Declarable was constructed
// and the init method was called.
private final AtomicReference<Properties> parametersReference = new AtomicReference<Properties>();
// condition determining the initialization state of this Declarable
volatile boolean initialized = false;
private BeanFactoryReference beanFactoryReference = null;
private String factoryKey = null;
/**
* Constructs an instance of the LazyWiringDeclarableSupport class registered with the
* SpringContextBootstrappingInitializer. This Declarable will receive notifications from the
* SpringContextBootstrappingInitializer when the Spring context is created and initialized (refreshed).
* The notification is necessary in order for this Declarable component to be configured and properly initialized
* with any required Spring bean dependencies.
*
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* #register(org.springframework.context.ApplicationListener)
*/
public LazyWiringDeclarableSupport() {
SpringContextBootstrappingInitializer.register(this);
}
/**
* Set the key used to locate (lookup) the Spring BeanFactory.
*
* @param factoryKey the key used to locate the Spring BeanFactory.
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.GemfireBeanFactoryLocator
* @see #getFactoryKey()
*/
public final void setFactoryKey(final String factoryKey) {
this.factoryKey = factoryKey;
}
/**
* Gets the key used to locate (lookup) the Spring BeanFactory.
*
* @return the key used to locate the Spring BeanFactory.
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.GemfireBeanFactoryLocator
* @see #setFactoryKey(String)
*/
protected String getFactoryKey() {
return factoryKey;
}
/**
* Asserts that this Declarable object has been properly configured and initialized by the Spring container
* after GemFire has constructed this Declarable object during startup. It is recommended to call this method
* in any GemFire CacheCallback/Declarable object operational method (e.g. CacheLoader.load(..)) before use
* in order to ensure that this Declarable was properly constructed, configured and initialized.
*
* @throws IllegalStateException if the Declarable object has not been properly configured or initialized
* by the Spring container.
* @see #init(java.util.Properties)
* @see #isInitialized()
*/
protected void assertInitialized() {
Assert.state(isInitialized(), String.format(
"This Declarable object (%1$s) has not been properly configured and initialized",
getClass().getName()));
}
/**
* Asserts that this Declarable object has not yet been used, or activated prior to being fully configured
* and initialized. It is possible, though rare, that the init(:Properties) might be called multiple times
* by GemFire before the Spring container configure, initializes and puts this component to use.
*
* @throws java.lang.IllegalStateException if the Declarable object has already been configured and initialized
* by the Spring container.
* @see #init(java.util.Properties)
* @see #isInitialized()
*/
protected void assertUninitialized() {
Assert.state(!isInitialized(), String.format(
"This Declarable object (%1$s) has already been configured and initialized",
getClass().getName()));
}
/**
* Performs the actual configuration and initialization of this Declarable object before use. This method
* is triggered by an ApplicationEvent (specifically, the ContextRefreshedEvent) indicating that the Spring context
* has been created and refreshed.
*
* @param beanFactory the ConfigurableListableBeanFactory used to configure and initialize this Declarable GemFire
* component.
* @param parameters Properties instance containing the parameters from GemFire's configuration file
* (e.g. cache.xml) to configure and initialize this Declarable object.
* @throws IllegalArgumentException if the bean-name parameter was specified in GemFire configuration meta-data
* but no bean with the specified name could be found in the Spring context.
* @see #init(java.util.Properties)
* @see #doPostInit(java.util.Properties)
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.wiring.BeanWiringInfo
* @see org.springframework.beans.factory.wiring.BeanWiringInfoResolver
*/
void doInit(final BeanFactory beanFactory, final Properties parameters) {
synchronized (this) {
if (isNotInitialized()) {
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();
beanConfigurer.setBeanFactory(beanFactory);
final String templateBeanName = parameters.getProperty(BEAN_NAME_PARAMETER);
if (StringUtils.hasText(templateBeanName)) {
if (beanFactory.containsBean(templateBeanName)) {
beanConfigurer.setBeanWiringInfoResolver(new BeanWiringInfoResolver() {
@Override public BeanWiringInfo resolveWiringInfo(final Object beanInstance) {
return new BeanWiringInfo(templateBeanName);
}
});
}
else {
throw new IllegalArgumentException(String.format(
"No bean with name '%1$s' was found in the Spring context '%2$s'.", templateBeanName, beanFactory));
}
}
beanConfigurer.afterPropertiesSet();
beanConfigurer.configureBean(this);
beanConfigurer.destroy();
initialized = true;
}
}
doPostInit(parameters);
}
/**
* Default no operation method performed post initialization of this Declarable GemFire component to be overridden
* by subclasses for application specific extension and behavior.
*
* @param parameters Properties instance containing the parameters from GemFire's configuration file
* (e.g. cache.xml) to configure and initialize this Declarable object.
* @see #doInit(BeanFactory, Properties)
*/
protected void doPostInit(final Properties parameters) {
}
/**
* Initialization method called by GemFire with configured parameters once this Declarable object has been
* constructed during GemFire startup using an &lt;initalizer&gt; element in GemFire's configuration meta-data.
*
* @param parameters the configured parameters passed from the GemFire configuration (e.g. cache.xml) to this
* Declarable as a Properties instance.
* @throws IllegalStateException if this Declarable object has already been configured/initialized
* by the Spring container and is currently active.
* @see #doInit(BeanFactory, Properties)
* @see java.util.Properties
*/
@Override
public final void init(final Properties parameters) {
parametersReference.set(parameters);
try {
doInit(locateBeanFactory(getFactoryKey()), nullSafeGetParameters());
}
catch (IllegalStateException ignore) {
// the BeanFactory does not exist or has been released and or closed, so ignore
}
}
/**
* Determines whether this Declarable object has been configured and initialized (i.e. the doInit method
* has been called) by the Spring container.
*
* @return a boolean value indicating whether this Declarable object has been configured and initialized
* by the Spring container.
* @see #assertInitialized()
* @see #doInit(BeanFactory, Properties)
*/
protected boolean isInitialized() {
return initialized;
}
/**
* Determines whether this Declarable object has been configured and initialized (i.e. the doInit method
* has been called) by the Spring container.
*
* @return a boolean value indicating whether this Declarable object has been configured and initialized
* by the Spring container.
* @see #doInit(BeanFactory, Properties)
* @see #isInitialized()
*/
protected boolean isNotInitialized() {
return !isInitialized();
}
/**
* Locates an existing Spring BeanFactory.
*
* @param factoryKey the key used to locate (lookup) the Spring BeanFactory.
* @return a reference to the Spring BeanFactory if it exists.
* @throws IllegalStateException if the BeanFactory has already been released or closed.
* @see org.springframework.data.gemfire.GemfireBeanFactoryLocator
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory locateBeanFactory(String factoryKey) {
if (beanFactoryReference == null) {
beanFactoryReference = new GemfireBeanFactoryLocator().useBeanFactory(factoryKey);
}
return beanFactoryReference.getFactory();
}
/**
* Null-safe operation to return the parameters passed to this Declarable object when created by GemFire from it's
* configuration meta-data.
*
* @return a Properties object containing the a parameters specified for this Declarable, or an
* empty Properties object if no parameters were supplied.
* @see java.util.Properties
*/
protected Properties nullSafeGetParameters() {
Properties parameters = parametersReference.get();
return (parameters != null ? parameters : new Properties());
}
/**
* Event handler method called when GemFire has created and initialized (refreshed) the Spring ApplicationContext
* using the SpringContextBootstrappingInitializer Declarable class.
*
* @param event the ContextRefreshedEvent published by the Spring ApplicationContext after it is successfully
* created and initialized by GemFire.
* @throws IllegalArgumentException if the ApplicationContext is not an instance of ConfigurableApplicationContext.
* @see #doInit(BeanFactory, Properties)
* @see #nullSafeGetParameters()
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
* @see org.springframework.context.event.ContextRefreshedEvent
*/
@Override
public final void onApplicationEvent(final ContextRefreshedEvent event) {
Assert.isTrue(event.getApplicationContext() instanceof ConfigurableApplicationContext, String.format(
"The Spring ApplicationContext (%1$s) must be an instance of ConfigurableApplicationContext",
ObjectUtils.nullSafeClassName(event.getApplicationContext())));
doInit(((ConfigurableApplicationContext) event.getApplicationContext()).getBeanFactory(),
nullSafeGetParameters());
}
/**
* When this bean gets destroyed by the Spring container, make sure this component gets unregistered from the
* SpringContextBootstrappingInitializer.
*
* @throws Exception if bean destruction is unsuccessful.
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* #unregister(org.springframework.context.ApplicationListener)
*/
@Override
public void destroy() throws Exception {
SpringContextBootstrappingInitializer.unregister(this);
beanFactoryReference.release();
parametersReference.set(null);
initialized = false;
}
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2010-2013 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;
import java.util.Properties;
import org.apache.geode.cache.Declarable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
import org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
import org.springframework.util.StringUtils;
/**
* Dedicated {@link Declarable} support class for wiring the declaring instance through the Spring container.
*
* <p>This implementation first looks for a 'bean-name' property which will be used to locate a 'template'
* bean definition. Autowiring will be performed, based on the settings defined in the Spring container.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.wiring.BeanWiringInfo
* @see org.springframework.data.gemfire.DeclarableSupport
* @see org.springframework.data.gemfire.LazyWiringDeclarableSupport
* @see org.apache.geode.cache.Declarable
* @deprecated please use LazyWiringDeclarableSupport instead.
*/
@Deprecated
public class WiringDeclarableSupport extends DeclarableSupport {
private static final String BEAN_NAME_PROPERTY = "bean-name";
@Override
protected void initInstance(Properties parameters) {
BeanFactory beanFactory = getBeanFactory();
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();
beanConfigurer.setBeanFactory(beanFactory);
final String beanName = parameters.getProperty(BEAN_NAME_PROPERTY);
if (StringUtils.hasText(beanName)) {
if (!beanFactory.containsBean(beanName)) {
throw new IllegalArgumentException(String.format("Cannot find bean named '%1$s'", beanName));
}
beanConfigurer.setBeanWiringInfoResolver(new BeanWiringInfoResolver() {
public BeanWiringInfo resolveWiringInfo(Object beanInstance) {
return new BeanWiringInfo(beanName);
}
});
}
beanConfigurer.afterPropertiesSet();
beanConfigurer.configureBean(this);
beanConfigurer.destroy();
}
}

View File

@@ -28,7 +28,6 @@ import org.apache.geode.cache.control.ResourceManager;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.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;
@@ -236,7 +235,7 @@ public @interface CacheServerApplication {
SubscriptionEvictionPolicy subscriptionEvictionPolicy() default SubscriptionEvictionPolicy.NONE;
/**
* Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup
* Determines whether the Spring {@link BeanFactory} locator 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.
*

View File

@@ -27,10 +27,10 @@ import java.lang.annotation.Target;
import org.apache.geode.cache.client.PoolFactory;
import org.apache.geode.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;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator;
/**
* The {@link ClientCacheApplication} annotation enables a Spring Data GemFire based application to become
@@ -259,7 +259,7 @@ public @interface ClientCacheApplication {
boolean threadLocalConnections() default PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS;
/**
* Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup
* Determines whether the {@link GemfireBeanFactoryLocator} 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.
*

View File

@@ -26,9 +26,9 @@ import java.lang.annotation.Target;
import org.apache.geode.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.support.GemfireBeanFactoryLocator;
/**
* The {@link PeerCacheApplication} annotation enables an embedded GemFire peer {@link org.apache.geode.cache.Cache}
@@ -131,7 +131,7 @@ public @interface PeerCacheApplication {
int searchTimeout() default 300;
/**
* Determines whether the Spring {@link BeanFactoryLocator} should be enabled to lookup
* Determines whether the {@link GemfireBeanFactoryLocator} 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.
*

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2016 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.support;
import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.newBeanFactoryLocator;
import org.apache.geode.cache.CacheCallback;
import org.apache.geode.cache.Declarable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.gemfire.RegionFactoryBean;
/**
* Convenience class for Spring-aware GemFire {@link Declarable} components. Provides subclasses with a reference
* to the current Spring {@link BeanFactory} in orde to perform Spring bean lookups or resource loading.
*
* Note, in most cases, the developer should just declare the same components as Spring beans in the Spring container,
* through {@link RegionFactoryBean}, which gives access to the full Spring container capabilities and does not
* enforce the {@link Declarable} interface to be implemented.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
* @see org.apache.geode.cache.CacheCallback
* @see org.apache.geode.cache.Declarable
*/
@SuppressWarnings("unused")
public abstract class DeclarableSupport implements CacheCallback, Declarable {
private String beanFactoryKey = null;
/**
* Returns a reference to the Spring {@link BeanFactory}.
*
* @return a reference to the Spring {@link BeanFactory}.
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator#useBeanFactory(String)
* @see org.springframework.beans.factory.BeanFactory
* @see #locateBeanFactory()
*/
protected BeanFactory getBeanFactory() {
return locateBeanFactory();
}
/**
* Set the key used to lookup the Spring {@link BeanFactory}.
*
* @param beanFactoryKey {@link String} containing the key used to lookup the Spring {@link BeanFactory}.
*/
public void setBeanFactoryKey(String beanFactoryKey) {
this.beanFactoryKey = beanFactoryKey;
}
/**
* Returns the key used to lookup the Spring {@link BeanFactory}.
*
* @return a {@link String} containing the key used to lookup the Spring {@link BeanFactory}.
*/
protected String getBeanFactoryKey() {
return this.beanFactoryKey;
}
/**
* Returns a reference to the Spring {@link BeanFactory}.
*
* @return a reference to the Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
* @see #locateBeanFactory(String)
* @see #getBeanFactoryKey()
*/
protected BeanFactory locateBeanFactory() {
return locateBeanFactory(getBeanFactoryKey());
}
/**
* Returns a reference to the Spring {@link BeanFactory} for the given {@code beanFactoryKey}.
*
* @param beanFactoryKey {@link String} containing the key used to lookup the Spring {@link BeanFactory}.
* @return a reference to the Spring {@link BeanFactory} for the given {@code beanFactoryKey}.
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator#useBeanFactory(String)
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory locateBeanFactory(String beanFactoryKey) {
return newBeanFactoryLocator().useBeanFactory(beanFactoryKey);
}
/**
* @inheritDoc
*/
@Override
public void close() {
}
}

View File

@@ -0,0 +1,389 @@
/*
* Copyright 2016 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.support;
import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.BeanFactoryReference.newBeanFactoryReference;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeSet;
import static org.springframework.data.gemfire.util.SpringUtils.nullOrEquals;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link GemfireBeanFactoryLocator} class stores a reference to the Spring
* {@link org.springframework.context.ApplicationContext} / {@link BeanFactory} needed to auto-wire
* user application GemFire objects implementing the {@link org.apache.geode.cache.Declarable} interface
* and defined in GemFire's native configuration format (e.g. {@literal cache.xml}.
*
* In most cases, a developer does not need to use this class directly as it is registered by the
* {@link org.springframework.data.gemfire.CacheFactoryBean} when the {@literal useBeanFactoryLocator} property
* is set, and used internally by bothe the {@link WiringDeclarableSupport} and {@link LazyWiringDeclarableSupport}
* SDG classes.
*
* @author Costin Leau
* @author John Blum
* @see LazyWiringDeclarableSupport
* @see WiringDeclarableSupport
*/
@SuppressWarnings("all")
public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAware, DisposableBean, InitializingBean {
// bean alias/name <-> BeanFactory mapping
protected static final ConcurrentMap<String, BeanFactory> BEAN_FACTORIES = new ConcurrentHashMap<>();
private BeanFactory beanFactory;
protected final Log logger = LogFactory.getLog(getClass());
private Set<String> associatedBeanNameWithAliases = Collections.emptySet();
private String associatedBeanName;
/**
* Factory method to construct a new, initialized instance of {@link GemfireBeanFactoryLocator}.
*
* @return a new, initialized instance of the {@link GemfireBeanFactoryLocator}.
* @see GemfireBeanFactoryLocator
* @see #GemfireBeanFactoryLocator()
* @see #afterPropertiesSet()
*/
public static GemfireBeanFactoryLocator newBeanFactoryLocator() {
GemfireBeanFactoryLocator beanFactoryLocator = new GemfireBeanFactoryLocator();
beanFactoryLocator.afterPropertiesSet();
return beanFactoryLocator;
}
/**
* Factory method to construct a new, initialized instance of {@link GemfireBeanFactoryLocator} with the given
* default Spring {@link BeanFactory} and associated Spring bean name.
*
* @param beanFactory reference to the {@link BeanFactory} used to resolve Spring bean references.
* @param associatedBeanName {@link String} contain the name of the Spring bean associated with
* the Spring {@link BeanFactory}.
* @return a new, initialized instance of {@link GemfireBeanFactoryLocator} with the given default
* Spring {@link BeanFactory} and associated Spring bean name.
* @see org.springframework.beans.factory.BeanFactory
* @see GemfireBeanFactoryLocator
* @see #GemfireBeanFactoryLocator()
* @see #setBeanFactory(BeanFactory)
* @see #setBeanName(String)
* @see #afterPropertiesSet()
*/
public static GemfireBeanFactoryLocator newBeanFactoryLocator(BeanFactory beanFactory, String associatedBeanName) {
Assert.isTrue(beanFactory == null || StringUtils.hasText(associatedBeanName),
"associatedBeanName must be specified when BeanFactory is not null");
GemfireBeanFactoryLocator beanFactoryLocator = new GemfireBeanFactoryLocator();
beanFactoryLocator.setBeanFactory(beanFactory);
beanFactoryLocator.setBeanName(associatedBeanName);
beanFactoryLocator.afterPropertiesSet();
return beanFactoryLocator;
}
/**
* Resolves the {@link BeanFactory} mapped to the given {@code beanFactoryKey}.
*
* @param beanFactoryKey {@link String} value containing the key used to lookup the {@link BeanFactory}.
* @return the {@link BeanFactory} mapped to the given key.
* @throws IllegalArgumentException if a Spring {@link BeanFactory} could not be found
* for the given {@code beanFactoryKey}.
* @see org.springframework.beans.factory.BeanFactory
*/
protected static BeanFactory resolveBeanFactory(String beanFactoryKey) {
BeanFactory beanFactory = BEAN_FACTORIES.get(beanFactoryKey);
Assert.isTrue(BEAN_FACTORIES.isEmpty() || beanFactory != null,
String.format("BeanFactory for key [%s] was not found", beanFactoryKey));
return beanFactory;
}
/**
* Resolves a single Spring {@link BeanFactory} from the mapping of registered bean factories.
*
* This class method is synchronized because it contains a "compound action", even though separate actions
* are performed on a {@link ConcurrentMap}, the actions are not independent and therefore must operate
* atomically.
*
* @return a single Spring {@link BeanFactory} from the registry.
* @throws IllegalStateException if the registry contains more than 1, or no
* Spring {@link BeanFactory bean factories}.
* @see org.springframework.beans.factory.BeanFactory
*/
protected static synchronized BeanFactory resolveSingleBeanFactory() {
if (!BEAN_FACTORIES.isEmpty()) {
boolean allTheSameBeanFactory = true;
BeanFactory currentBeanFactory = null;
for (BeanFactory beanFactory : BEAN_FACTORIES.values()) {
allTheSameBeanFactory &= nullOrEquals(currentBeanFactory, beanFactory);
currentBeanFactory = beanFactory;
if (!allTheSameBeanFactory) {
break;
}
}
Assert.state(allTheSameBeanFactory, String.format(
"BeanFactory key must be specified when more than one BeanFactory %s is registered",
new TreeSet(BEAN_FACTORIES.keySet()).toString()));
return BEAN_FACTORIES.values().iterator().next();
}
return null;
}
/**
* Registers all the provided names for given Spring {@link BeanFactory}.
*
* @param names {@link Set} of names and aliases to associate with the Spring {@link BeanFactory}.
* @param beanFactory reference to the Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
* @throws IllegalArgumentException if {@link BeanFactory} is {@literal null}.
* @throws IllegalStateException if one of the provided names is already associated with
* an existing, other than given, Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
*/
protected static synchronized void registerAliases(Set<String> names, BeanFactory beanFactory) {
Assert.isTrue(nullSafeSet(names).isEmpty() || beanFactory != null,
"BeanFactory must not be null when aliases are specified");
for (String name : nullSafeSet(names)) {
BeanFactory existingBeanFactory = BEAN_FACTORIES.putIfAbsent(name, beanFactory);
Assert.isTrue(nullOrEquals(existingBeanFactory, beanFactory),
String.format("BeanFactory reference already exists for key [%s]", name));
}
}
/**
* Removes all Spring {@link BeanFactory} associations/mappings for the given {@link Set} of names.
*
* @param names {@link Set} of names identifying the associations/mappings to remove.
* @return a boolean value indicating whether all associations/mappings were removed successfully.
*/
protected static synchronized boolean unregisterAliases(Set<String> names) {
return BEAN_FACTORIES.keySet().removeAll(names);
}
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() {
BeanFactory beanFactory = getBeanFactory();
registerAliases(resolveAndInitializeBeanNamesWithAliases(beanFactory), beanFactory);
}
/**
* Resolves all names (including aliases) from the given Spring {@link BeanFactory}
* assigned to the {@link #getAssociatedBeanName()}.
*
* @param beanFactory {@link BeanFactory} used to resolve the names assigned to the Spring bean
* identified by the {@link #getAssociatedBeanName()}.
* @return a {@link Set} of all the names assigned to the Spring bean identified by
* the {@link #getAssociatedBeanName()} using the provided Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
* @see #getAssociatedBeanName()
*/
Set<String> resolveAndInitializeBeanNamesWithAliases(BeanFactory beanFactory) {
String associatedBeanName = getAssociatedBeanName();
if (beanFactory != null && StringUtils.hasText(associatedBeanName)) {
String[] beanAliases = beanFactory.getAliases(associatedBeanName);
this.associatedBeanNameWithAliases = new TreeSet<>();
this.associatedBeanNameWithAliases.add(associatedBeanName);
Collections.addAll(this.associatedBeanNameWithAliases, beanAliases);
}
return this.associatedBeanNameWithAliases;
}
/**
* @inheritDoc
*/
@Override
public void destroy() {
unregisterAliases(getAssociatedBeanNameWithAliases());
}
/**
* Attempts to use a single, existing Spring {@link BeanFactory} from the registry based on
* the {@link #setBeanName(String)} beanName} property.
*
* @return the single Spring {@link BeanFactory} from the registry.
* @throws IllegalStateException if the {@link BeanFactory} with the associated
* {@link #setBeanName(String) beanName} is not found.
* @see org.springframework.beans.factory.BeanFactory
* @see #getAssociatedBeanName()
* @see #useBeanFactory(String)
*/
public BeanFactory useBeanFactory() {
return useBeanFactory(getAssociatedBeanName());
}
/**
*
* @param beanFactoryKey
* @return
*/
public BeanFactory useBeanFactory(String beanFactoryKey) {
return newBeanFactoryReference(StringUtils.hasText(beanFactoryKey) ? resolveBeanFactory(beanFactoryKey)
: resolveSingleBeanFactory()).get();
}
/**
* @inheritDoc
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Returns a reference to the {@link BeanFactory} managed by this {@link GemfireBeanFactoryLocator} instance;
* Might be {@literal null} if this {@link GemfireBeanFactoryLocator} is just used to lookup
* an existing {@link BeanFactory} reference.
*
* @return the managed {@link BeanFactory} reference.
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory getBeanFactory() {
return this.beanFactory;
}
/**
* @inheritDoc
*/
@Override
public void setBeanName(String name) {
this.associatedBeanName = name;
}
/**
* Gets the name of the Spring bean associated with the Spring {@link BeanFactory} that possibly created the bean.
*
* @return a {@link String} containing the name of the Spring bean associated with the Spring {@link BeanFactory}.
* @see #setBeanFactory(BeanFactory)
*/
protected String getAssociatedBeanName() {
return this.associatedBeanName;
}
/**
* Returns a {@link Set} of all names and aliases assigned to the Spring bean that is associated with
* the Spring {@link BeanFactory}.
*
* @return a {@link Set} containing all the names and aliases assigned to the Spring bean associated with
* the Spring {@link BeanFactory}.
* @see #setBeanName(String)
*/
protected Set<String> getAssociatedBeanNameWithAliases() {
return Collections.unmodifiableSet(nullSafeSet(this.associatedBeanNameWithAliases));
}
/**
* Builder method to set the bean name used by this locator to lookup a Spring {@link BeanFactory}.
*
* @param beanName {@link String} containing the bean name to set on this locator.
* @return this {@link GemfireBeanFactoryLocator}.
* @see #setBeanName(String)
*/
public GemfireBeanFactoryLocator withBeanName(String beanName) {
setBeanName(beanName);
return this;
}
/**
* Reference holder storing a reference to a Spring {@link BeanFactory}.
*
* @see org.springframework.beans.factory.BeanFactory
*/
protected static class BeanFactoryReference {
protected static final String UNINITIALIZED_BEAN_FACTORY_REFERENCE_MESSAGE =
"A BeanFactory was not initialized; Please verify the useBeanFactoryLocator property was properly set";
private final AtomicReference<BeanFactory> beanFactory = new AtomicReference<>(null);
/**
* Factory method to construct an instance of {@link BeanFactoryReference} initialized
* with the given {@link BeanFactory}.
*
* @param beanFactory {@link BeanFactory} reference to store.
* @return a new instance of {@link BeanFactoryReference} initialized with the given {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
* @see #BeanFactoryReference(BeanFactory)
*/
public static BeanFactoryReference newBeanFactoryReference(BeanFactory beanFactory) {
return new BeanFactoryReference(beanFactory);
}
/**
* Constructs an instance of {@link BeanFactoryReference} initialized with the given {@link BeanFactory}.
*
* @param beanFactory {@link BeanFactory} reference to store; may be {@literal null}.
* @see org.springframework.beans.factory.BeanFactory
*/
public BeanFactoryReference(BeanFactory beanFactory) {
this.beanFactory.set(beanFactory);
}
/**
* Returns the reference to the Spring {@link BeanFactory}.
*
* @return a reference to the Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory
*/
public BeanFactory get() {
BeanFactory beanFactory = this.beanFactory.get();
Assert.state(beanFactory != null, UNINITIALIZED_BEAN_FACTORY_REFERENCE_MESSAGE);
return beanFactory;
}
/**
* Releases the stored reference to the Spring {@link BeanFactory}.
*/
public void release() {
this.beanFactory.set(null);
}
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright 2016 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.support;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.CacheCallback;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.LoaderHelper;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.Assert;
/**
* The {@link LazyWiringDeclarableSupport} class is an implementation of GemFire's {@link Declarable} interface
* that enables support for wiring GemFire components with Spring bean dependencies defined in
* a Spring {@link ApplicationContext}.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* @see org.springframework.data.gemfire.support.WiringDeclarableSupport
* @see org.apache.geode.cache.Declarable
* @since 1.3.4
*/
@SuppressWarnings("unused")
public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSupport
implements ApplicationListener<ContextRefreshedEvent>, DisposableBean {
// atomic reference to the parameters passed by GemFire when this Declarable object
// was constructed, configured and its init method called
private final AtomicReference<Properties> parametersReference = new AtomicReference<>();
// condition to determine the initialized state of this Declarable object
volatile boolean initialized = false;
/**
* Constructs a new instance of the {@link LazyWiringDeclarableSupport} class registered with the
* {@link SpringContextBootstrappingInitializer} as a Spring {@link ApplicationListener}.
*
* This {@link Declarable} object will receive notifications from the {@link SpringContextBootstrappingInitializer}
* when the Spring context is created and initialized (refreshed). The notification is necessary in order for
* this {@link Declarable} object to be properly configured and initialized with any required,
* Spring-defined dependencies.
*
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* #register(org.springframework.context.ApplicationListener)
*/
public LazyWiringDeclarableSupport() {
SpringContextBootstrappingInitializer.register(this);
}
/**
* Asserts that this {@link Declarable} object has been properly configured and initialized by the Spring container
* after has GemFire constructed this {@link Declarable} object during startup.
*
* This method is recommended to be called before any of this {@link Declarable} object's {@link CacheCallback}
* methods (e.g. {@link CacheLoader#load(LoaderHelper)} are invoked in order to ensure that this {@link Declarable}
* object was properly constructed, configured and initialized by the Spring container before hand.
*
* @throws IllegalStateException if this {@link Declarable} object was not been properly constructed, configured
* and initialized by the Spring container.
* @see #init(java.util.Properties)
* @see #isInitialized()
*/
protected void assertInitialized() {
Assert.state(isInitialized(), String.format(
"This Declarable object [%s] has not been properly configured and initialized", getClass().getName()));
}
/**
* Asserts that this {@link Declarable} object has not yet been used, or activated prior to being fully constructed,
* configured and initialized by the Spring container.
*
* It is possible, though rare, that the {@link #init(Properties)} method might be called multiple times by GemFire
* before the Spring container constructs, configures, initializes and generally puts this component to use.
*
* @throws java.lang.IllegalStateException if the Declarable object has already been configured and initialized
* by the Spring container.
* @see #init(java.util.Properties)
* @see #isNotInitialized()
*/
protected void assertUninitialized() {
Assert.state(isNotInitialized(), String.format(
"This Declarable object [%s] has already been configured and initialized", getClass().getName()));
}
/**
* Determines whether this {@link Declarable} object has been properly configured and initialized
* by the Spring container.
*
* @return a boolean value indicating whether this {@link Declarable} object has been properly configured
* and initialized by the Spring container.
* @see #doInit(Properties)
* @see #assertInitialized()
*/
protected boolean isInitialized() {
return this.initialized;
}
/**
* Determines whether this {@link Declarable} object has been properly configured and initialized
* by the Spring container.
*
* @return a boolean value indicating whether this {@link Declarable} object has been properly configured
* and initialized by the Spring container.
* @see #doInit(Properties)
* @see #isInitialized()
*/
protected boolean isNotInitialized() {
return !isInitialized();
}
/**
* Initialization method called by GemFire with the configured parameters once this {@link Declarable} object
* has been constructed by GemFire and the &lt;initalizer&gt; element is parsed
* in GemFire's configuration meta-data during startup.
*
* @param parameters {@link Properties} containing the configured parameters parsed from GemFire's
* configuration meta-data (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see #doInit(Properties)
* @see java.util.Properties
*/
@Override
public final void init(Properties parameters) {
setParameters(parameters);
try {
doInit(locateBeanFactory(), nullSafeGetParameters());
}
catch (IllegalStateException ignore) {
// BeanFactory does not exist, has been closed or the GemfireBeanFactoryLocator is not in use
}
}
/**
* Performs the actual configuration and initialization of this {@link Declarable} object before use.
*
* This method is triggered by the Spring {@link org.springframework.context.ApplicationContext}, Spring application
* {@link ContextRefreshedEvent}) indicating that the Spring container (context) has been created and refreshed.
*
* @param parameters {@link Properties} containing the configured parameters parsed from GemFire's
* configuration meta-data (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @throws IllegalArgumentException if the {@literal bean-name} parameter was specified in GemFire's
* configuration meta-data but no bean with the specified name could be found in the Spring context.
* @see #init(java.util.Properties)
* @see #configureThis(BeanFactory, String)
* @see #doPostInit(java.util.Properties)
* @see java.util.Properties
*/
synchronized void doInit(BeanFactory beanFactory, Properties parameters) {
this.initialized = (isInitialized() || configureThis(beanFactory,
parameters.getProperty(TEMPLATE_BEAN_NAME_PROPERTY)));
doPostInit(parameters);
}
/**
* Performs any post configuration and initialization activities required by the application.
*
* By default, this method does nothing.
*
* @param parameters {@link Properties} containing the configured parameters parsed from GemFire's
* configuration meta-data (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see #doInit(Properties)
* @see java.util.Properties
*/
protected void doPostInit(Properties parameters) {
}
/**
* Null-safe operation to return the parameters passed to this {@link Declarable} object when created by GemFire
* from it's own configuration meta-data (e.g. {@literal cache.xml}).
*
* @return a {@link Properties} containing the configured parameters parsed from GemFire's configuration meta-data
* (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see java.util.Properties
*/
protected Properties nullSafeGetParameters() {
Properties parameters = parametersReference.get();
return (parameters != null ? parameters : new Properties());
}
/**
* Stores a reference to the {@link Properties parameters} passed to the {@link Declarable#init(Properties)} method.
*
* @param parameters {@link Properties} containing the configured parameters parsed from GemFire's
* configuration meta-data (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see java.util.Properties
*/
protected void setParameters(Properties parameters) {
parametersReference.set(parameters);
}
/**
* Event handler method called when GemFire has created and initialized (refreshed)
* the Spring {@link ApplicationContext} using the {@link SpringContextBootstrappingInitializer}.
*
* @param event {@link ContextRefreshedEvent} published by the Spring {@link ApplicationContext} after it is
* successfully created and initialized by GemFire.
* @see org.springframework.context.event.ContextRefreshedEvent
* @see #doInit(BeanFactory, Properties)
* @see #nullSafeGetParameters()
*/
@Override
@SuppressWarnings("all")
public final void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext, String.format(
"The Spring ApplicationContext [%s] must be an instance of ConfigurableApplicationContext",
applicationContext));
ConfigurableListableBeanFactory beanFactory =
((ConfigurableApplicationContext) applicationContext).getBeanFactory();
doInit(beanFactory, nullSafeGetParameters());
}
/**
* When this {@link Declarable} object/bean gets destroyed by the Spring container, {@code destroy()} will
* make sure this component gets unregistered from the {@link SpringContextBootstrappingInitializer} properly.
*
* @throws Exception if bean destruction is unsuccessful.
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* #unregister(org.springframework.context.ApplicationListener)
* @see #setParameters(Properties)
*/
@Override
public void destroy() throws Exception {
SpringContextBootstrappingInitializer.unregister(this);
setParameters(null);
this.initialized = false;
}
}

View File

@@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.Declarable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
@@ -44,11 +45,11 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* The SpringContextBootstrappingInitializer class is a GemFire configuration initializer used to bootstrap a Spring
* ApplicationContext inside a GemFire Server JVM-based process. This enables a GemFire Cache Server resources to be
* mostly configured with Spring Data GemFire's XML namespace. The Cache itself is the only resource that cannot be
* configured and initialized in a Spring context since the initializer is not invoked until after GemFire creates
* and initializes the Cache for use.
* The {@link SpringContextBootstrappingInitializer} class is a GemFire configuration initializer used to bootstrap
* a Spring {@link ApplicationContext} inside a GemFire Server JVM-based process. This enables a GemFire Server
* resource to be mostly configured with Spring Data GemFire's configuration meta-data. The GemFire {@link Cache}
* itself is the only resource that cannot be configured and initialized in a Spring context since the initializer
* is not invoked until after GemFire creates and initializes the GemFire {@link Cache} for use.
*
* @author John Blum
* @see org.springframework.context.ApplicationContext
@@ -58,6 +59,8 @@ import org.springframework.util.StringUtils;
* @see org.springframework.context.event.ApplicationContextEvent
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.support.ClassPathXmlApplicationContext
* @see org.springframework.core.io.DefaultResourceLoader
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Declarable
* @link http://gemfire.docs.pivotal.io/latest/userguide/index.html#basic_config/the_cache/setting_cache_initializer.html
* @link https://jira.springsource.org/browse/SGF-248
@@ -74,13 +77,13 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
private static final ApplicationEventMulticaster applicationEventNotifier = new SimpleApplicationEventMulticaster();
private static final AtomicReference<ClassLoader> beanClassLoaderReference = new AtomicReference<ClassLoader>(null);
private static final AtomicReference<ClassLoader> beanClassLoaderReference = new AtomicReference<>(null);
static volatile ConfigurableApplicationContext applicationContext;
static volatile ContextRefreshedEvent contextRefreshedEvent;
private static final List<Class<?>> registeredAnnotatedClasses = new CopyOnWriteArrayList<Class<?>>();
private static final List<Class<?>> registeredAnnotatedClasses = new CopyOnWriteArrayList<>();
protected final Log logger = initLogger();
@@ -92,7 +95,9 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.ConfigurableApplicationContext
*/
public static synchronized ConfigurableApplicationContext getApplicationContext() {
Assert.state(applicationContext != null, "The Spring ApplicationContext was not configured and initialized properly!");
Assert.state(applicationContext != null,
"A Spring ApplicationContext was not configured and initialized properly");
return applicationContext;
}
@@ -110,7 +115,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
beanClassLoaderReference.set(beanClassLoader);
}
else {
throw new IllegalStateException("The Spring ApplicationContext has already been initialized!");
throw new IllegalStateException("A Spring ApplicationContext has already been initialized");
}
}
@@ -254,8 +259,11 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
"'AnnotatedClasses', 'basePackages' or 'configLocations' must be specified in order to"
+ " construct and configure an instance of the ConfigurableApplicationContext");
Class<?>[] annotatedClasses = registeredAnnotatedClasses.toArray(
new Class<?>[registeredAnnotatedClasses.size()]);
return scanBasePackages(registerAnnotatedClasses(createApplicationContext(configLocations),
registeredAnnotatedClasses.toArray(new Class<?>[registeredAnnotatedClasses.size()])), basePackages);
annotatedClasses), basePackages);
}
/* (non-Javadoc) - used for testing purposes only */
@@ -276,9 +284,11 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
*/
protected ConfigurableApplicationContext initApplicationContext(ConfigurableApplicationContext applicationContext) {
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null");
Assert.notNull(applicationContext, "ConfigurableApplicationContext must not be null");
applicationContext.addApplicationListener(this);
applicationContext.registerShutdownHook();
return setClassLoader(applicationContext);
}
@@ -292,8 +302,10 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
*/
protected ConfigurableApplicationContext refreshApplicationContext(ConfigurableApplicationContext applicationContext) {
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null");
Assert.notNull(applicationContext, "ConfigurableApplicationContext must not be null");
applicationContext.refresh();
return applicationContext;
}
@@ -310,7 +322,10 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
*/
ConfigurableApplicationContext registerAnnotatedClasses(ConfigurableApplicationContext applicationContext,
Class<?>[] annotatedClasses) {
if (applicationContext instanceof AnnotationConfigApplicationContext && !ObjectUtils.isEmpty(annotatedClasses)) {
if (applicationContext instanceof AnnotationConfigApplicationContext
&& !ObjectUtils.isEmpty(annotatedClasses)) {
((AnnotationConfigApplicationContext) applicationContext).register(annotatedClasses);
}
@@ -329,7 +344,10 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
*/
ConfigurableApplicationContext scanBasePackages(ConfigurableApplicationContext applicationContext,
String[] basePackages) {
if (applicationContext instanceof AnnotationConfigApplicationContext && !ObjectUtils.isEmpty(basePackages)) {
if (applicationContext instanceof AnnotationConfigApplicationContext
&& !ObjectUtils.isEmpty(basePackages)) {
((AnnotationConfigApplicationContext) applicationContext).scan(basePackages);
}
@@ -354,17 +372,6 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
return applicationContext;
}
/**
* Null-safe operation used to get the ID of the Spring ApplicationContext.
*
* @param applicationContext the Spring ApplicationContext from which to get the ID.
* @return the ID of the given Spring ApplicationContext or null if the ApplicationContext reference is null.
* @see org.springframework.context.ApplicationContext#getId()
*/
String nullSafeGetApplicationContextId(ApplicationContext applicationContext) {
return (applicationContext != null ? applicationContext.getId() : null);
}
/**
* Initializes a Spring ApplicationContext with the given parameters specified with a GemFire &lt;initializer&gt;
* block in cache.xml.
@@ -380,7 +387,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see java.util.Properties
*/
@Override
public void init(final Properties parameters) {
public void init(Properties parameters) {
try {
synchronized (SpringContextBootstrappingInitializer.class) {
if (applicationContext == null || !applicationContext.isActive()) {
@@ -412,6 +419,17 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
}
}
/**
* Null-safe operation used to get the ID of the Spring ApplicationContext.
*
* @param applicationContext the Spring ApplicationContext from which to get the ID.
* @return the ID of the given Spring ApplicationContext or null if the ApplicationContext reference is null.
* @see org.springframework.context.ApplicationContext#getId()
*/
String nullSafeGetApplicationContextId(ApplicationContext applicationContext) {
return (applicationContext != null ? applicationContext.getId() : null);
}
/**
* Gets notified when the Spring ApplicationContext gets created and refreshed by GemFire, once the
* &lt;initializer&gt; block is processed and the SpringContextBootstrappingInitializer Declarable component
@@ -429,7 +447,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* #multicastEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void onApplicationEvent(final ApplicationContextEvent event) {
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
synchronized (applicationEventNotifier) {
contextRefreshedEvent = (ContextRefreshedEvent) event;
@@ -442,5 +460,4 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
}
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2016 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.support;
import java.util.Properties;
import org.apache.geode.cache.Declarable;
import org.apache.shiro.util.Assert;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
import org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.util.StringUtils;
/**
* {@link Declarable} support class used to wire declaring, implementing instances through the Spring container.
*
* This implementation first looks for a {@literal 'bean-name'} property, which will be used to locate
* a Spring bean definition used as the 'template' for auto-wiring purposes. Auto-wiring will be performed
* based on the settings defined in the Spring container.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.wiring.BeanWiringInfo
* @see DeclarableSupport
* @see LazyWiringDeclarableSupport
* @see org.apache.geode.cache.Declarable
*/
@SuppressWarnings("unused")
public abstract class WiringDeclarableSupport extends DeclarableSupport {
protected static final String TEMPLATE_BEAN_NAME_PROPERTY = "bean-name";
/**
* @inheritDoc
*/
@Override
public void init(Properties parameters) {
configureThis(parameters.getProperty(TEMPLATE_BEAN_NAME_PROPERTY));
}
/**
* Configures this {@link Declarable} object using the bean defined and identified in the Spring {@link BeanFactory}
* with the given {@code name} used as a template for auto-wiring purposes.
*
* @param templateBeanName {@link String} containing the name of the Spring bean used as a template
* for auto-wiring purposes.
* @return a boolean value indicating whether this {@link Declarable} object was successfully configured
* and initialized by the Spring container.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see #configureThis(BeanFactory, String)
* @see #locateBeanFactory()
*/
protected boolean configureThis(String templateBeanName) {
return configureThis(locateBeanFactory(), templateBeanName);
}
/**
* Configures this {@link Declarable} object using the bean defined and identified in the given
* Spring {@link BeanFactory} with the given {@code name} used as a template for auto-wiring purposes.
*
* @param beanFactory Spring {@link BeanFactory} used to configure, auto-wire
* and initialize this {@link Declarable} object.
* @param templateBeanName {@link String} containing the name of the Spring bean used as a template
* for auto-wiring purposes.
* @return a boolean value indicating whether this {@link Declarable} object was successfully configured
* and initialized by the Spring container.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see #newBeanConfigurer(BeanFactory, String)
*/
protected boolean configureThis(BeanFactory beanFactory, String templateBeanName) {
BeanConfigurerSupport beanConfigurer = newBeanConfigurer(beanFactory, templateBeanName);
beanConfigurer.configureBean(this);
beanConfigurer.destroy();
return true;
}
/**
* Constructs a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
*
* @param beanFactory reference to the Spring {@link BeanFactory}.
* @return a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.BeanFactory
* @see #newBeanConfigurer(BeanFactory, String)
*/
protected BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory) {
return newBeanConfigurer(beanFactory, null);
}
/**
* Constructs a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory} and name of a Spring bean defined in the Spring {@link BeanFactory}
* used as a template to wire this {@link Declarable} object.
*
* @param beanFactory reference to the Spring {@link BeanFactory}.
* @param templateBeanName {@link String} containing the name of a Spring bean in the Spring {@link BeanFactory}
* used as a template to wire this {@link Declarable} object.
* @return a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory, String templateBeanName) {
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();
beanConfigurer.setBeanFactory(beanFactory);
if (StringUtils.hasText(templateBeanName)) {
Assert.isTrue(beanFactory.containsBean(templateBeanName),
String.format("Cannot find bean with name [%s]", templateBeanName));
beanConfigurer.setBeanWiringInfoResolver(beanInstance -> new BeanWiringInfo(templateBeanName));
}
beanConfigurer.afterPropertiesSet();
return beanConfigurer;
}
}

View File

@@ -63,6 +63,16 @@ public abstract class SpringUtils {
return (obj1 == null ? obj2 == null : obj1.equals(obj2));
}
/* (non-Javadoc) */
public static boolean nullOrEquals(Object obj1, Object obj2) {
return (obj1 == null || obj1.equals(obj2));
}
/* (non-Javadoc) */
public static boolean nullSafeEquals(Object obj1, Object obj2) {
return (obj1 != null && obj1.equals(obj2));
}
/* (non-Javadoc) */
public static String dereferenceBean(String beanName) {
return String.format("%1$s%2$s", BeanFactory.FACTORY_BEAN_PREFIX, beanName);