From 65ddad967d3adf4cd83b95e2fb02524d22bd3198 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 24 Mar 2020 14:26:12 -0700 Subject: [PATCH] DATAGEODE-314 - Introduce new BeanFactoryPoolResolver implementation. The BeanFactoryPoolResolver implementation resolves a managed, named Pool object (bean) from the Spring BeanFactory (container). --- .../support/BeanFactoryPoolResolver.java | 96 +++++++++ .../BeanFactoryPoolResolverUnitTests.java | 185 ++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 src/main/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolver.java create mode 100644 src/test/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolverUnitTests.java diff --git a/src/main/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolver.java b/src/main/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolver.java new file mode 100644 index 00000000..805d192c --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolver.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 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 + * + * https://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.client.support; + +import org.apache.geode.cache.client.Pool; + +import org.apache.shiro.util.StringUtils; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.data.gemfire.client.PoolResolver; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * {@link PoolResolver} implementation that uses the Spring {@link BeanFactory} to resolve managed {@link Pool} objects. + * This means the {@link Pool} was configured and initialized by the Spring container given the {@link Pool} would be a + * proper bean declaration in this case. + * + * @author John Blum + * @see org.apache.geode.cache.client.Pool + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.BeanFactoryAware + * @see org.springframework.data.gemfire.client.PoolResolver + * @since 2.3.0 + */ +public class BeanFactoryPoolResolver implements BeanFactoryAware, PoolResolver { + + private BeanFactory beanFactory; + + /** + * Constructs a new instance of the {@link BeanFactoryPoolResolver} initialized with + * the given Spring {@link BeanFactory} used to resolve managed {@link Pool} objects. + * + * @param beanFactory Spring {@link BeanFactory} used to resolve managed {@link Pool} objects. + * @see org.springframework.beans.factory.BeanFactory + */ + public BeanFactoryPoolResolver(@NonNull BeanFactory beanFactory) { + setBeanFactory(beanFactory); + } + + /** + * @inheritDoc + */ + @Override + public final void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException { + + Assert.notNull(beanFactory, "BeanFactory must not be null"); + + this.beanFactory = beanFactory; + } + + /** + * Returns a reference to the configured Spring {@link BeanFactory} used to resolve managed {@link Pool} objects. + * + * @return a reference to the configured Spring {@link BeanFactory}. + * @see org.springframework.beans.factory.BeanFactory + */ + protected BeanFactory getBeanFactory() { + return this.beanFactory; + } + + /** + * Resolves the managed, {@link String named} Apache Geode {@link Pool} from the Spring {@link BeanFactory}. + * + * @param poolName {@link String name} of the {@link Pool} to resolve. + * @return the resolved, {@link String named}, managed {@link Pool} object or {@literal null} if no {@link Pool} + * with the given {@link String name} could be resolved from the configured Spring {@link BeanFactory}. + * @see org.apache.geode.cache.client.Pool + * @see #getBeanFactory() + */ + @Nullable @Override + public Pool resolve(@Nullable String poolName) { + + BeanFactory beanFactory = getBeanFactory(); + + return StringUtils.hasText(poolName) && beanFactory.containsBean(poolName) + ? beanFactory.getBean(poolName, Pool.class) + : null; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolverUnitTests.java b/src/test/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolverUnitTests.java new file mode 100644 index 00000000..ba66f61e --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/support/BeanFactoryPoolResolverUnitTests.java @@ -0,0 +1,185 @@ +/* + * Copyright 2020 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 + * + * https://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.client.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +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.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.apache.geode.cache.client.Pool; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; + +/** + * Unit Tests for {@link BeanFactoryPoolResolver}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.mockito.junit.MockitoJUnitRunner + * @see org.apache.geode.cache.client.Pool + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.data.gemfire.client.support.BeanFactoryPoolResolver + * @since 2.3.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class BeanFactoryPoolResolverUnitTests { + + @Mock + private BeanFactory mockBeanFactory; + + private BeanFactoryPoolResolver poolResolver; + + @Mock + private Pool mockPool; + + @Before + public void setup() { + this.poolResolver = new BeanFactoryPoolResolver(this.mockBeanFactory); + } + + @Test + public void constructBeanFactoryPoolResolverWithBeanFactory() { + + BeanFactoryPoolResolver poolResolver = new BeanFactoryPoolResolver(this.mockBeanFactory); + + assertThat(poolResolver).isNotNull(); + assertThat(poolResolver.getBeanFactory()).isEqualTo(this.mockBeanFactory); + } + + @SuppressWarnings("all") + @Test(expected = IllegalArgumentException.class) + public void constructBeanFactoryPoolResolverWithNullThrowsIllegalArgumentException() { + + try { + new BeanFactoryPoolResolver(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("BeanFactory must not be null"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void setAndGetBeanFactory() { + + BeanFactory mockBeanFactoryTwo = mock(BeanFactory.class); + + assertThat(this.poolResolver.getBeanFactory()).isEqualTo(this.mockBeanFactory); + + this.poolResolver.setBeanFactory(mockBeanFactoryTwo); + + assertThat(this.poolResolver.getBeanFactory()).isEqualTo(mockBeanFactoryTwo); + } + + @SuppressWarnings("all") + @Test(expected = IllegalArgumentException.class) + public void setBeanFactoryToNullThrowsIllegalArgumentException() { + + try { + assertThat(this.poolResolver.getBeanFactory()).isEqualTo(this.mockBeanFactory); + this.poolResolver.setBeanFactory(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("BeanFactory must not be null"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + assertThat(this.poolResolver.getBeanFactory()).isEqualTo(this.mockBeanFactory); + } + } + + @Test + public void resolveResolvablePoolByName() { + + when(this.mockBeanFactory.containsBean(eq("TestPool"))).thenReturn(true); + when(this.mockBeanFactory.getBean(eq("TestPool"), eq(Pool.class))).thenReturn(this.mockPool); + + assertThat(this.poolResolver.resolve("TestPool")).isEqualTo(this.mockPool); + + verify(this.mockBeanFactory, times(1)).containsBean(eq("TestPool")); + verify(this.mockBeanFactory, times(1)).getBean(eq("TestPool"), eq(Pool.class)); + verifyZeroInteractions(this.mockPool); + } + + private Pool testResolveUnresolvablePoolByInvalidName(String poolName) { + + try { + return this.poolResolver.resolve(poolName); + } + finally { + verify(this.mockBeanFactory, never()).containsBean(anyString()); + verify(this.mockBeanFactory, never()).getBean(anyString(), any(Pool.class)); + } + } + + @Test + public void resolveUnresolvablePoolByBlankNameReturnsNull() { + assertThat(testResolveUnresolvablePoolByInvalidName(" ")).isNull(); + } + + @Test + public void resolveUnresolvablePoolByEmptyNameReturnsNull() { + assertThat(testResolveUnresolvablePoolByInvalidName("")).isNull(); + } + + @Test + public void resolveUnresolvablePoolByNullNameReturnsNull() { + assertThat(testResolveUnresolvablePoolByInvalidName(null)).isNull(); + } + + @Test + public void resolvePoolWhenBeanFactoryDoesNotContainPoolBeanByNameReturnsNull() { + + when(this.mockBeanFactory.containsBean(anyString())).thenReturn(false); + + assertThat(this.poolResolver.resolve("TestPool")).isNull(); + + verify(this.mockBeanFactory, times(1)).containsBean(eq("TestPool")); + verify(this.mockBeanFactory, never()).getBean(anyString(), eq(Pool.class)); + } + + @Test(expected = BeanNotOfRequiredTypeException.class) + public void resolvePoolWhenBeanFactoryContainsBeanByPoolNameButNotAsAPoolType() { + + when(this.mockBeanFactory.containsBean(anyString())).thenReturn(true); + when(this.mockBeanFactory.getBean(anyString(), eq(Pool.class))) + .thenThrow(new BeanNotOfRequiredTypeException("TestPool", Pool.class, Object.class)); + + this.poolResolver.resolve("TestPool"); + } +}