diff --git a/src/main/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessor.java index 24986433..26f3bddc 100644 --- a/src/main/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessor.java @@ -25,9 +25,14 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.data.gemfire.DiskStoreFactoryBean; import org.springframework.data.gemfire.RegionLookupFactoryBean; import org.springframework.data.gemfire.util.ArrayUtils; +import org.springframework.data.gemfire.wan.AsyncEventQueueFactoryBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import com.gemstone.gemfire.cache.DiskStore; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue; + /** * The PdxDiskStoreAwareBeanFactoryPostProcessor class is a BeanFactoryPostProcessor that modifies all Region bean * definitions in the Spring BeanFactory to form a dependency on the Cache's PDX Disk Store bean. A persistent Region @@ -35,6 +40,7 @@ import org.springframework.util.StringUtils; * having PDX data is loaded from disk. *
* @author John Blum + * @see org.springframework.beans.factory.config.BeanDefinition * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory * @since 1.3.3 @@ -42,28 +48,52 @@ import org.springframework.util.StringUtils; @SuppressWarnings("unused") public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPostProcessor { - private static final String[] EMPTY_STRING_ARRAY = new String[0]; + protected static final String DATA_POLICY_PROPERTY = "dataPolicy"; + protected static final String DATA_POLICY_NAME_PROPERTY = "dataPolicyName"; + protected static final String PERSISTENT_KEYWORD = "PERSISTENT"; + protected static final String PERSISTENT_PROPERTY = "persistent"; + protected static final String SHORTCUT_PROPERTY = "shortcut"; - private String pdxDiskStoreName; + protected static final String[] EMPTY_STRING_ARRAY = new String[0]; - public PdxDiskStoreAwareBeanFactoryPostProcessor() { - } + private final String pdxDiskStoreName; public PdxDiskStoreAwareBeanFactoryPostProcessor(final String pdxDiskStoreName) { - setPdxDiskStoreName(pdxDiskStoreName); + Assert.isTrue(StringUtils.hasText(pdxDiskStoreName), "The name of the PDX Disk Store must be specified!"); + this.pdxDiskStoreName = pdxDiskStoreName; } public String getPdxDiskStoreName() { - Assert.state(!StringUtils.isEmpty(pdxDiskStoreName), "The PDX Disk Store name was not properly initialized!"); return pdxDiskStoreName; } - public final void setPdxDiskStoreName(final String pdxDiskStoreName) { - this.pdxDiskStoreName = pdxDiskStoreName; - } - @Override public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException { + Assert.state(!beanFactory.isConfigurationFrozen(), + "The BeanFactory configuration meta-data is frozen and cannot be modified further!"); + //postProcessPdxDiskStoreDependencies(beanFactory); + postProcessPdxDiskStoreDependencies(beanFactory, AsyncEventQueue.class, DiskStore.class, Region.class); + //postProcessPdxDiskStoreDependencies(beanFactory, AsyncEventQueueFactoryBean.class, DiskStoreFactoryBean.class, + // RegionLookupFactoryBean.class); + } + + private void postProcessPdxDiskStoreDependencies(final ConfigurableListableBeanFactory beanFactory, final Class>... beanTypes) { + for (Class> beanType : beanTypes) { + for (String beanName : beanFactory.getBeanNamesForType(beanType)) { + if (!beanName.equalsIgnoreCase(getPdxDiskStoreName())) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + + if (isDiskStore(beanDefinition) || (isPersistent(beanDefinition) + && !hasDiskStoreReference(beanFactory, beanDefinition))) { + addPdxDiskStoreDependency(beanDefinition); + } + } + } + } + } + + // TODO remove + private void postProcessPdxDiskStoreDependencies(final ConfigurableListableBeanFactory beanFactory) { for (String beanName : beanFactory.getBeanDefinitionNames()) { // NOTE do not add the PDX Disk Store bean dependency to itself! if (!beanName.equalsIgnoreCase(getPdxDiskStoreName())) { @@ -75,7 +105,8 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos // NOTE add the dependency to the Disk Store bean definition in case the Disk Store bean is defined // before the Region that uses it! // TODO what else depends on a PDX Disk Store besides Regions? - if (isPersistentRegion(beanDefinition) || isDiskStore(beanDefinition)) { + if (isDiskStore(beanDefinition) || isPersistentAsyncEventQueue(beanDefinition) + || isPersistentRegion(beanDefinition)) { addPdxDiskStoreDependency(beanDefinition); } } @@ -89,37 +120,131 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos private boolean isDiskStore(final AbstractBeanDefinition beanDefinition) { return (beanDefinition.hasBeanClass() - && DiskStoreFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())); + && (DiskStoreFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) + || DiskStore.class.isAssignableFrom(beanDefinition.getBeanClass())); } + // TODO remove + private boolean isPersistentAsyncEventQueue(final BeanDefinition beanDefinition) { + return (beanDefinition instanceof AbstractBeanDefinition + && isPersistentAsyncEventQueue((AbstractBeanDefinition) beanDefinition)); + } + + // TODO remove + private boolean isPersistentAsyncEventQueue(final AbstractBeanDefinition beanDefinition) { + return (isAsyncEventQueue(beanDefinition) && isPersistent(beanDefinition)); + } + + // TODO remove + private boolean isAsyncEventQueue(final AbstractBeanDefinition beanDefinition) { + return (beanDefinition.hasBeanClass() + && AsyncEventQueueFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())); + } + + // TODO remove private boolean isPersistentRegion(final BeanDefinition beanDefinition) { return (beanDefinition instanceof AbstractBeanDefinition && isPersistentRegion((AbstractBeanDefinition) beanDefinition)); } + // TODO remove private boolean isPersistentRegion(final AbstractBeanDefinition beanDefinition) { return (isRegion(beanDefinition) && isPersistent(beanDefinition)); } + // TODO remove // NOTE Class.isAssignableFrom is not null-safe, hence the AbstractBeanDefinition.hasBeanClass call! private boolean isRegion(final AbstractBeanDefinition beanDefinition) { return (beanDefinition.hasBeanClass() && RegionLookupFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())); } - private boolean isPersistent(final AbstractBeanDefinition beanDefinition) { - PropertyValue persistentPropertyValue = beanDefinition.getPropertyValues().getPropertyValue("persistent"); - - return (persistentPropertyValue != null - && Boolean.parseBoolean(String.valueOf(persistentPropertyValue.getValue()))); + private boolean hasDiskStoreReference(final ConfigurableListableBeanFactory beanFactory, + final BeanDefinition beanDefinition) { + String diskStoreName = getPropertyValue(beanDefinition, "diskStoreName"); + return (StringUtils.hasText(diskStoreName) && beanFactory.containsBeanDefinition(diskStoreName)); } + // TODO will property placeholders be a problem or will the PropertyPlaceholderConfigurer BeanFactoryPostProcessor + // execute before this BeanFactoryPostProcessor??? + private boolean isPersistent(final BeanDefinition beanDefinition) { + boolean persistent = getPropertyValue(beanDefinition, DATA_POLICY_PROPERTY, DATA_POLICY_NAME_PROPERTY) + .contains(PERSISTENT_KEYWORD); + + persistent |= Boolean.parseBoolean(getPropertyValue(beanDefinition, PERSISTENT_PROPERTY)); + + persistent |= getPropertyValue(beanDefinition, SHORTCUT_PROPERTY).contains(PERSISTENT_KEYWORD); + + return persistent; + } + + /** + * Gets the value of a potentially multi-named property on a BeanDefinition returning the first non-null value. + * + * @param beanDefinition the BeanDefinition of the Bean with the property identified by name(s). + * @param propertyNames a String array containing all the possible names of the property. + * @return the first non-null value of the property, which might have multi-names (aliases). + * @see #getPropertyValue(org.springframework.beans.factory.config.BeanDefinition, String) + */ + private String getPropertyValue(final BeanDefinition beanDefinition, final String... propertyNames) { + String propertyValue = null; + + for (String propertyName : propertyNames) { + propertyValue = getPropertyValue(beanDefinition, propertyName); + if (!isNull(propertyValue)) { + break; + } + } + + return String.valueOf(propertyValue); + } + + /** + * Gets the value of the property identified by name from the Bean's BeanDefinition. + * + * @param beanDefinition the BeanDefinition containing the configuration meta-data and values of the properties + * for the Bean. + * @param propertyName a String identifying the property by name. + * @return a String value of the named property for the Bean. + * @see #getPropertyValue(org.springframework.beans.factory.config.BeanDefinition, String...) + */ + private String getPropertyValue(final BeanDefinition beanDefinition, final String propertyName) { + PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue(propertyName); + return String.valueOf(propertyValue != null ? propertyValue.getValue() : null); + } + + /** + * Determines whether the specified String value is null. The String value is null if it is a null references + * or is equal to the "null" String irrespective of case or whitespace. + * + * @param value the String to evaluate for null value. + * @return a boolean indicating whether the String value is null. + */ + private boolean isNull(final String value) { + return (value == null || "null".equalsIgnoreCase(value.trim())); + } + + /** + * Adds the PDX Disk Store dependency to the beginning of the list of dependencies declared by the Bean. + * + * @param beanDefinition the BeanDefinition to add the dependency to the PDX Disk Store on. + * @see org.springframework.beans.factory.config.BeanDefinition#setDependsOn(String[]) + */ private void addPdxDiskStoreDependency(final BeanDefinition beanDefinition) { String[] newDependsOn = (String[]) ArrayUtils.insert(getDependsOn(beanDefinition), 0, getPdxDiskStoreName()); beanDefinition.setDependsOn(newDependsOn); } + /** + * Gets the current list of dependencies declared in the BeanDefinition for the Bean, returning an + * empty String array if the dependsOn property is null. + * + * @param beanDefinition the BeanDefinition of the Bean containing the dependencies. + * @return an array of Bean names that this Bean depends on, or an empty String array if the dependencies + * are undefined. + * @see org.springframework.beans.factory.config.BeanDefinition#getDependsOn() + */ private String[] getDependsOn(final BeanDefinition beanDefinition) { return (beanDefinition.getDependsOn() != null ? beanDefinition.getDependsOn() : EMPTY_STRING_ARRAY); } diff --git a/src/test/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessorTest.java b/src/test/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessorTest.java index f8113cba..5d71f944 100644 --- a/src/test/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessorTest.java +++ b/src/test/java/org/springframework/data/gemfire/config/PdxDiskStoreAwareBeanFactoryPostProcessorTest.java @@ -21,12 +21,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Test; @@ -34,19 +37,24 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.data.gemfire.CacheFactoryBean; -import org.springframework.data.gemfire.DiskStoreFactoryBean; -import org.springframework.data.gemfire.PartitionedRegionFactoryBean; -import org.springframework.data.gemfire.ReplicatedRegionFactoryBean; + +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.DiskStore; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionShortcut; +import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue; +import com.gemstone.gemfire.internal.cache.PartitionedRegion; /** * The PdxDiskStoreAwareBeanFactoryPostProcessorTest class is a test suite of test cases testing the functionality * of the PdxDiskStoreAwareBeanFactoryPostProcessor class. * * @author John Blum - * @see org.mockito.Mockito * @see org.junit.Test + * @see org.mockito.Mockito * @see org.springframework.data.gemfire.config.PdxDiskStoreAwareBeanFactoryPostProcessor * @since 1.3.3 */ @@ -63,17 +71,48 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessorTest { return (array == null || array.length == 0); } + protected static boolean isBeanType(final BeanDefinition beanDefinition, final Class beanType) { + return (beanDefinition instanceof AbstractBeanDefinition + && ((AbstractBeanDefinition) beanDefinition).hasBeanClass() + && beanType.isAssignableFrom(((AbstractBeanDefinition) beanDefinition).getBeanClass())); + } + protected ConfigurableListableBeanFactory createMockBeanFactory(final Map