Improves auto-confiuguration of GemfireTemplates for cache Regions to minimize issues when auto-wiring templates into application components.

Resolves gh-55.
This commit is contained in:
John Blum
2019-09-20 01:43:01 -07:00
parent add18afbfd
commit 2234075281
8 changed files with 469 additions and 59 deletions

View File

@@ -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<String> 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<String> autoConfiguredRegionTemplateBeanNames = Collections.synchronizedSet(new HashSet<>());
private final Set<String> regionNamesWithTemplates = Collections.synchronizedSet(new HashSet<>());
@Bean
BeanFactoryPostProcessor regionTemplateBeanFactoryPostProcessor() {
return beanFactory -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<String> beanDefinitionNames =
Arrays.asList(ArrayUtils.nullSafeArray(registry.getBeanDefinitionNames(), String.class));
Set<String> 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<Object> 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<String> 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<String, GemfireTemplate> 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;
}
}

View File

@@ -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";
}
}
}

View File

@@ -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 <a href="https://github.com/spring-projects/spring-boot-data-geode/issues/55">Autowiring a GemfireTemplate into the application is not working in all cases</a>
* @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<Object, Object> exampleRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> exampleRegion = new ClientRegionFactoryBean<>();
exampleRegion.setCache(gemfireCache);
exampleRegion.setShortcut(ClientRegionShortcut.LOCAL);
return exampleRegion;
}
@Bean("TestBean")
public Object testBean(@Qualifier("exampleTemplate") GemfireTemplate exampleTemplate) {
return "TEST";
}
}
}

View File

@@ -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<Long, String> exampleRegion;
@Test
public void exampleRegionIsPresent() {
assertThat(this.exampleRegion).isNotNull();
assertThat(this.exampleRegion.getName()).isEqualTo("Example");
}
@Test
public void exampleRegionTemplateIsPresent() {

View File

@@ -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<ISBN, Book> 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";
}
}
}

View File

@@ -70,13 +70,19 @@ public class ExistingRegionTemplateByRegionAutoConfigurationIntegrationTests ext
private Region<Object, Object> 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);

View File

@@ -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";
}
}
}

View File

@@ -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.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
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 {