From 4684a57952eae7829458db72ed29e97fe8a70244 Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 2 Jun 2016 17:12:02 -0700 Subject: [PATCH] SGF-499 - Prevent SDG-defined Pools from being destroyed before the Regions that use them. (cherry picked from commit 1bca49630997ed3a331428704de1931dc4c967c9) Signed-off-by: John Blum --- .../data/gemfire/RegionFactoryBean.java | 14 +- .../client/ClientRegionFactoryBean.java | 8 +- ...RegionAndPoolBeanFactoryPostProcessor.java | 105 +++++++++ .../config/GemfireDataNamespaceHandler.java | 4 +- .../data/gemfire/config/PoolParser.java | 18 ++ ...mfireRepositoryConfigurationExtension.java | 40 ++-- .../support/GemfireRepositoryFactoryBean.java | 24 +- .../gemfire/client/ClientCachePoolTests.java | 1 - ...nAndPoolBeanFactoryPostProcessorTests.java | 215 ++++++++++++++++++ .../data/gemfire/config/PoolParserTest.java | 2 + .../client/ClientCachePoolTests-context.xml | 10 +- 11 files changed, 397 insertions(+), 44 deletions(-) create mode 100644 src/main/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessor.java create mode 100644 src/test/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessorTests.java diff --git a/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java index ac20e0a7..0b6cb503 100644 --- a/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/RegionFactoryBean.java @@ -505,19 +505,22 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean region = getObject(); + + if (region != null) { if (close) { - if (!getRegion().getRegionService().isClosed()) { + if (!region.getRegionService().isClosed()) { try { - getRegion().close(); + region.close(); } catch (Exception ignore) { } } } + if (destroy) { - getRegion().destroyRegion(); + region.destroyRegion(); } } } @@ -550,7 +553,8 @@ public abstract class RegionFactoryBean extends RegionLookupFactoryBean region = getRegion(); + return (region != null ? region.getAttributes() : attributes); } /** diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java index f6d5dea9..c856a0ee 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -31,7 +31,6 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import com.gemstone.gemfire.cache.CacheClosedException; import com.gemstone.gemfire.cache.CacheListener; import com.gemstone.gemfire.cache.CacheLoader; import com.gemstone.gemfire.cache.CacheWriter; @@ -339,13 +338,12 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean try { region.close(); } - catch (CacheClosedException ignore) { + catch (Exception ignore) { } } } - // TODO perhaps 'destroy' should take precedence over 'close' since 'destroy' is a functional superset - // of 'close' - else if (destroy) { + + if (destroy) { region.destroyRegion(); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessor.java new file mode 100644 index 00000000..ad6ed407 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessor.java @@ -0,0 +1,105 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.client.PoolFactoryBean; + +/** + * The ClientRegionAndPoolBeanFactoryPostProcessor class is a Spring {@link BeanFactoryPostProcessor} that ensures + * a proper dependency is declared between a GemFire client Region and the GemFire Pool it references and uses, if + * the GemFire Pool has been defined and configured in Spring (Data GemFire) configuration meta-data (XML). + * + * @author John Blum + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor + * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory + * @since 1.8.2 + */ +public class ClientRegionAndPoolBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + protected static final String POOL_NAME_PROPERTY = "poolName"; + + /* (non-Javadoc)*/ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + Set clientRegionBeanNames = new HashSet(); + Set poolBeanNames = new HashSet(); + + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + + if (isClientRegionBean(beanDefinition)) { + clientRegionBeanNames.add(beanName); + } + else if (isPoolBean(beanDefinition)) { + poolBeanNames.add(beanName); + } + } + + for (String clientRegionBeanName : clientRegionBeanNames) { + BeanDefinition clientRegionBean = beanFactory.getBeanDefinition(clientRegionBeanName); + String poolName = getPoolName(clientRegionBean); + + if (poolBeanNames.contains(poolName)) { + addDependsOn(clientRegionBean, poolName); + } + } + } + + /* (non-Javadoc)*/ + boolean isClientRegionBean(BeanDefinition beanDefinition) { + return ClientRegionFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()); + } + + /* (non-Javadoc)*/ + boolean isPoolBean(BeanDefinition beanDefinition) { + return PoolFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()); + } + + /* (non-Javadoc) */ + String getPoolName(BeanDefinition clientRegionBean) { + PropertyValue poolNameProperty = clientRegionBean.getPropertyValues().getPropertyValue(POOL_NAME_PROPERTY); + return (poolNameProperty != null ? String.valueOf(poolNameProperty.getValue()) : null); + } + + /* (non-Javadoc) */ + BeanDefinition addDependsOn(BeanDefinition bean, String beanName) { + String[] dependsOn = bean.getDependsOn(); + List dependsOnList = new ArrayList(); + + if (dependsOn != null) { + Collections.addAll(dependsOnList, dependsOn); + } + + dependsOnList.add(beanName); + bean.setDependsOn(dependsOnList.toArray(new String[dependsOnList.size()])); + + return bean; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java index 39dcc5c7..dea27aa3 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java @@ -36,10 +36,10 @@ class GemfireDataNamespaceHandler extends NamespaceHandlerSupport { public void init() { // Repository namespace RepositoryConfigurationExtension extension = new GemfireRepositoryConfigurationExtension(); - registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension)); - registerBeanDefinitionParser("function-executions", new FunctionExecutionBeanDefinitionParser()); registerBeanDefinitionParser("datasource", new GemfireDataSourceParser()); + registerBeanDefinitionParser("function-executions", new FunctionExecutionBeanDefinitionParser()); registerBeanDefinitionParser("json-region-autoproxy", new GemfireRegionAutoProxyParser()); + registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension)); registerBeanDefinitionParser("snapshot-service", new SnapshotServiceParser()); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/PoolParser.java b/src/main/java/org/springframework/data/gemfire/config/PoolParser.java index 4572ac33..54fbf90f 100644 --- a/src/main/java/org/springframework/data/gemfire/config/PoolParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/PoolParser.java @@ -19,6 +19,7 @@ package org.springframework.data.gemfire.config; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -49,6 +50,8 @@ import org.w3c.dom.Element; */ class PoolParser extends AbstractSingleBeanDefinitionParser { + static final AtomicBoolean INFRASTRUCTURE_REGISTRATION = new AtomicBoolean(false); + protected static final int DEFAULT_LOCATOR_PORT = GemfireUtils.DEFAULT_LOCATOR_PORT; protected static final int DEFAULT_SERVER_PORT = GemfireUtils.DEFAULT_CACHE_SERVER_PORT; @@ -62,6 +65,18 @@ class PoolParser extends AbstractSingleBeanDefinitionParser { protected static final String SERVER_ELEMENT_NAME = "server"; protected static final String SERVERS_ATTRIBUTE_NAME = "servers"; + /* (non-Javadoc) */ + static void registerSupportingInfrastructureComponents(ParserContext parserContext) { + if (INFRASTRUCTURE_REGISTRATION.compareAndSet(false, true)) { + AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder + .genericBeanDefinition(ClientRegionAndPoolBeanFactoryPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .getBeanDefinition(); + + BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, parserContext.getRegistry()); + } + } + @Override protected Class getBeanClass(Element element) { return PoolFactoryBean.class; @@ -69,6 +84,9 @@ class PoolParser extends AbstractSingleBeanDefinitionParser { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + registerSupportingInfrastructureComponents(parserContext); + ParsingUtils.setPropertyValue(element, builder, "free-connection-timeout"); ParsingUtils.setPropertyValue(element, builder, "idle-timeout"); ParsingUtils.setPropertyValue(element, builder, "keep-alive"); diff --git a/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtension.java index deaf2669..2dfcaf09 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtension.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.gemfire.repository.config; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -28,18 +29,20 @@ import org.springframework.data.repository.config.XmlRepositoryConfigurationSour import org.springframework.util.StringUtils; /** - * {@link RepositoryConfigurationExtension} implementation to add Gemfire specific extensions to the repository XML - * namespace and annotation based configuration. + * {@link RepositoryConfigurationExtension} implementation handling GemFire specific extensions to the Repository XML + * namespace and annotation-based configuration meta-data. * * @author Oliver Gierke * @author John Blum + * @see org.springframework.data.repository.config.RepositoryConfigurationExtension */ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { - private static final String ANNOTATION_MAPPING_CONTEXT_REF = "mappingContextRef"; + private static final String GEMFIRE_MODULE_PREFIX = "gemfire"; private static final String MAPPING_CONTEXT_PROPERTY_NAME = "gemfireMappingContext"; - private static final String XML_MAPPING_CONTEXT_REF = "mapping-context-ref"; - + private static final String MAPPING_CONTEXT_REF_ANNOTATION_ATTRIBUTE = "mappingContextRef"; + private static final String MAPPING_CONTEXT_REF_XML_ATTRIBUTE = "mapping-context-ref"; + static final String DEFAULT_MAPPING_CONTEXT_BEAN_NAME = String.format("%1$s.%2$s", GemfireMappingContext.class.getName(), "DEFAULT"); @@ -58,7 +61,7 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura */ @Override protected String getModulePrefix() { - return "gemfire"; + return GEMFIRE_MODULE_PREFIX; } /* @@ -69,12 +72,10 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) { super.registerBeansForRoot(registry, configurationSource); - - String attribute = configurationSource.getAttribute(ANNOTATION_MAPPING_CONTEXT_REF); - if (!StringUtils.hasText(attribute)) { + if (!StringUtils.hasText(configurationSource.getAttribute(MAPPING_CONTEXT_REF_ANNOTATION_ATTRIBUTE))) { registry.registerBeanDefinition(DEFAULT_MAPPING_CONTEXT_BEAN_NAME, - new RootBeanDefinition(GemfireMappingContext.class)); + new RootBeanDefinition(GemfireMappingContext.class)); } } @@ -83,10 +84,9 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) */ @Override - public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { - - String mappingContextRef = config.getAttribute(ANNOTATION_MAPPING_CONTEXT_REF); - builder.addPropertyReference(MAPPING_CONTEXT_PROPERTY_NAME, getDefaultedMappingContextBeanName(mappingContextRef)); + public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource configurationSource) { + builder.addPropertyReference(MAPPING_CONTEXT_PROPERTY_NAME, resolveMappingContextBeanName( + configurationSource.getAttribute(MAPPING_CONTEXT_REF_ANNOTATION_ATTRIBUTE))); } /* @@ -94,13 +94,13 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.XmlRepositoryConfigurationSource) */ @Override - public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) { - - String mappingContextRef = config.getElement().getAttribute(XML_MAPPING_CONTEXT_REF); - builder.addPropertyReference(MAPPING_CONTEXT_PROPERTY_NAME, getDefaultedMappingContextBeanName(mappingContextRef)); + public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource configurationSource) { + builder.addPropertyReference(MAPPING_CONTEXT_PROPERTY_NAME, resolveMappingContextBeanName( + configurationSource.getElement().getAttribute(MAPPING_CONTEXT_REF_XML_ATTRIBUTE))); } - private static String getDefaultedMappingContextBeanName(String source) { - return StringUtils.hasText(source) ? source : DEFAULT_MAPPING_CONTEXT_BEAN_NAME; + /* (non-Javadoc) */ + private static String resolveMappingContextBeanName(String source) { + return (StringUtils.hasText(source) ? source : DEFAULT_MAPPING_CONTEXT_BEAN_NAME); } } diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java index 98634c88..09ae7927 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java @@ -56,7 +56,7 @@ public class GemfireRepositoryFactoryBean, S, ID ext private Iterable> regions; - private MappingContext, GemfirePersistentProperty> context; + private MappingContext, GemfirePersistentProperty> mappingContext; /** * Sets a reference to the Spring {@link ApplicationContext} in which this object runs. @@ -81,11 +81,12 @@ public class GemfireRepositoryFactoryBean, S, ID ext */ public void setGemfireMappingContext(MappingContext, GemfirePersistentProperty> mappingContext) { setMappingContext(mappingContext); - this.context = mappingContext; + this.mappingContext = mappingContext; } /** - * Gets a reference to the {@link MappingContext} used to perform domain object type to store mappings. + * Returns a reference to the Spring Data {@link MappingContext} used to perform domain object type + * to data store mappings. * * @return a reference to the {@link MappingContext}. * @see org.springframework.data.gemfire.mapping.GemfireMappingContext @@ -93,11 +94,17 @@ public class GemfireRepositoryFactoryBean, S, ID ext * @see #setGemfireMappingContext(MappingContext) */ protected MappingContext, GemfirePersistentProperty> getGemfireMappingContext() { - return this.context; + return this.mappingContext; } - Iterable> getRegions() { - return regions; + /** + * Returns an {@link Iterable} reference to the GemFire {@link Region}s defined + * in the Spring {@link ApplicationContext}. + * + * @return a reference to all GemFire {@link Region}s defined in the Spring {@link ApplicationContext}. + */ + protected Iterable> getRegions() { + return this.regions; } /** @@ -108,7 +115,7 @@ public class GemfireRepositoryFactoryBean, S, ID ext */ @Override protected RepositoryFactorySupport createRepositoryFactory() { - return new GemfireRepositoryFactory(regions, context); + return new GemfireRepositoryFactory(getRegions(), getGemfireMappingContext()); } /* @@ -117,8 +124,7 @@ public class GemfireRepositoryFactoryBean, S, ID ext */ @Override public void afterPropertiesSet() { - Assert.state(context != null, "GemfireMappingContext must not be null!"); + Assert.state(getGemfireMappingContext() != null, "GemfireMappingContext must not be null"); super.afterPropertiesSet(); } - } diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCachePoolTests.java b/src/test/java/org/springframework/data/gemfire/client/ClientCachePoolTests.java index 212bfa37..d03f1aeb 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientCachePoolTests.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCachePoolTests.java @@ -104,5 +104,4 @@ public class ClientCachePoolTests extends AbstractGemFireClientServerIntegration public void close() { } } - } diff --git a/src/test/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessorTests.java b/src/test/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessorTests.java new file mode 100644 index 00000000..ecf06221 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/ClientRegionAndPoolBeanFactoryPostProcessorTests.java @@ -0,0 +1,215 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.config; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.internal.matchers.VarargMatcher; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.client.PoolFactoryBean; + +/** + * The ClientRegionAndPoolBeanFactoryPostProcessorTests class is a test suite of test cases testing the contract + * and functionality of the {@link ClientRegionAndPoolBeanFactoryPostProcessor} class. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.mockito.runners.MockitoJUnitRunner + * @see org.springframework.data.gemfire.config.ClientRegionAndPoolBeanFactoryPostProcessor + * @since 1.8.2 + */ +@RunWith(MockitoJUnitRunner.class) +public class ClientRegionAndPoolBeanFactoryPostProcessorTests { + + @Mock + private BeanDefinition mockBeanDefinition; + + private ClientRegionAndPoolBeanFactoryPostProcessor beanFactoryPostProcessor = + new ClientRegionAndPoolBeanFactoryPostProcessor(); + + protected static T[] asArray(T... array) { + return array; + } + + protected PropertyValue newPropertyValue(String name, Object value) { + return new PropertyValue(name, value); + } + + protected MutablePropertyValues newPropertyValues(PropertyValue... propertyValues) { + return new MutablePropertyValues(Arrays.asList(propertyValues)); + } + + protected Matcher stringArrayMatcher(String... expected) { + return new ArrayMatcher(expected); + } + + @Test + public void postProcessBeanFactory() { + BeanDefinition mockClientRegionBeanOne = mock(BeanDefinition.class, "MockClientRegionBeanOne"); + BeanDefinition mockClientRegionBeanTwo = mock(BeanDefinition.class, "MockClientRegionBeanTwo"); + BeanDefinition mockPoolBean = mock(BeanDefinition.class, "MockPoolBean"); + + ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class, "MockBeanFactory"); + + when(mockClientRegionBeanOne.getBeanClassName()).thenReturn(ClientRegionFactoryBean.class.getName()); + when(mockClientRegionBeanTwo.getBeanClassName()).thenReturn(ClientRegionFactoryBean.class.getName()); + when(mockPoolBean.getBeanClassName()).thenReturn(PoolFactoryBean.class.getName()); + when(mockBeanDefinition.getBeanClassName()).thenReturn(Object.class.getName()); + when(mockBeanFactory.getBeanDefinitionNames()).thenReturn( + asArray("mockClientRegionBeanOne", "mockClientRegionBeanTwo", "mockGenericBean", "mockPoolBean")); + when(mockBeanFactory.getBeanDefinition(eq("mockClientRegionBeanOne"))).thenReturn(mockClientRegionBeanOne); + when(mockBeanFactory.getBeanDefinition(eq("mockClientRegionBeanTwo"))).thenReturn(mockClientRegionBeanTwo); + when(mockBeanFactory.getBeanDefinition(eq("mockGenericBean"))).thenReturn(mockBeanDefinition); + when(mockBeanFactory.getBeanDefinition(eq("mockPoolBean"))).thenReturn(mockPoolBean); + when(mockClientRegionBeanOne.getDependsOn()).thenReturn(asArray("mockClientCacheBean")); + when(mockClientRegionBeanOne.getPropertyValues()).thenReturn(newPropertyValues(newPropertyValue( + ClientRegionAndPoolBeanFactoryPostProcessor.POOL_NAME_PROPERTY, "mockPoolBean"))); + when(mockClientRegionBeanTwo.getPropertyValues()).thenReturn(newPropertyValues(newPropertyValue( + ClientRegionAndPoolBeanFactoryPostProcessor.POOL_NAME_PROPERTY, "DEFAULT"))); + + beanFactoryPostProcessor.postProcessBeanFactory(mockBeanFactory); + + verify(mockBeanFactory, times(1)).getBeanDefinitionNames(); + verify(mockBeanFactory, times(2)).getBeanDefinition(eq("mockClientRegionBeanOne")); + verify(mockBeanFactory, times(2)).getBeanDefinition(eq("mockClientRegionBeanTwo")); + verify(mockBeanFactory, times(1)).getBeanDefinition(eq("mockGenericBean")); + verify(mockBeanFactory, times(1)).getBeanDefinition(eq("mockPoolBean")); + verify(mockBeanDefinition, times(2)).getBeanClassName(); + verify(mockClientRegionBeanOne, times(1)).getBeanClassName(); + verify(mockClientRegionBeanOne, times(1)).getPropertyValues(); + verify(mockClientRegionBeanOne, times(1)).getDependsOn(); + verify(mockClientRegionBeanOne, times(1)).setDependsOn(argThat(stringArrayMatcher( + "mockClientCacheBean", "mockPoolBean"))); + verify(mockClientRegionBeanTwo, times(1)).getBeanClassName(); + verify(mockClientRegionBeanTwo, times(1)).getPropertyValues(); + verify(mockClientRegionBeanTwo, never()).getDependsOn(); + verify(mockClientRegionBeanTwo, never()).setDependsOn(isA(String[].class)); + verify(mockPoolBean, times(2)).getBeanClassName(); + } + + @Test + public void isClientRegionBeanReturnsFalse() { + when(mockBeanDefinition.getBeanClassName()).thenReturn(Object.class.getName()); + assertThat(beanFactoryPostProcessor.isClientRegionBean(mockBeanDefinition), is(false)); + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void isClientRegionBeanReturnsTrue() { + when(mockBeanDefinition.getBeanClassName()).thenReturn(ClientRegionFactoryBean.class.getName()); + assertThat(beanFactoryPostProcessor.isClientRegionBean(mockBeanDefinition), is(true)); + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void isPoolBeanReturnsFalse() { + when(mockBeanDefinition.getBeanClassName()).thenReturn(Object.class.getName()); + assertThat(beanFactoryPostProcessor.isPoolBean(mockBeanDefinition), is(false)); + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void isPoolBeanReturnsTrue() { + when(mockBeanDefinition.getBeanClassName()).thenReturn(PoolFactoryBean.class.getName()); + assertThat(beanFactoryPostProcessor.isPoolBean(mockBeanDefinition), is(true)); + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void getPoolNameWhenPoolNamePropertyIsUnspecifiedReturnsNull() { + when(mockBeanDefinition.getPropertyValues()).thenReturn(newPropertyValues()); + assertThat(beanFactoryPostProcessor.getPoolName(mockBeanDefinition), is(nullValue(String.class))); + verify(mockBeanDefinition, times(1)).getPropertyValues(); + } + + @Test + public void getPoolNameReturnsPoolBeanName() { + when(mockBeanDefinition.getPropertyValues()).thenReturn(newPropertyValues(newPropertyValue( + ClientRegionAndPoolBeanFactoryPostProcessor.POOL_NAME_PROPERTY, "testPoolName"))); + assertThat(beanFactoryPostProcessor.getPoolName(mockBeanDefinition), is(equalTo("testPoolName"))); + verify(mockBeanDefinition, times(1)).getPropertyValues(); + } + + @Test + public void addDependsOnToExistingDependencies() { + when(mockBeanDefinition.getDependsOn()).thenReturn(asArray("testBeanNameOne", "testBeanNameTwo")); + assertThat(beanFactoryPostProcessor.addDependsOn(mockBeanDefinition, "testBeanNameThree"), + is(sameInstance(mockBeanDefinition))); + verify(mockBeanDefinition, times(1)).getDependsOn(); + verify(mockBeanDefinition, times(1)).setDependsOn(argThat(stringArrayMatcher( + "testBeanNameOne", "testBeanNameTwo", "testBeanNameThree"))); + } + + @Test + public void addDependsOnToNonExistingDependencies() { + when(mockBeanDefinition.getDependsOn()).thenReturn(null); + assertThat(beanFactoryPostProcessor.addDependsOn(mockBeanDefinition, "testBeanName"), + is(sameInstance(mockBeanDefinition))); + verify(mockBeanDefinition, times(1)).getDependsOn(); + verify(mockBeanDefinition, times(1)).setDependsOn(argThat(stringArrayMatcher("testBeanName"))); + } + + protected static final class ArrayMatcher extends BaseMatcher implements VarargMatcher { + + private final T[] expected; + + protected ArrayMatcher(T... expected) { + this.expected = expected; + } + + @Override + public boolean matches(Object item) { + Object[] actual = (item instanceof Object[] ? (Object[]) item : asArray(item)); + + return Arrays.equals(actual, expected); + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("expected (%1$s)", this.expected)); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/PoolParserTest.java b/src/test/java/org/springframework/data/gemfire/config/PoolParserTest.java index b53d9eb2..c406e177 100644 --- a/src/test/java/org/springframework/data/gemfire/config/PoolParserTest.java +++ b/src/test/java/org/springframework/data/gemfire/config/PoolParserTest.java @@ -87,6 +87,8 @@ public class PoolParserTest { @Before public void setup() { + PoolParser.INFRASTRUCTURE_REGISTRATION.set(true); + parser = new PoolParser() { @Override BeanDefinitionRegistry getRegistry(final ParserContext parserContext) { return mockRegistry; diff --git a/src/test/resources/org/springframework/data/gemfire/client/ClientCachePoolTests-context.xml b/src/test/resources/org/springframework/data/gemfire/client/ClientCachePoolTests-context.xml index e12328fe..156e906f 100644 --- a/src/test/resources/org/springframework/data/gemfire/client/ClientCachePoolTests-context.xml +++ b/src/test/resources/org/springframework/data/gemfire/client/ClientCachePoolTests-context.xml @@ -1,18 +1,24 @@ - + + warning + + + - +