From 1328fa330074d8528708305dff2ed7f5ee6337f5 Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 3 Dec 2015 17:52:56 -0800 Subject: [PATCH] SGF-450 - GemfireRepositoryFactoryBean needs to explicitly register the default GemfireMappingContext when not explicitly defined as a bean in the application's Spring context. (cherry picked from commit eebe7bb6229a13f70ce7ab8d26798138038f9db7) Signed-off-by: John Blum --- .../support/GemfireRepositoryFactoryBean.java | 126 +++++++-- ...eRepositoryConfigurationExtensionTest.java | 16 ++ .../GemfireRepositoryFactoryBeanTest.java | 99 ------- .../GemfireRepositoryFactoryBeanTest.java | 257 ++++++++++++++++++ 4 files changed, 373 insertions(+), 125 deletions(-) delete mode 100644 src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryFactoryBeanTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBeanTest.java 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 489b7deb..30c6a69e 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 @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.gemfire.repository.support; import java.io.Serializable; @@ -23,6 +24,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.gemfire.mapping.GemfireMappingContext; import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; @@ -30,6 +32,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.util.Assert; import com.gemstone.gemfire.cache.Region; @@ -38,23 +41,37 @@ import com.gemstone.gemfire.cache.Region; * * @author Oliver Gierke * @author John Blum + * @see org.springframework.beans.factory.FactoryBean + * @see org.springframework.context.ApplicationContext + * @see org.springframework.context.ApplicationContextAware + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + * @see org.springframework.data.gemfire.mapping.GemfirePersistentProperty + * @see org.springframework.data.mapping.context.MappingContext + * @see org.springframework.data.repository.Repository + * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport + * @see com.gemstone.gemfire.cache.Region */ @SuppressWarnings("unused") public class GemfireRepositoryFactoryBean, S, ID extends Serializable> extends RepositoryFactoryBeanSupport implements ApplicationContextAware { + protected static final String DEFAULT_MAPPING_CONTEXT_BEAN_NAME = + String.format("%1$s.%2$s", GemfireMappingContext.class.getName(), "DEFAULT"); + private ApplicationContext applicationContext; private Iterable> regions; private MappingContext, GemfirePersistentProperty> context; - /* - * (non-Javadoc) - * - * @see - * org.springframework.context.ApplicationContextAware#setApplicationContext - * (org.springframework.context.ApplicationContext) + /** + * Sets a reference to the Spring {@link ApplicationContext} in which this object runs. + * + * @param applicationContext the Spring {@link ApplicationContext} reference. + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(ApplicationContext) + * @see org.springframework.context.ApplicationContext */ @Override @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -65,39 +82,96 @@ public class GemfireRepositoryFactoryBean, S, ID ext } /** - * Configures the {@link MappingContext} to be used. - * - * @param context the context to set + * Gets a reference to the Spring {@link ApplicationContext} in which this object runs. + * + * @return a reference to the Spring {@link ApplicationContext}. + * @see org.springframework.context.ApplicationContext + * @see #setApplicationContext(ApplicationContext) */ - public void setGemfireMappingContext(MappingContext, GemfirePersistentProperty> context) { - setMappingContext(context); - this.context = context; + protected ApplicationContext getApplicationContext() { + Assert.state(applicationContext != null, "The Spring ApplicationContext was not properly initialized"); + return applicationContext; + } + + /** + * Configures the {@link MappingContext} used to perform domain object type to store mappings. + * + * @param mappingContext the {@link MappingContext} to set. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + * @see org.springframework.data.mapping.context.MappingContext + */ + public void setGemfireMappingContext(MappingContext, GemfirePersistentProperty> mappingContext) { + setMappingContext(mappingContext); + this.context = mappingContext; + } + + /** + * Gets a reference to the {@link MappingContext} used to perform domain object type to store mappings. + * + * @return a reference to the {@link MappingContext}. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + * @see org.springframework.data.mapping.context.MappingContext + * @see #setGemfireMappingContext(MappingContext) + */ + protected MappingContext, GemfirePersistentProperty> getGemfireMappingContext() { + return this.context; + } + + /* (non-Javadoce) */ + Iterable> getRegions() { + return regions; } /* * (non-Javadoc) - * - * @see - * org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport - * #createRepositoryFactory() + * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet() + */ + @Override + public void afterPropertiesSet() { + resolveMappingContext(); + super.afterPropertiesSet(); + } + + /** + * Creates an instance of {@link RepositoryFactorySupport} that interfaces with GemFire. + * + * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory() + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport */ @Override protected RepositoryFactorySupport createRepositoryFactory() { return new GemfireRepositoryFactory(regions, context); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet() + /** + * Resolves the appropriate Spring Data {@link MappingContext} to use for handling entity domain object type + * to store mappings. + * + * @see org.springframework.data.mapping.context.MappingContext + * @see #getApplicationContext() + * @see #setMappingContext(MappingContext) */ - @Override - public void afterPropertiesSet() { - if (this.context == null) { - // TODO register the GemfireMappingContext instance as a (Singleton) bean in the Spring context - setGemfireMappingContext(new GemfireMappingContext()); - } + protected void resolveMappingContext() { + if (getGemfireMappingContext() == null) { + ApplicationContext applicationContext = getApplicationContext(); - super.afterPropertiesSet(); + GemfireMappingContext gemfireMappingContext; + + try { + gemfireMappingContext = applicationContext.getBean(DEFAULT_MAPPING_CONTEXT_BEAN_NAME, + GemfireMappingContext.class); + } + catch (BeansException ignore) { + gemfireMappingContext = new GemfireMappingContext(); + + if (applicationContext instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) applicationContext).getBeanFactory() + .registerSingleton(DEFAULT_MAPPING_CONTEXT_BEAN_NAME, gemfireMappingContext); + } + } + + setGemfireMappingContext(gemfireMappingContext); + } } } diff --git a/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtensionTest.java b/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtensionTest.java index e7db55f8..c3bfdbd2 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtensionTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryConfigurationExtensionTest.java @@ -1,3 +1,19 @@ +/* + * 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.repository.config; import static org.hamcrest.CoreMatchers.equalTo; diff --git a/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryFactoryBeanTest.java deleted file mode 100644 index 4ca0a914..00000000 --- a/src/test/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryFactoryBeanTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.springframework.data.gemfire.repository.config; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.data.gemfire.TestUtils; -import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean; -import org.springframework.data.mapping.context.MappingContext; - -import com.gemstone.gemfire.cache.Region; - -/** - * The GemfireRepositoryFactoryBeanTest class is test suite of test cases testing the contract and functionality - * of the GemfireRepositoryFactoryBean class. - * - * @author John Blum - * @see org.junit.Test - * @see org.mockito.Mockito - * @since 1.6.3 - */ -public class GemfireRepositoryFactoryBeanTest { - - private GemfireRepositoryFactoryBean repositoryFactoryBean; - - @Before - public void setup() { - repositoryFactoryBean = new GemfireRepositoryFactoryBean(); - } - - protected List toList(Iterable iterable) { - List list = new ArrayList(); - - if (iterable != null) { - for (T element : iterable) { - list.add(element); - } - } - - return list; - } - - @Test - public void setApplicationContext() throws Exception { - Map expectedRegions = new HashMap(2); - - expectedRegions.put("regionOne", mock(Region.class)); - expectedRegions.put("regionTwo", mock(Region.class)); - - ApplicationContext mockApplicationContext = mock(ApplicationContext.class); - - when(mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(expectedRegions); - - repositoryFactoryBean.setApplicationContext(mockApplicationContext); - - Iterable actualRegions = TestUtils.readField("regions", repositoryFactoryBean); - - assertThat(actualRegions, is(notNullValue())); - - List regions = toList(actualRegions); - - assertThat(regions.size(), is(equalTo(2))); - assertThat(regions.containsAll(expectedRegions.values()), is(true)); - - ApplicationContext actualApplicationContext = TestUtils.readField("applicationContext", repositoryFactoryBean); - - assertThat(actualApplicationContext, is(sameInstance(mockApplicationContext))); - } - - @Test - @SuppressWarnings("unchecked") - public void setGemfireMappingContextAlsoSetsMappingContext() throws Exception { - MappingContext mockMappingContext = mock(MappingContext.class); - - repositoryFactoryBean.setGemfireMappingContext(mockMappingContext); - - MappingContext actualMappingContext = TestUtils.readField("context", repositoryFactoryBean); - - assertThat(actualMappingContext, is(sameInstance(mockMappingContext))); - - actualMappingContext = TestUtils.readField("mappingContext", repositoryFactoryBean); - - assertThat(actualMappingContext, is(sameInstance(mockMappingContext))); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBeanTest.java new file mode 100644 index 00000000..8743490b --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBeanTest.java @@ -0,0 +1,257 @@ +/* + * 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.repository.support; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Matchers; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.gemfire.TestUtils; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; +import org.springframework.data.mapping.context.MappingContext; + +import com.gemstone.gemfire.cache.Region; + +/** + * The GemfireRepositoryFactoryBeanTest class is test suite of test cases testing the contract and functionality + * of the GemfireRepositoryFactoryBean class. + * + * @author John Blum + * @see org.junit.Rule + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean + * @since 1.6.3 + */ +public class GemfireRepositoryFactoryBeanTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private GemfireRepositoryFactoryBean repositoryFactoryBean; + + @Before + public void setup() { + repositoryFactoryBean = new GemfireRepositoryFactoryBean(); + } + + protected List toList(Iterable iterable) { + List list = new ArrayList(); + + if (iterable != null) { + for (T element : iterable) { + list.add(element); + } + } + + return list; + } + + @Test + @SuppressWarnings("unchecked") + public void setAndGetApplicationContextSuccessfully() { + Map expectedRegions = new HashMap(2); + + expectedRegions.put("regionOne", mock(Region.class)); + expectedRegions.put("regionTwo", mock(Region.class)); + + ApplicationContext mockApplicationContext = mock(ApplicationContext.class); + + when(mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(expectedRegions); + + repositoryFactoryBean.setApplicationContext(mockApplicationContext); + + Iterable actualRegions = repositoryFactoryBean.getRegions(); + + assertThat(actualRegions, is(notNullValue())); + + List regions = toList(actualRegions); + + assertThat(regions.size(), is(equalTo(2))); + assertThat(regions.containsAll(expectedRegions.values()), is(true)); + + ApplicationContext actualApplicationContext = repositoryFactoryBean.getApplicationContext(); + + assertThat(actualApplicationContext, is(sameInstance(mockApplicationContext))); + + verify(mockApplicationContext, times(1)).getBeansOfType(eq(Region.class)); + } + + @Test + public void getNullApplicationContextThrowsIllegalStateException() { + expectedException.expect(IllegalStateException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The Spring ApplicationContext was not properly initialized"); + repositoryFactoryBean.getApplicationContext(); + } + + @Test + @SuppressWarnings("unchecked") + public void setGemfireMappingContextAlsoSetsMappingContext() throws Exception { + MappingContext mockMappingContext = mock(MappingContext.class); + + repositoryFactoryBean.setGemfireMappingContext(mockMappingContext); + + MappingContext actualMappingContext = repositoryFactoryBean.getGemfireMappingContext(); + + assertThat(actualMappingContext, is(sameInstance(mockMappingContext))); + + actualMappingContext = TestUtils.readField("mappingContext", repositoryFactoryBean); + + assertThat(actualMappingContext, is(sameInstance(mockMappingContext))); + } + + @Test + @SuppressWarnings("unchecked") + public void resolveConfiguredMappingContext() { + final MappingContext mockMappingContext = mock(MappingContext.class); + + repositoryFactoryBean = new GemfireRepositoryFactoryBean() { + @Override protected MappingContext, GemfirePersistentProperty> getGemfireMappingContext() { + return mockMappingContext; + } + + @Override public void setGemfireMappingContext(MappingContext mappingContext) { + throw new IllegalStateException(String.format( + "setGemfireMappingContext with (%1$s) should not have been called", mappingContext)); + } + }; + + repositoryFactoryBean.resolveMappingContext(); + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(sameInstance(mockMappingContext))); + } + + @Test + public void resolveMappingContextFromApplicationContext() throws Exception { + final ApplicationContext mockApplicationContext = mock(ApplicationContext.class); + + GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class); + + when(mockApplicationContext.getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), eq( + GemfireMappingContext.class))).thenReturn(mockMappingContext); + + repositoryFactoryBean = new GemfireRepositoryFactoryBean() { + @Override protected ApplicationContext getApplicationContext() { + return mockApplicationContext; + } + }; + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(nullValue())); + + repositoryFactoryBean.resolveMappingContext(); + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(sameInstance( + (MappingContext) mockMappingContext))); + + MappingContext actualMappingContext = TestUtils.readField("mappingContext", repositoryFactoryBean); + + assertThat(actualMappingContext, is(sameInstance((MappingContext) mockMappingContext))); + + verify(mockApplicationContext, times(1)).getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), + eq(GemfireMappingContext.class)); + } + + @Test + public void resolveNewMappingContext() throws Exception { + final ApplicationContext mockApplicationContext = mock(ApplicationContext.class); + + when(mockApplicationContext.getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), eq( + GemfireMappingContext.class))).thenThrow(new NoSuchBeanDefinitionException( + GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME)); + + repositoryFactoryBean = new GemfireRepositoryFactoryBean() { + @Override protected ApplicationContext getApplicationContext() { + return mockApplicationContext; + } + }; + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(nullValue())); + + repositoryFactoryBean.resolveMappingContext(); + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(instanceOf(GemfireMappingContext.class))); + + MappingContext actualMappingContext = TestUtils.readField("mappingContext", repositoryFactoryBean); + + assertThat(actualMappingContext, is(instanceOf(GemfireMappingContext.class))); + + verify(mockApplicationContext, times(1)).getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), + eq(GemfireMappingContext.class)); + } + + @Test + public void resolveNewMappingContextAndRegistersSingleton() throws Exception { + final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class); + + ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class); + + when(mockApplicationContext.getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), eq( + GemfireMappingContext.class))).thenThrow(new NoSuchBeanDefinitionException( + GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME)); + + when(mockApplicationContext.getBeanFactory()).thenReturn(mockBeanFactory); + + repositoryFactoryBean = new GemfireRepositoryFactoryBean() { + @Override protected ApplicationContext getApplicationContext() { + return mockApplicationContext; + } + }; + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(nullValue())); + + repositoryFactoryBean.resolveMappingContext(); + + assertThat(repositoryFactoryBean.getGemfireMappingContext(), is(instanceOf(GemfireMappingContext.class))); + + MappingContext actualMappingContext = TestUtils.readField("mappingContext", repositoryFactoryBean); + + assertThat(actualMappingContext, is(instanceOf(GemfireMappingContext.class))); + + verify(mockApplicationContext, times(1)).getBean(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), + eq(GemfireMappingContext.class)); + verify(mockApplicationContext, times(1)).getBeanFactory(); + verify(mockBeanFactory, times(1)).registerSingleton(eq(GemfireRepositoryFactoryBean.DEFAULT_MAPPING_CONTEXT_BEAN_NAME), + Matchers.isA(GemfireMappingContext.class)); + } + +}