From af35414d07a83f72813fc1d95035e51aa72b50cc Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 20 Sep 2019 01:43:01 -0700 Subject: [PATCH] Improves auto-confiuguration of GemfireTemplates for cache Regions to minimize issues when auto-wiring templates into application components. Resolves gh-55. --- .../RegionTemplateAutoConfiguration.java | 286 +++++++++++++++--- ...lateAutoConfigurationIntegrationTests.java | 9 + ...lateAutoConfigurationIntegrationTests.java | 118 ++++++++ ...lateAutoConfigurationIntegrationTests.java | 9 + ...lateAutoConfigurationIntegrationTests.java | 29 +- ...gionAutoConfigurationIntegrationTests.java | 12 +- ...lateAutoConfigurationIntegrationTests.java | 28 +- ...lateAutoConfigurationIntegrationTests.java | 29 +- 8 files changed, 465 insertions(+), 55 deletions(-) create mode 100644 spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredNonInjectedRegionTemplateAutoConfigurationIntegrationTests.java diff --git a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/RegionTemplateAutoConfiguration.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/RegionTemplateAutoConfiguration.java index c68895f0..d03ebae4 100644 --- a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/RegionTemplateAutoConfiguration.java +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/RegionTemplateAutoConfiguration.java @@ -15,19 +15,30 @@ */ package org.springframework.geode.boot.autoconfigure; -import java.util.Map; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; -import org.apache.geode.internal.concurrent.ConcurrentHashSet; import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.SingletonBeanRegistry; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -38,8 +49,18 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; +import org.springframework.core.type.StandardMethodMetadata; +import org.springframework.data.gemfire.GemfireOperations; import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.data.gemfire.ResolvableRegionFactoryBean; +import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.util.ArrayUtils; import org.springframework.data.gemfire.util.CollectionUtils; +import org.springframework.data.gemfire.util.SpringUtils; +import org.springframework.geode.config.annotation.support.TypelessAnnotationConfigSupport; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -48,13 +69,26 @@ import org.springframework.util.StringUtils; * the Spring {@link ConfigurableApplicationContext} in order to perform {@link Region} data access operations. * * @author John Blum + * @see org.apache.geode.cache.GemFireCache * @see org.apache.geode.cache.Region + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.config.BeanDefinition + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor * @see org.springframework.beans.factory.config.BeanPostProcessor - * @see org.springframework.beans.factory.config.SingletonBeanRegistry + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory + * @see org.springframework.beans.factory.support.BeanDefinitionBuilder + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.boot.autoconfigure.AutoConfigureAfter + * @see org.springframework.boot.autoconfigure.condition.ConditionalOnBean + * @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass + * @see org.springframework.context.ApplicationContext * @see org.springframework.context.ConfigurableApplicationContext * @see org.springframework.context.annotation.Bean * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.event.EventListener * @see org.springframework.data.gemfire.GemfireTemplate + * @see org.springframework.data.gemfire.ResolvableRegionFactoryBean + * @see org.springframework.geode.config.annotation.support.TypelessAnnotationConfigSupport * @since 1.0.0 */ @Configuration @@ -62,79 +96,237 @@ import org.springframework.util.StringUtils; @ConditionalOnBean(GemFireCache.class) @ConditionalOnClass(GemfireTemplate.class) @SuppressWarnings("unused") -public class RegionTemplateAutoConfiguration { +public class RegionTemplateAutoConfiguration extends TypelessAnnotationConfigSupport { - private static final Set regionTemplateNames = new ConcurrentHashSet<>(); + private static final Object NON_BEAN = new Object(); - private String toRegionTemplateName(String regionName) { - return StringUtils.uncapitalize(regionName) + "Template"; + private static final String TEMPLATE = "Template"; + + private final Set autoConfiguredRegionTemplateBeanNames = Collections.synchronizedSet(new HashSet<>()); + private final Set regionNamesWithTemplates = Collections.synchronizedSet(new HashSet<>()); + + @Bean + BeanFactoryPostProcessor regionTemplateBeanFactoryPostProcessor() { + + return beanFactory -> { + + if (beanFactory instanceof BeanDefinitionRegistry) { + + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + + List beanDefinitionNames = + Arrays.asList(ArrayUtils.nullSafeArray(registry.getBeanDefinitionNames(), String.class)); + + Set userRegionTemplateNames = new HashSet<>(); + + for (String beanName : beanDefinitionNames) { + + String regionTemplateBeanName = toRegionTemplateBeanName(beanName); + + if (!beanDefinitionNames.contains(regionTemplateBeanName)) { + + BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); + + Class resolvedBeanType = resolveBeanClass(beanDefinition, registry).orElse(null); + + if (isRegionBeanDefinition(resolvedBeanType)) { + register(newGemfireTemplateBeanDefinition(beanName), regionTemplateBeanName, registry); + } + else if (isGemfireTemplateBeanDefinition(resolvedBeanType)) { + userRegionTemplateNames.add(beanName); + } + else if (isBeanWithGemfireTemplateDependency(beanFactory, beanDefinition)) { + SpringUtils.addDependsOn(beanDefinition, GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME); + } + } + } + + setAutoConfiguredRegionTemplateDependencies(registry, userRegionTemplateNames); + } + }; } + private boolean isBeanWithGemfireTemplateDependency(@NonNull BeanFactory beanFactory, + @NonNull BeanDefinition beanDefinition) { + + Predicate isGemfireTemplate = value -> value instanceof RuntimeBeanReference + ? beanFactory.isTypeMatch(((RuntimeBeanReference) value).getBeanName(), GemfireOperations.class) + : value instanceof GemfireOperations; + + boolean match = beanDefinition.getConstructorArgumentValues().getGenericArgumentValues().stream() + .map(ConstructorArgumentValues.ValueHolder::getValue) + .anyMatch(isGemfireTemplate); + + match |= match || beanDefinition.getPropertyValues().getPropertyValueList().stream() + .map(PropertyValue::getValue) + .anyMatch(isGemfireTemplate); + + match |= match || Optional.of(beanDefinition) + .filter(AnnotatedBeanDefinition.class::isInstance) + .map(AnnotatedBeanDefinition.class::cast) + .map(AnnotatedBeanDefinition::getFactoryMethodMetadata) + .filter(StandardMethodMetadata.class::isInstance) + .map(StandardMethodMetadata.class::cast) + .map(StandardMethodMetadata::getIntrospectedMethod) + .map(method -> Arrays.stream(ArrayUtils.nullSafeArray(method.getParameterTypes(), Class.class)) + .filter(Objects::nonNull) + .anyMatch(GemfireOperations.class::isAssignableFrom) + ).orElse(false); + + return match; + } + + private boolean isGemfireTemplateBeanDefinition(@Nullable Class beanType) { + return beanType != null && GemfireOperations.class.isAssignableFrom(beanType); + } + + private boolean isRegionBeanDefinition(@Nullable Class beanType) { + return beanType != null && ResolvableRegionFactoryBean.class.isAssignableFrom(beanType); + } + + private BeanDefinition newGemfireTemplateBeanDefinition(String regionBeanName) { + + BeanDefinitionBuilder builder = + BeanDefinitionBuilder.genericBeanDefinition(GemfireTemplate.class); + + builder.addConstructorArgReference(regionBeanName); + + return builder.getBeanDefinition(); + } + + // Register BeanDefinition with bean name in BeanDefinitionRegistry + private boolean register(BeanDefinition beanDefinition, String beanName, BeanDefinitionRegistry registry) { + + if (this.autoConfiguredRegionTemplateBeanNames.add(beanName)) { + registry.registerBeanDefinition(beanName, beanDefinition); + return true; + } + + return false; + } + + private void setAutoConfiguredRegionTemplateDependencies(BeanDefinitionRegistry registry, + Set dependencyBeanNames) { + + String[] dependencyBeanNamesArray = dependencyBeanNames.toArray(new String[0]); + + this.autoConfiguredRegionTemplateBeanNames.stream() + .map(registry::getBeanDefinition) + .forEach(beanDefinition -> SpringUtils.addDependsOn(beanDefinition, dependencyBeanNamesArray)); + } + + // Required by @EnableClusterDefinedRegions & Native-Defined Regions (e.g. Regions defined in "cache.xml"). @Bean BeanPostProcessor regionTemplateBeanPostProcessor(ConfigurableApplicationContext applicationContext) { + handlePrematureCacheCreation(applicationContext); + return new BeanPostProcessor() { - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + /** + * User-defined {@link GemfireTemplate} beans should be post processed before + * auto-configured {@link GemfireTemplate} beans! + * + * @see RegionTemplateAutoConfiguration#setAutoConfiguredRegionTemplateDependencies(BeanDefinitionRegistry, Set) + */ + @Nullable @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof Region) { - - String regionTemplateName = toRegionTemplateName(beanName); - - registerRegionTemplateBean(regionTemplateName, bean); + if (bean instanceof GemfireTemplate) { + if (autoConfiguredRegionTemplateBeanNames.contains(beanName)) { + if (regionNamesWithTemplates.contains(((GemfireTemplate) bean).getRegion().getName())) { + // Returning NO_BEAN means an existing, user-defined GemfireTemplate bean already exists + // for the target Region and the auto-configured GemfireTemplate bean is not required. + bean = NON_BEAN; + } + } + else { + regionNamesWithTemplates.add(((GemfireTemplate) bean).getRegion().getName()); + } } return bean; } - @SuppressWarnings("all") - private void registerRegionTemplateBean(String regionTemplateName, Object bean) { + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - Optional.ofNullable(applicationContext) - .filter(it -> bean instanceof Region) - .filter(it -> !it.containsBean(regionTemplateName)) - .filter(it -> isGemfireTemplateWithRegionNotPresent(it, (Region) bean)) - .map(ConfigurableApplicationContext::getBeanFactory) - .filter(SingletonBeanRegistry.class::isInstance) - .map(SingletonBeanRegistry.class::cast) - .ifPresent(beanFactory -> { - beanFactory.registerSingleton(regionTemplateName, new GemfireTemplate((Region) bean)); - regionTemplateNames.add(regionTemplateName); - }); + if (bean instanceof GemFireCache) { + + GemFireCache cache = (GemFireCache) bean; + + registerRegionTemplatesForCacheRegions(applicationContext, cache); + } + + return bean; } }; } + // TODO: Remove this logic when DATAGEODE-231 is resolved! + private void handlePrematureCacheCreation(ConfigurableApplicationContext applicationContext) { + + Optional.ofNullable(GemfireUtils.resolveGemFireCache()) + .ifPresent(cache -> registerRegionTemplatesForCacheRegions(applicationContext, cache)); + } + + // Required by @EnableCachingDefinedRegions @EventListener({ ContextRefreshedEvent.class }) - public void registerRemainingRegionTemplatesOnContextRefresh(ContextRefreshedEvent event) { + public void regionTemplateContextRefreshedEventListener(ContextRefreshedEvent event) { + + this.regionNamesWithTemplates.clear(); ApplicationContext applicationContext = event.getApplicationContext(); if (applicationContext instanceof ConfigurableApplicationContext) { - ConfigurableListableBeanFactory beanFactory = - ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + ConfigurableApplicationContext configurableApplicationContext = + (ConfigurableApplicationContext) applicationContext; - Optional.ofNullable(applicationContext.getBean(GemFireCache.class)) - .map(GemFireCache::rootRegions) - .ifPresent(rootRegions -> rootRegions.stream() - .filter(Objects::nonNull) - .filter(region -> !regionTemplateNames.contains(toRegionTemplateName(region.getName()))) - .filter(region -> !applicationContext.containsBean(toRegionTemplateName(region.getName()))) - .filter(region -> isGemfireTemplateWithRegionNotPresent(applicationContext, region)) - .forEach(region -> beanFactory.registerSingleton(toRegionTemplateName(region.getName()), - new GemfireTemplate(region)))); + GemFireCache cache = configurableApplicationContext.getBean(GemFireCache.class); + + registerRegionTemplatesForCacheRegions(configurableApplicationContext, cache); } } - private boolean isGemfireTemplateWithRegionNotPresent(ApplicationContext applicationContext, Region region) { + private void registerRegionTemplatesForCacheRegions(@NonNull ConfigurableApplicationContext applicationContext, + @NonNull GemFireCache cache) { - Map gemfireTemplateBeans = - applicationContext.getBeansOfType(GemfireTemplate.class, false, false); + for (Region region : CollectionUtils.nullSafeSet(cache.rootRegions())) { - return CollectionUtils.nullSafeMap(gemfireTemplateBeans).values().stream() - .map(GemfireTemplate::getRegion) - .noneMatch(templateRegion -> templateRegion.equals(region)); + String regionTemplateBeanName = toRegionTemplateBeanName(region.getName()); + + registerRegionTemplateBean(applicationContext, region, regionTemplateBeanName); + } + } + + private void registerRegionTemplateBean(@NonNull ConfigurableApplicationContext applicationContext, + @NonNull Region region, String regionTemplateBeanName) { + + Optional.of(applicationContext) + .filter(it -> isNotBean(it, regionTemplateBeanName)) + .map(ConfigurableApplicationContext::getBeanFactory) + .ifPresent(beanFactory -> register(newGemfireTemplate(region), regionTemplateBeanName, beanFactory)); + } + + private boolean isNotBean(@NonNull ApplicationContext applicationContext, @Nullable String beanName) { + return !(StringUtils.hasText(beanName) && applicationContext.containsBean(beanName)); + } + + @SuppressWarnings("unchecked") + private GemfireTemplate newGemfireTemplate(@NonNull Region region) { + return new GemfireTemplate(region); + } + + // Register Singleton Object with bean name in BeanDefinitionRegistry + private void register(Object singletonObject, String beanName, ConfigurableBeanFactory beanFactory) { + + if (this.autoConfiguredRegionTemplateBeanNames.add(beanName)) { + beanFactory.registerSingleton(beanName, singletonObject); + } + } + + private String toRegionTemplateBeanName(@NonNull String regionName) { + return StringUtils.uncapitalize(regionName) + TEMPLATE; } } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/CachingDefinedRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/CachingDefinedRegionTemplateAutoConfigurationIntegrationTests.java index b4af6661..898f4188 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/CachingDefinedRegionTemplateAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/CachingDefinedRegionTemplateAutoConfigurationIntegrationTests.java @@ -102,6 +102,10 @@ public class CachingDefinedRegionTemplateAutoConfigurationIntegrationTests exten .map(Region::getName) .sorted() .collect(Collectors.toList())).containsExactly("BooksByAuthor", "BooksByYear", "CachedBooks"); + + assertThat(this.booksByAuthor).isNotNull(); + assertThat(this.booksByTitle).isNotNull(); + assertThat(this.booksByYear).isNotNull(); } @Test @@ -134,5 +138,10 @@ public class CachingDefinedRegionTemplateAutoConfigurationIntegrationTests exten LibraryService libraryService() { return new LibraryService(); } + + //@Bean("TestBean") + Object testBean(@Qualifier("booksByAuthor") GemfireTemplate booksByAuthorTemplate) { + return "TEST"; + } } } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredNonInjectedRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredNonInjectedRegionTemplateAutoConfigurationIntegrationTests.java new file mode 100644 index 00000000..921d7dca --- /dev/null +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredNonInjectedRegionTemplateAutoConfigurationIntegrationTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2019 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.geode.boot.autoconfigure.template; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientRegionShortcut; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport; +import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects; +import org.springframework.data.gemfire.util.RegionUtils; +import org.springframework.geode.boot.autoconfigure.RegionTemplateAutoConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration Tests for {@link RegionTemplateAutoConfiguration}. + * + * This Integration Test class tests that the {@link GemfireTemplate} is created regardless of whether + * the {@literal Example} client {@link Region} bean is actually referenced (injected) into application code. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.client.ClientCache + * @see org.springframework.boot.autoconfigure.SpringBootApplication + * @see org.springframework.boot.test.context.SpringBootTest + * @see org.springframework.data.gemfire.GemfireTemplate + * @see org.springframework.data.gemfire.client.ClientRegionFactoryBean + * @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects + * @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport + * @see org.springframework.geode.boot.autoconfigure.RegionTemplateAutoConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @see Autowiring a GemfireTemplate into the application is not working in all cases + * @since 1.2.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@SuppressWarnings("unused") +public class DeclaredNonInjectedRegionTemplateAutoConfigurationIntegrationTests extends IntegrationTestsSupport { + + @Autowired + private ClientCache clientCache; + + @Autowired + @Qualifier("exampleTemplate") + private GemfireTemplate exampleTemplate; + + @Before + public void setup() { + + assertThat(this.clientCache).isNotNull(); + assertThat(this.exampleTemplate).isNotNull(); + } + + @Test + public void clientCacheContainsExampleRegion() { + + Region example = this.clientCache.getRegion(RegionUtils.toRegionPath("Example")); + + assertThat(example).isNotNull(); + assertThat(example.getName()).isEqualTo("Example"); + } + + @Test + public void exampleRegionTemplateExists() { + assertThat(this.exampleTemplate.getRegion()) + .isEqualTo(this.clientCache.getRegion(RegionUtils.toRegionPath("Example"))); + } + + @SpringBootApplication + @EnableGemFireMockObjects + static class TestConfiguration { + + @Bean("Example") + public ClientRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { + + ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean<>(); + + exampleRegion.setCache(gemfireCache); + exampleRegion.setShortcut(ClientRegionShortcut.LOCAL); + + return exampleRegion; + } + + @Bean("TestBean") + public Object testBean(@Qualifier("exampleTemplate") GemfireTemplate exampleTemplate) { + return "TEST"; + } + } +} diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredRegionTemplateAutoConfigurationIntegrationTests.java index 594ea1d5..4308f1ed 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredRegionTemplateAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/DeclaredRegionTemplateAutoConfigurationIntegrationTests.java @@ -45,10 +45,12 @@ import org.springframework.test.context.junit4.SpringRunner; * * @author John Blum * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache * @see org.apache.geode.cache.Region * @see org.springframework.boot.autoconfigure.SpringBootApplication * @see org.springframework.boot.test.context.SpringBootTest * @see org.springframework.data.gemfire.GemfireTemplate + * @see org.springframework.data.gemfire.client.ClientRegionFactoryBean * @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport * @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects * @see org.springframework.geode.boot.autoconfigure.RegionTemplateAutoConfiguration @@ -67,6 +69,13 @@ public class DeclaredRegionTemplateAutoConfigurationIntegrationTests extends Int @Resource(name = "Example") private Region exampleRegion; + @Test + public void exampleRegionIsPresent() { + + assertThat(this.exampleRegion).isNotNull(); + assertThat(this.exampleRegion.getName()).isEqualTo("Example"); + } + @Test public void exampleRegionTemplateIsPresent() { diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/EntityDefinedRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/EntityDefinedRegionTemplateAutoConfigurationIntegrationTests.java index 0aa86e30..ad1d9f63 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/EntityDefinedRegionTemplateAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/EntityDefinedRegionTemplateAutoConfigurationIntegrationTests.java @@ -17,11 +17,15 @@ package org.springframework.geode.boot.autoconfigure.template; import static org.assertj.core.api.Assertions.assertThat; +import java.util.stream.Collectors; + import javax.annotation.Resource; +import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.apache.geode.cache.client.ClientRegionShortcut; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions; import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport; @@ -62,6 +67,9 @@ import example.app.books.model.ISBN; @SuppressWarnings("unused") public class EntityDefinedRegionTemplateAutoConfigurationIntegrationTests extends IntegrationTestsSupport { + @Autowired + public GemFireCache gemfireCache; + @Autowired @Qualifier("authorsTemplate") private GemfireTemplate authorsTemplate; @@ -76,6 +84,20 @@ public class EntityDefinedRegionTemplateAutoConfigurationIntegrationTests extend @Resource(name = "Books") private Region books; + @Before + public void setup() { + + assertThat(this.gemfireCache).isNotNull(); + + assertThat(this.gemfireCache.rootRegions().stream() + .map(Region::getName) + .sorted() + .collect(Collectors.toList())).containsExactly("Authors", "Books"); + + assertThat(this.authors).isNotNull(); + assertThat(this.books).isNotNull(); + } + @Test public void authorsRegionTemplateIsPresent() { @@ -93,6 +115,11 @@ public class EntityDefinedRegionTemplateAutoConfigurationIntegrationTests extend @SpringBootApplication @EnableGemFireMockObjects @EnableEntityDefinedRegions(basePackageClasses = Book.class, clientRegionShortcut = ClientRegionShortcut.LOCAL) - static class TestApplicationConfiguration { } + static class TestApplicationConfiguration { + @Bean("TestBean") + Object testBean(@Qualifier("booksTemplate") GemfireTemplate booksTemplate) { + return "TEST"; + } + } } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests.java index dae9f562..e3362feb 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests.java @@ -70,13 +70,19 @@ public class ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests ext private Region example; @Test - public void exampleRegionTemplateIsNotPresent() { + public void exampleTemplateIsPresentButIsNotGemfireTemplate() { - assertThat(this.applicationContext.containsBean("exampleTemplate")).isFalse(); + assertThat(this.applicationContext.containsBean("exampleTemplate")).isTrue(); + assertThat(this.applicationContext.getBean("exampleTemplate")).isNotInstanceOf(GemfireTemplate.class); } @Test - public void testRegionTemplateIsPresent() { + public void onlyOneBeanOfTypeGemfireTemplateExists() { + assertThat(this.applicationContext.getBeanNamesForType(GemfireTemplate.class)).hasSize(1); + } + + @Test + public void testTemplateIsPresent() { assertThat(this.testTemplate).isNotNull(); assertThat(this.testTemplate.getRegion()).isEqualTo(this.example); diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/NativeDefinedRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/NativeDefinedRegionTemplateAutoConfigurationIntegrationTests.java index 0270f235..25a16b0f 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/NativeDefinedRegionTemplateAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/NativeDefinedRegionTemplateAutoConfigurationIntegrationTests.java @@ -17,6 +17,9 @@ package org.springframework.geode.boot.autoconfigure.template; import static org.assertj.core.api.Assertions.assertThat; +import java.util.stream.Collectors; + +import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.junit.Test; @@ -24,12 +27,16 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.DependsOn; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.config.annotation.EnableGemFireProperties; import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport; +import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.geode.boot.autoconfigure.RegionTemplateAutoConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -56,9 +63,22 @@ public class NativeDefinedRegionTemplateAutoConfigurationIntegrationTests extend @Autowired private ApplicationContext applicationContext; + @Autowired + private GemFireCache cache; + @Autowired private GemfireTemplate exampleTemplate; + @Test + public void cacheContainsExampleRegion() { + + assertThat(this.cache).isNotNull(); + + assertThat(CollectionUtils.nullSafeSet(this.cache.rootRegions()).stream() + .map(Region::getName) + .collect(Collectors.toSet())).containsExactly("Example"); + } + @Test(expected = NoSuchBeanDefinitionException.class) public void exampleRegionBeanIsNotPresent() { this.applicationContext.getBean("Example", Region.class); @@ -74,6 +94,12 @@ public class NativeDefinedRegionTemplateAutoConfigurationIntegrationTests extend @SpringBootApplication @EnableGemFireProperties(cacheXmlFile = "template-cache.xml") - static class TestConfiguration { } + static class TestConfiguration { + @Bean("TestBean") + @DependsOn("gemfireCache") + Object testBean(@Qualifier("exampleTemplate") GemfireTemplate exampleTemplate) { + return "TEST"; + } + } } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ServerDefinedRegionTemplateAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ServerDefinedRegionTemplateAutoConfigurationIntegrationTests.java index ce70f99b..1e5fe6a3 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ServerDefinedRegionTemplateAutoConfigurationIntegrationTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/template/ServerDefinedRegionTemplateAutoConfigurationIntegrationTests.java @@ -18,16 +18,19 @@ package org.springframework.geode.boot.autoconfigure.template; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; +import java.util.stream.Collectors; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @@ -39,11 +42,12 @@ import org.springframework.data.gemfire.config.annotation.CacheServerApplication import org.springframework.data.gemfire.config.annotation.EnableClusterDefinedRegions; import org.springframework.data.gemfire.config.annotation.EnableLogging; import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport; +import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.geode.boot.autoconfigure.RegionTemplateAutoConfiguration; import org.springframework.test.context.junit4.SpringRunner; /** - * Integration tests for {@link RegionTemplateAutoConfiguration} using SDG's {@link EnableServerDefinedRegions} + * Integration tests for {@link RegionTemplateAutoConfiguration} using SDG's {@link EnableClusterDefinedRegions} * annotation to define {@link Region Regions} and associated Templates. * * @author John Blum @@ -80,11 +84,24 @@ public class ServerDefinedRegionTemplateAutoConfigurationIntegrationTests @Autowired private ApplicationContext applicationContext; + @Autowired + private ClientCache clientCache; + @Autowired private GemfireTemplate exampleServerRegionTemplate; @Test - public void exampleServerRegionExistsAsClientRegion() { + public void clientCacheContainsExampleServerRegion() { + + assertThat(this.clientCache).isNotNull(); + + assertThat(CollectionUtils.nullSafeSet(this.clientCache.rootRegions()).stream() + .map(Region::getName) + .collect(Collectors.toSet())).containsExactly("ExampleServerRegion"); + } + + @Test + public void exampleServerRegionExistsAsClientRegionBean() { Region exampleServerRegion = this.applicationContext.getBean("ExampleServerRegion", Region.class); @@ -105,7 +122,13 @@ public class ServerDefinedRegionTemplateAutoConfigurationIntegrationTests @SpringBootApplication @EnableClusterDefinedRegions @EnableLogging(logLevel = GEMFIRE_LOG_LEVEL) - static class GemFireClientConfiguration { } + static class GemFireClientConfiguration { + + @Bean("TestBean") + Object testBean(@Qualifier("exampleServerRegionTemplate") GemfireTemplate exampleServerRegionTemplate) { + return "TEST"; + } + } @CacheServerApplication(logLevel = GEMFIRE_LOG_LEVEL) static class GemFireServerConfiguration {