diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java new file mode 100644 index 00000000..3b6640b2 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java @@ -0,0 +1,386 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.config.annotation; + +import static java.util.Arrays.stream; +import static org.springframework.data.gemfire.util.ArrayUtils.asArray; +import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; +import static org.springframework.data.gemfire.util.CollectionUtils.asSet; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionShortcut; +import org.springframework.beans.BeanInstantiationException; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.type.MethodMetadata; +import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.data.gemfire.config.annotation.support.BeanDefinitionRegistryPostProcessorSupport; +import org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean; +import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.util.CollectionUtils; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * The {@link CachingDefinedRegionsConfiguration} class is a Spring {@link Configuration @Configuration} class + * that applies configuration to a Spring (Data GemFire/Geode) application to create GemFire/Geode cache + * {@link Region Regions} based on the use of Spring's Cache Abstraction to enable caching for application + * service classes and methods. + * + * @author John Blum + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.springframework.beans.factory.config.BeanDefinition + * @see org.springframework.beans.factory.config.BeanPostProcessor + * @see org.springframework.beans.factory.support.BeanDefinitionBuilder + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor + * @see org.springframework.cache.annotation.CacheConfig + * @see org.springframework.cache.annotation.CacheEvict + * @see org.springframework.cache.annotation.CachePut + * @see org.springframework.cache.annotation.Cacheable + * @see org.springframework.cache.annotation.Caching + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.core.annotation.AnnotatedElementUtils + * @see org.springframework.core.annotation.AnnotationUtils + * @see org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions + * @see org.springframework.data.gemfire.config.annotation.support.BeanDefinitionRegistryPostProcessorSupport + * @see org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean + * @since 2.0.0 + */ +@Configuration +public class CachingDefinedRegionsConfiguration { + + private static final Class[] CLASS_CACHE_ANNOTATION_TYPES; + + private static final Class[] METHOD_CACHE_ANNOTATION_TYPES = + asArray(Cacheable.class, CacheEvict.class, CachePut.class); + + static { + + List annotationTypes = new ArrayList<>(); + + Collections.addAll(annotationTypes, METHOD_CACHE_ANNOTATION_TYPES); + annotationTypes.add(CacheConfig.class); + + CLASS_CACHE_ANNOTATION_TYPES = annotationTypes.toArray(new Class[annotationTypes.size()]); + + } + + private static final Set INFRASTRUCTURE_ROLES = + asSet(BeanDefinition.ROLE_INFRASTRUCTURE, BeanDefinition.ROLE_SUPPORT); + + private static final String ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE = "org.springframework.data.gemfire"; + private static final String ORG_SPRINGFRAMEWORK_PACKAGE = "org.springframework"; + + @Bean + @SuppressWarnings("all") + public BeanDefinitionRegistryPostProcessor cacheAbstractionAnnotationsRegionBeanDefinitionRegistrar() { + + return new BeanDefinitionRegistryPostProcessorSupport() { + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + registerBeanDefinitions(registry); + } + }; + } + + @Bean + @SuppressWarnings("all") + public BeanPostProcessor cacheAbstractionAnnotationsRegionBeanRegistrar(ConfigurableBeanFactory beanFactory) { + + return new BeanPostProcessor() { + + @Nullable @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + + if (isNotInfrastructureBean(bean)) { + registerRegionBeans(collectCacheNames(bean.getClass()), beanFactory); + } + + return bean; + } + }; + } + + void registerBeanDefinitions(BeanDefinitionRegistry registry) { + + for (String beanName : registry.getBeanDefinitionNames()) { + + BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); + + if (isNotInfrastructureBean(beanDefinition)) { + resolveBeanClass(beanDefinition, registry).ifPresent(beanClass -> + registerRegionBeanDefinitions(collectCacheNames(beanClass), registry)); + } + } + } + + private boolean isNotInfrastructureBean(Object bean) { + return isNotInfrastructureClass(bean.getClass().getName()); + } + + boolean isNotInfrastructureBean(BeanDefinition beanDefinition) { + return (isNotInfrastructureRole(beanDefinition) && isNotInfrastructureClass(beanDefinition)); + } + + boolean isNotInfrastructureClass(BeanDefinition beanDefinition) { + return resolveBeanClassName(beanDefinition).filter(this::isNotInfrastructureClass).isPresent(); + } + + boolean isNotInfrastructureClass(String className) { + return (className.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE) + || !className.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE)); + } + + boolean isNotInfrastructureRole(BeanDefinition beanDefinition) { + return !INFRASTRUCTURE_ROLES.contains(beanDefinition.getRole()); + } + + boolean isUserLevelMethod(Method method) { + + return Optional.ofNullable(method) + .filter(ClassUtils::isUserLevelMethod) + .filter(it -> !Object.class.equals(it.getDeclaringClass())) + .isPresent(); + } + + @SuppressWarnings("unchecked") + private Set collectCacheNames(Class type) { + + Set cacheNames = new HashSet<>(); + + cacheNames.addAll(collectCachingCacheNames(type)); + cacheNames.addAll(collectCacheNames(type, CLASS_CACHE_ANNOTATION_TYPES)); + + stream(type.getMethods()).forEach(method -> { + + if (isUserLevelMethod(method)) { + cacheNames.addAll(collectCachingCacheNames(method)); + cacheNames.addAll(collectCacheNames(method, METHOD_CACHE_ANNOTATION_TYPES)); + } + }); + + return cacheNames; + } + + @SuppressWarnings("all") + Set collectCacheNames(AnnotatedElement annotatedElement, + Class... annotationTypes) { + + Stream cacheNames = stream(nullSafeArray(annotationTypes, Class.class)) + .map(annotationType -> resolveAnnotation(annotatedElement, annotationType)) + .flatMap(annotation -> collectCacheNames((Annotation) annotation).stream()); + + return cacheNames.collect(Collectors.toSet()); + } + + private Set collectCacheNames(Annotation annotation) { + + return Optional.ofNullable(annotation) + .map(AnnotationUtils::getAnnotationAttributes) + .map(annotationAttributes -> (String[]) annotationAttributes.get("cacheNames")) + .map(CollectionUtils::asSet) + .orElse(Collections.emptySet()); + } + + private Set collectCachingCacheNames(AnnotatedElement annotatedElement) { + + Set cacheNames = new HashSet<>(); + + Optional.ofNullable(resolveAnnotation(annotatedElement, Caching.class)) + .ifPresent(caching -> { + + cacheNames.addAll(stream(nullSafeArray(caching.cacheable(), Cacheable.class)) + .flatMap(cacheable -> collectCacheNames(cacheable).stream()) + .collect(Collectors.toSet())); + + cacheNames.addAll(stream(nullSafeArray(caching.evict(), CacheEvict.class)) + .flatMap(cacheable -> collectCacheNames(cacheable).stream()) + .collect(Collectors.toSet())); + + cacheNames.addAll(stream(nullSafeArray(caching.put(), CachePut.class)) + .flatMap(cacheable -> collectCacheNames(cacheable).stream()) + .collect(Collectors.toSet())); + + }); + + return cacheNames; + } + + private BeanDefinitionRegistry registerRegionBeanDefinitions(Set cacheNames, + BeanDefinitionRegistry registry) { + + boolean containsGemFirePoolBeanDefinition = + registry.containsBeanDefinition(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME); + + cacheNames.forEach(cacheName -> { + + if (!registry.containsBeanDefinition(cacheName)) { + + BeanDefinitionBuilder builder = + BeanDefinitionBuilder.genericBeanDefinition(GemFireCacheTypeAwareRegionFactoryBean.class); + + builder.addPropertyReference("cache", GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME); + + if (!containsGemFirePoolBeanDefinition) { + builder.addPropertyValue("poolName", GemfireUtils.DEFAULT_POOL_NAME); + } + + builder.addPropertyValue("regionName", cacheName); + builder.addPropertyValue("serverRegionShortcut", RegionShortcut.PARTITION); + + registry.registerBeanDefinition(cacheName, builder.getBeanDefinition()); + } + }); + + return registry; + } + + private ConfigurableBeanFactory registerRegionBeans(Set cacheNames, ConfigurableBeanFactory beanFactory) { + + boolean containsGemFirePoolBean = beanFactory.containsBean(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME); + + cacheNames.forEach(cacheName -> { + + if (!beanFactory.containsBean(cacheName)) { + try { + + GemFireCacheTypeAwareRegionFactoryBean regionFactoryBean = + new GemFireCacheTypeAwareRegionFactoryBean<>(); + + regionFactoryBean.setCache(beanFactory.getBean("gemfireCache", GemFireCache.class)); + + if (!containsGemFirePoolBean) { + regionFactoryBean.setPoolName(GemfireUtils.DEFAULT_POOL_NAME); + } + + regionFactoryBean.setRegionName(cacheName); + regionFactoryBean.setServerRegionShortcut(RegionShortcut.PARTITION); + regionFactoryBean.afterPropertiesSet(); + + Optional.ofNullable(regionFactoryBean.getObject()) + .ifPresent(region -> beanFactory.registerSingleton(cacheName, region)); + } + catch (Exception cause) { + throw new BeanInstantiationException(Region.class, + String.format("Failed to create Region for cache [%s]", cacheName), cause); + } + } + }); + + return beanFactory; + } + + /* (non-Javadoc) */ + A resolveAnnotation(AnnotatedElement annotatedElement, Class annotationType) { + + return (annotatedElement instanceof Class + ? AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType) + : AnnotationUtils.findAnnotation(annotatedElement, annotationType)); + } + + /* (non-Javadoc) */ + Optional> resolveBeanClass(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) { + return resolveBeanClass(beanDefinition, resolveBeanClassLoader(registry)); + } + + /* (non-Javadoc) */ + private Optional> resolveBeanClass(BeanDefinition beanDefinition, ClassLoader classLoader) { + + Class beanClass = (beanDefinition instanceof AbstractBeanDefinition + ? safeResolveType(() -> ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(classLoader)) : null); + + if (beanClass == null) { + beanClass = resolveBeanClassName(beanDefinition).map(beanClassName -> + safeResolveType(() -> ClassUtils.forName(beanClassName, classLoader))).orElse(null); + } + + return Optional.ofNullable(beanClass); + } + + /* (non-Javadoc) */ + ClassLoader resolveBeanClassLoader(BeanDefinitionRegistry registry) { + + return (registry instanceof ConfigurableBeanFactory + ? ((ConfigurableBeanFactory) registry).getBeanClassLoader() + : Thread.currentThread().getContextClassLoader()); + } + + /* (non-Javadoc) */ + Optional resolveBeanClassName(BeanDefinition beanDefinition) { + + Optional beanClassName = + Optional.ofNullable(beanDefinition.getBeanClassName()).filter(StringUtils::hasText); + + if (!beanClassName.isPresent()) { + beanClassName = Optional.of(beanDefinition) + .filter(it -> StringUtils.hasText(it.getFactoryMethodName())) + .filter(it -> it instanceof AnnotatedBeanDefinition) + .map(it -> ((AnnotatedBeanDefinition) it).getFactoryMethodMetadata()) + .map(MethodMetadata::getReturnTypeName); + } + + return beanClassName; + } + + /* (non-Javadoc) */ + Class safeResolveType(TypeResolver typeResolver) { + try { + return typeResolver.resolve(); + } + catch (ClassNotFoundException cause) { + return null; + } + } + + interface TypeResolver { + Class resolve() throws ClassNotFoundException; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegions.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegions.java new file mode 100644 index 00000000..e833fccc --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegions.java @@ -0,0 +1,59 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.geode.cache.Region; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.gemfire.cache.config.EnableGemfireCaching; + +/** + * The {@link EnableCachingDefinedRegions} annotation marks a Spring {@link Configuration @Configuration} application + * annotated class to enable the creation of GemFire/Geode {@link Region Regions} based on Spring's Cache Abstraction + * Annotations applied to application service methods and types. + * + * Additionally, this annotation also enable Spring's Cache Abstraction with SDG's {@link EnableGemfireCaching}, + * which declares Spring's {@link org.springframework.cache.annotation.EnableCaching} annotation as well as + * declares the SDG {@link org.springframework.data.gemfire.cache.GemfireCacheManager} bean definition. + * + * @author John Blum + * @see java.lang.annotation.Documented + * @see java.lang.annotation.Inherited + * @see java.lang.annotation.Retention + * @see java.lang.annotation.Target + * @see org.apache.geode.cache.Region + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.Import + * @see org.springframework.data.gemfire.cache.config.EnableGemfireCaching + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@EnableGemfireCaching +@Import(CachingDefinedRegionsConfiguration.class) +public @interface EnableCachingDefinedRegions { + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.java index 9fc0ef41..3675fe60 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.java @@ -32,7 +32,7 @@ import org.springframework.core.annotation.AliasFor; /** * The {@link EnableEntityDefinedRegions} annotation marks a Spring {@link Configuration @Configuration} application - * annotated class to enable the creation of the GemFire/Geode {@link Region Regions} based on + * annotated class to enable the creation of GemFire/Geode {@link Region Regions} based on * the application persistent entities. * * @author John Blum @@ -53,7 +53,6 @@ import org.springframework.core.annotation.AliasFor; @Inherited @Documented @Import(IndexConfiguration.class) -@SuppressWarnings({ "unused" }) public @interface EnableEntityDefinedRegions { /** diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/BeanDefinitionRegistryPostProcessorSupport.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/BeanDefinitionRegistryPostProcessorSupport.java new file mode 100644 index 00000000..63715d87 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/BeanDefinitionRegistryPostProcessorSupport.java @@ -0,0 +1,44 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.config.annotation.support; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; + +/** + * The {@link BeanDefinitionRegistryPostProcessorSupport} is an abstract class supporting the implementation + * of the Spring {@link BeanDefinitionRegistryPostProcessor} interface. + * + * @author John Blum + * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor + * @since 2.0.0 + */ +@SuppressWarnings("all") +public abstract class BeanDefinitionRegistryPostProcessorSupport implements BeanDefinitionRegistryPostProcessor { + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + } +} diff --git a/src/main/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessor.java index ffad4dde..554cd324 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessor.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessor.java @@ -16,7 +16,10 @@ package org.springframework.data.gemfire.config.support; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; + import java.util.Collections; +import java.util.Optional; import java.util.Set; import org.apache.geode.cache.GemFireCache; @@ -28,20 +31,22 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** - * The AutoRegionLookupBeanPostProcessor class is a Spring {@link BeanPostProcessor} that post processes - * a {@link GemFireCache} by registering all cache {@link Region Regions} that have not been explicitly - * defined in the Spring application context. This is usually the case for {@link Region Regions} that - * have been defined in GemFire's native {@literal cache.xml} or defined using GemFire 8's cluster-based - * configuration service. + * The {@link AutoRegionLookupBeanPostProcessor} class is a Spring {@link BeanPostProcessor} that post processes + * a {@link GemFireCache} by registering all cache {@link Region Regions} that have not been explicitly defined + * in the Spring application context. + * + * This is usually the case for {@link Region Regions} that have been defined in GemFire's native {@literal cache.xml} + * or defined using GemFire Cluster-based Configuration Service. * * @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.BeanFactoryAware * @see org.springframework.beans.factory.config.BeanPostProcessor - * @see org.apache.geode.cache.GemFireCache - * @see org.apache.geode.cache.Region * @since 1.5.0 */ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { @@ -52,7 +57,9 @@ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, Bea * {@inheritDoc} */ @Override + @SuppressWarnings("all") public final void setBeanFactory(BeanFactory beanFactory) throws BeansException { + Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory, String.format("BeanFactory [%1$s] must be an instance of %2$s", ObjectUtils.nullSafeClassName(beanFactory), ConfigurableListableBeanFactory.class.getSimpleName())); @@ -62,16 +69,8 @@ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, Bea /* (non-Javadoc) */ protected ConfigurableListableBeanFactory getBeanFactory() { - Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized"); - return this.beanFactory; - } - - /** - * {@inheritDoc} - */ - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; + return Optional.ofNullable(this.beanFactory).orElseThrow(() -> + newIllegalStateException("BeanFactory was not properly configured")); } /** @@ -79,6 +78,7 @@ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, Bea */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof GemFireCache) { registerCacheRegionsAsBeans((GemFireCache) bean); } @@ -88,14 +88,14 @@ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, Bea /* (non-Javadoc) */ void registerCacheRegionsAsBeans(GemFireCache cache) { - for (Region region : cache.rootRegions()) { - registerCacheRegionAsBean(region); - } + cache.rootRegions().forEach(this::registerCacheRegionAsBean); } /* (non-Javadoc) */ void registerCacheRegionAsBean(Region region) { + if (region != null) { + String regionBeanName = getBeanName(region); if (!getBeanFactory().containsBean(regionBeanName)) { @@ -110,13 +110,15 @@ public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, Bea /* (non-Javadoc) */ String getBeanName(Region region) { - String regionFullPath = region.getFullPath(); - return (regionFullPath.lastIndexOf(Region.SEPARATOR) > 0 ? regionFullPath : region.getName()); + + return Optional.ofNullable(region.getFullPath()) + .filter(StringUtils::hasText) + .filter(regionFullPath -> regionFullPath.lastIndexOf(Region.SEPARATOR) > 0) + .orElseGet(region::getName); } /* (non-Javadoc) */ Set> nullSafeSubregions(Region parentRegion) { - Set> subregions = parentRegion.subregions(false); - return (subregions != null ? subregions : Collections.>emptySet()); + return Optional.ofNullable(parentRegion.subregions(false)).orElse(Collections.emptySet()); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/support/ClientRegionPoolBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/support/ClientRegionPoolBeanFactoryPostProcessor.java index 0ff966c5..d4c9af9d 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/ClientRegionPoolBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/ClientRegionPoolBeanFactoryPostProcessor.java @@ -17,17 +17,21 @@ package org.springframework.data.gemfire.config.support; +import java.util.Arrays; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; +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.ConfigurableListableBeanFactory; import org.springframework.data.gemfire.client.ClientRegionFactoryBean; import org.springframework.data.gemfire.client.PoolFactoryBean; import org.springframework.data.gemfire.util.SpringUtils; +import org.springframework.util.StringUtils; /** * {@link ClientRegionPoolBeanFactoryPostProcessor} is a Spring {@link BeanFactoryPostProcessor} implementation @@ -48,11 +52,14 @@ public class ClientRegionPoolBeanFactoryPostProcessor implements BeanFactoryPost * {@inheritDoc} */ @Override + @SuppressWarnings("all") public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - Set clientRegionBeanNames = new HashSet(); - Set poolBeanNames = new HashSet(); - for (String beanName : beanFactory.getBeanDefinitionNames()) { + Set clientRegionBeanNames = new HashSet<>(); + Set poolBeanNames = new HashSet<>(); + + Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(beanName -> { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (isClientRegionBean(beanDefinition)) { @@ -61,26 +68,44 @@ public class ClientRegionPoolBeanFactoryPostProcessor implements BeanFactoryPost else if (isPoolBean(beanDefinition)) { poolBeanNames.add(beanName); } - } + }); + + clientRegionBeanNames.forEach(clientRegionBeanName -> { - for (String clientRegionBeanName : clientRegionBeanNames) { BeanDefinition clientRegionBean = beanFactory.getBeanDefinition(clientRegionBeanName); + String poolName = getPoolName(clientRegionBean); if (poolBeanNames.contains(poolName)) { SpringUtils.addDependsOn(clientRegionBean, poolName); } - } + }); + } + + boolean isBeanDefinitionOfType(BeanDefinition beanDefinition, Class type) { + + return Optional.of(beanDefinition) + .map(it -> beanDefinition.getBeanClassName()) + .filter(StringUtils::hasText) + .map(beanClassName -> type.getName().equals(beanClassName)) + .orElseGet(() -> + Optional.ofNullable(beanDefinition.getFactoryMethodName()) + .filter(StringUtils::hasText) + .filter(it -> beanDefinition instanceof AnnotatedBeanDefinition) + .map(it -> ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata()) + .map(methodMetadata -> type.getName().equals(methodMetadata.getReturnTypeName())) + .orElse(false) + ); } /* (non-Javadoc)*/ boolean isClientRegionBean(BeanDefinition beanDefinition) { - return ClientRegionFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()); + return isBeanDefinitionOfType(beanDefinition, ClientRegionFactoryBean.class); } /* (non-Javadoc)*/ boolean isPoolBean(BeanDefinition beanDefinition) { - return PoolFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()); + return isBeanDefinitionOfType(beanDefinition, PoolFactoryBean.class); } /* (non-Javadoc) */ diff --git a/src/main/java/org/springframework/data/gemfire/config/support/CustomEditorBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/support/CustomEditorBeanFactoryPostProcessor.java index 08112416..5ef57683 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/CustomEditorBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/CustomEditorBeanFactoryPostProcessor.java @@ -56,14 +56,15 @@ import org.springframework.data.gemfire.wan.OrderPolicyConverter; * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor * @since 1.6.0 */ -@SuppressWarnings({ "deprecation", "unused" }) public class CustomEditorBeanFactoryPostProcessor implements BeanFactoryPostProcessor { /** * {@inheritDoc} */ @Override + @SuppressWarnings("all") public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.registerCustomEditor(ConnectionEndpoint.class, StringToConnectionEndpointConverter.class); //beanFactory.registerCustomEditor(ConnectionEndpoint[].class, ConnectionEndpointArrayToIterableConverter.class); beanFactory.registerCustomEditor(ConnectionEndpointList.class, StringToConnectionEndpointListConverter.class); @@ -87,6 +88,7 @@ public class CustomEditorBeanFactoryPostProcessor implements BeanFactoryPostProc * {@inheritDoc} */ @Override + @SuppressWarnings("all") public Iterable convert(ConnectionEndpoint[] source) { return ConnectionEndpointList.from(source); } @@ -100,6 +102,7 @@ public class CustomEditorBeanFactoryPostProcessor implements BeanFactoryPostProc * {@inheritDoc} */ @Override + @SuppressWarnings("all") public ConnectionEndpoint convert(String source) { return assertConverted(source, ConnectionEndpoint.parse(source), ConnectionEndpoint.class); } @@ -113,6 +116,7 @@ public class CustomEditorBeanFactoryPostProcessor implements BeanFactoryPostProc * {@inheritDoc} */ @Override + @SuppressWarnings("all") public ConnectionEndpointList convert(String source) { return assertConverted(source, ConnectionEndpointList.parse(0, source.split(",")), ConnectionEndpointList.class); diff --git a/src/main/java/org/springframework/data/gemfire/config/support/DefinedIndexesApplicationListener.java b/src/main/java/org/springframework/data/gemfire/config/support/DefinedIndexesApplicationListener.java index 697b22b4..df3d6b28 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/DefinedIndexesApplicationListener.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/DefinedIndexesApplicationListener.java @@ -16,6 +16,8 @@ package org.springframework.data.gemfire.config.support; +import java.util.Optional; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.geode.cache.query.MultiIndexCreationException; @@ -52,17 +54,19 @@ public class DefinedIndexesApplicationListener implements ApplicationListener { + try { + queryService.createDefinedIndexes(); + } + catch (MultiIndexCreationException cause) { + logger.warn(String.format("Failed to create pre-defined Indexes: %s", cause.getMessage()), cause); + + } + }); } /* (non-Javadoc) */ @@ -72,11 +76,13 @@ public class DefinedIndexesApplicationListener implements ApplicationListener T readField(Object obj, String fieldName) { + try { + Class type = obj.getClass(); Field field; diff --git a/src/main/java/org/springframework/data/gemfire/config/support/GemfireFeature.java b/src/main/java/org/springframework/data/gemfire/config/support/GemfireFeature.java index 6e0dcd58..a20d1873 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/GemfireFeature.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/GemfireFeature.java @@ -24,6 +24,7 @@ package org.springframework.data.gemfire.config.support; */ @SuppressWarnings("unused") public enum GemfireFeature { + AEQ, BACKUP, CACHING, diff --git a/src/main/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessor.java index 98c1d4d0..ba1f9894 100644 --- a/src/main/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessor.java @@ -16,6 +16,10 @@ package org.springframework.data.gemfire.config.support; +import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; + +import java.util.Arrays; + import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.Region; import org.apache.geode.cache.asyncqueue.AsyncEventQueue; @@ -57,7 +61,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos * @throws IllegalArgumentException if the GemFire PDX {@link DiskStore} name is unspecified. */ public PdxDiskStoreAwareBeanFactoryPostProcessor(String pdxDiskStoreName) { - Assert.hasText(pdxDiskStoreName, "The PDX DiskStore name must be specified"); + Assert.hasText(pdxDiskStoreName, "PDX DiskStore name is required"); this.pdxDiskStoreName = pdxDiskStoreName; } @@ -67,13 +71,14 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos * @return the name of the GemFire PDX {@link DiskStore}. */ public String getPdxDiskStoreName() { - return pdxDiskStoreName; + return this.pdxDiskStoreName; } /** * {@inheritDoc} */ @Override + @SuppressWarnings("all") public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { postProcessPdxDiskStoreDependencies(beanFactory, AsyncEventQueue.class, DiskStore.class, Region.class); } @@ -91,9 +96,11 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos private void postProcessPdxDiskStoreDependencies(ConfigurableListableBeanFactory beanFactory, Class... beanTypes) { - for (Class beanType : beanTypes) { + Arrays.stream(nullSafeArray(beanTypes, Class.class)).forEach(beanType -> { + for (String beanName : beanFactory.getBeanNamesForType(beanType)) { if (!beanName.equalsIgnoreCase(getPdxDiskStoreName())) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); // NOTE for simplicity sake, we add a bean dependency to any bean definition for a bean @@ -105,7 +112,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos addPdxDiskStoreDependency(beanDefinition); } } - } + }); } /** diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java new file mode 100644 index 00000000..8e40a786 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java @@ -0,0 +1,131 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.config.annotation; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.geode.cache.GemFireCache; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Service; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * The EnableCachingDefinedRegionsIntegrationTests class... + * + * @author John Blum + * @since 1.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class EnableCachingDefinedRegionsIntegrationTests { + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private CacheableEchoService echoService; + + @Autowired + private GemFireCache gemfireCache; + + @Before + public void setup() { + + //System.err.printf("@Cacheable Beans [%s]%n", + // Arrays.toString(applicationContext.getBeanNamesForAnnotation(Cacheable.class))); + + assertThat(this.gemfireCache).isNotNull(); + + //System.err.printf("Cache Regions [%s]%n", this.gemfireCache.rootRegions().stream() + // .map(Region::getFullPath).collect(Collectors.toSet())); + } + + @Test + public void cacheRegionsExists() { + assertThat(gemfireCache.getRegion("/Example")).isNotNull(); + assertThat(gemfireCache.getRegion("/Echo")).isNotNull(); + } + + @Test + public void echoServiceOperationsAreSuccessful() { + assertThat(echoService.isCacheMiss()).isFalse(); + assertThat(echoService.echo("one")).isEqualTo("one"); + assertThat(echoService.isCacheMiss()).isTrue(); + assertThat(echoService.echo("two")).isEqualTo("two"); + assertThat(echoService.isCacheMiss()).isTrue(); + assertThat(echoService.echo("one")).isEqualTo("one"); + assertThat(echoService.isCacheMiss()).isFalse(); + assertThat(echoService.echo("three")).isEqualTo("three"); + assertThat(echoService.isCacheMiss()).isTrue(); + assertThat(echoService.echo("two")).isEqualTo("two"); + assertThat(echoService.isCacheMiss()).isFalse(); + } + + @PeerCacheApplication(name = "EnableCachingDefinedRegionsIntegrationTests", logLevel = "warning") + @EnableCachingDefinedRegions + static class TestConfiguration { + + @Bean + CacheableEchoService echoService() { + return new CacheableEchoService(); + } + + @Bean + TestService testService() { + return new DefaultTestService(); + } + } + + @Service + static class CacheableEchoService { + + private final AtomicBoolean cacheMiss = new AtomicBoolean(false); + + public boolean isCacheMiss() { + return this.cacheMiss.compareAndSet(true, false); + } + + @Cacheable("Echo") + public Object echo(String key) { + this.cacheMiss.set(true); + return key; + } + } + + interface TestService { + Object testMethod(String key); + } + + static class DefaultTestService implements TestService { + + @CachePut("Example") + public Object testMethod(String key) { + return "test"; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java new file mode 100644 index 00000000..60c5a48f --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java @@ -0,0 +1,699 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire.config.annotation; + +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.doAnswer; +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.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.util.ArrayUtils.asArray; +import static org.springframework.data.gemfire.util.CollectionUtils.asSet; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.core.SpringVersion; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.type.MethodMetadata; +import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.test.support.MapBuilder; +import org.springframework.stereotype.Service; + +/** + * Unit tests for {@link EnableCachingDefinedRegions} and {@link CachingDefinedRegionsConfiguration}. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.Region + * @see org.springframework.beans.factory.config.BeanDefinition + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.cache.annotation.Cacheable + * @see org.springframework.cache.annotation.CacheEvict + * @see org.springframework.cache.annotation.CachePut + * @see org.springframework.cache.annotation.Caching + * @see org.springframework.stereotype.Service + * @since 2.0.0 + */ +public class EnableCachingDefinedRegionsUnitTests { + + // Subject Under Test (SUT) + private CachingDefinedRegionsConfiguration configuration = new CachingDefinedRegionsConfiguration(); + + @SuppressWarnings("unchecked") + private BeanDefinition mockBeanDefinition(Class beanClass) { + + try { + + AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class, beanClass.getSimpleName()); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(beanClass.getName()); + when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenReturn((Class) beanClass); + when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION); + + return mockBeanDefinition; + } + catch (ClassNotFoundException cause) { + throw newRuntimeException(cause, "Mock for class [%s] failed", beanClass.getName()); + } + } + + private BeanDefinitionRegistry mockBeanDefinitionRegistry(Map registeredBeanDefinitions) { + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + + when(mockBeanDefinitionRegistry.getBeanDefinitionNames()) + .thenReturn(registeredBeanDefinitions.keySet().toArray(new String[registeredBeanDefinitions.size()])); + + when(mockBeanDefinitionRegistry.getBeanDefinition(anyString())) + .thenAnswer(invocation -> registeredBeanDefinitions.get(invocation.getArgument(0))); + + when(mockBeanDefinitionRegistry.containsBeanDefinition(anyString())) + .thenAnswer(invocation -> registeredBeanDefinitions.containsKey(invocation.getArgument(0))); + + doAnswer(invocation -> registeredBeanDefinitions.put(invocation.getArgument(0), invocation.getArgument(1))) + .when(mockBeanDefinitionRegistry).registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + return mockBeanDefinitionRegistry; + } + + @Test + public void cacheableServiceOneRegistersRegionsOneAndTwo() { + + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceOne", mockBeanDefinition(CacheableServiceOne.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceOne")); + + verify(mockBeanDefinitionRegistry, times(1)) + .containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)); + + verify(mockBeanDefinitionRegistry, times(2)) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + Arrays.asList("RegionOne", "RegionTwo").forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); + } + + @Test + public void cacheableServiceTwoRegistersRegionsTwoThreeFourFiveAndSix() { + + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceTwo", mockBeanDefinition(CacheableServiceTwo.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionTwo", "RegionThree", "RegionFour", "RegionFive", "RegionSix"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceTwo")); + + verify(mockBeanDefinitionRegistry, times(1)) + .containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); + + } + + @Test + public void cacheableServiceThreeRegistersSeventeenRegionBeans() { + + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceThree", mockBeanDefinition(CacheableServiceThree.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionSix", "RegionSeven", "RegionEight", "RegionNine", "RegionTen", "RegionEleven", + "RegionTwelve", "RegionThirteen", "RegionFourteen", "RegionFifteen", "RegionSixteen", "RegionSeventeen", + "RegionEighteen", "RegionNineteen", "RegionTwenty", "RegionTwentyOne", "RegionTwentyFive"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceThree")); + + verify(mockBeanDefinitionRegistry, times(1)) + .containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); + } + + @Test + public void cacheableServiceFourRegistersNineteenRegionBeans() { + + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceFour", mockBeanDefinition(CacheableServiceFour.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionSix", "RegionSeven", "RegionEight", "RegionNine", "RegionTen", "RegionEleven", + "RegionTwelve", "RegionThirteen", "RegionFourteen", "RegionFifteen", "RegionSixteen", "RegionSeventeen", + "RegionEighteen", "RegionNineteen", "RegionTwenty", "RegionTwentyOne", "RegionTwentyTwo", + "RegionTwentyThree", "RegionTwentyFour"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceFour")); + + verify(mockBeanDefinitionRegistry, times(1)) + .containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)); + + verify(mockBeanDefinitionRegistry, never()) + .registerBeanDefinition(eq("RegionTwentyFive"), any(BeanDefinition.class)); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class))); + } + + @Test + public void cacheableServiceOneTwoThreeRegistersTwentyTwoRegionBeans() { + + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceOne", mockBeanDefinition(CacheableServiceOne.class)) + .put("cacheableServiceTwo", mockBeanDefinition(CacheableServiceTwo.class)) + .put("cacheableServiceThree", mockBeanDefinition(CacheableServiceThree.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionOne", "RegionTwo", "RegionThree", "RegionFour", "RegionFive", "RegionSix", + "RegionSeven", "RegionEight", "RegionNine", "RegionTen", "RegionEleven", "RegionTwelve", + "RegionThirteen", "RegionFourteen", "RegionFifteen", "RegionSixteen", "RegionSeventeen", + "RegionEighteen", "RegionNineteen", "RegionTwenty", "RegionTwentyOne", "RegionTwentyFive"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceOne")); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceTwo")); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceThree")); + + verify(mockBeanDefinitionRegistry, times(3)) + .containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class))); + } + + @Test + @SuppressWarnings("unchecked") + public void collectCacheNamesForCacheableAndCachePutOnCacheableServiceThreeRegistersRegionFifteenSixteenSeventeen() { + + assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, Cacheable.class, CachePut.class)) + .containsExactly("RegionFifteen", "RegionSixteen", "RegionSeventeen"); + } + + @Test + @SuppressWarnings("unchecked") + public void collectCacheNamesForCacheableOnCacheableServiceThreeRegistersRegionFifteen() { + + assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, Cacheable.class)) + .containsExactly("RegionFifteen"); + } + + @Test + @SuppressWarnings("unchecked") + public void collectCacheNamesForCachePutOnCacheableServiceThreeRegistersRegionSixteenSeventeen() { + + assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, CachePut.class)) + .containsExactly("RegionSixteen", "RegionSeventeen"); + } + + @Test + @SuppressWarnings("unchecked") + public void resolveBeanClassFromBeanClassName() throws ClassNotFoundException { + + AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + + when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenReturn((Class) CacheableServiceOne.class); + + assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null)) + .isEqualTo(CacheableServiceOne.class); + + verify(mockBeanDefinition, times(1)) + .resolveBeanClass(eq(Thread.currentThread().getContextClassLoader())); + + verifyNoMoreInteractions(mockBeanDefinition); + + verifyZeroInteractions(mockBeanDefinitionRegistry); + } + + @Test + @SuppressWarnings("unchecked") + public void resolveBeanClassFromFactoryMethodReturnType() throws ClassNotFoundException { + + AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + + MethodMetadata mockMethodMetadata = mock(MethodMetadata.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(" "); + when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod"); + when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockMethodMetadata); + when(mockMethodMetadata.getReturnTypeName()).thenReturn(CacheableServiceTwo.class.getName()); + + assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null)) + .isEqualTo(CacheableServiceTwo.class); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata(); + verify(mockMethodMetadata, times(1)).getReturnTypeName(); + + verifyNoMoreInteractions(mockBeanDefinition); + verifyNoMoreInteractions(mockMethodMetadata); + + verifyZeroInteractions(mockBeanDefinitionRegistry); + } + + @Test + public void isNotInfrastructureBeanIsTrue() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class); + + assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isTrue(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getRole(); + } + + @Test + public void isNotInfrastructureBeanWithInfrastructureClassIsFalse() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class); + + assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isFalse(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getRole(); + } + + @Test + public void isNotInfrastructureBeanWithInfrastructureRoleIsFalse() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class); + + when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE); + + assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isFalse(); + + verify(mockBeanDefinition, never()).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getRole(); + } + + @Test + public void isNotInfrastructureClassWithBeanDefinitionIsTrue() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class); + + assertThat(this.configuration.isNotInfrastructureClass(mockBeanDefinition)).isTrue(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void isNotInfrastructureClassWithBeanDefinitionIsFalse() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class); + + assertThat(this.configuration.isNotInfrastructureClass(mockBeanDefinition)).isFalse(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + } + + @Test + public void isNotInfrastructureClassIsTrue() { + assertThat(this.configuration.isNotInfrastructureClass(CacheableServiceOne.class.getName())).isTrue(); + assertThat(this.configuration.isNotInfrastructureClass("org.example.app.MyClass")).isTrue(); + } + + @Test + public void isNotInfrastructureClassIsFalse() { + assertThat(this.configuration.isNotInfrastructureClass("org.springframework.SomeType")).isFalse(); + assertThat(this.configuration.isNotInfrastructureClass("org.springframework.core.type.SomeType")) + .isFalse(); + } + + @Test + public void isNotInfrastructureRoleIsTrue() { + + BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); + + when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION).thenReturn(Integer.MAX_VALUE); + + assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isTrue(); + assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isTrue(); + + verify(mockBeanDefinition, times(2)).getRole(); + } + + @Test + public void isNotInfrastructureRoleIsFalse() { + + BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); + + when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE) + .thenReturn(BeanDefinition.ROLE_SUPPORT); + + assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isFalse(); + assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isFalse(); + + verify(mockBeanDefinition, times(2)).getRole(); + } + + @Test + public void isUserLevelMethodWithNullMethodReturnsFalse() { + assertThat(this.configuration.isUserLevelMethod(null)).isFalse(); + } + + @Test + public void isUserLevelMethodWithObjectMethodReturnsFalse() throws NoSuchMethodException { + assertThat(this.configuration.isUserLevelMethod(Object.class.getMethod("equals", Object.class))) + .isFalse(); + } + + @Test + public void isUserLevelMethodWithUserMethodReturnsTrue() throws NoSuchMethodException { + assertThat(this.configuration.isUserLevelMethod(CacheableServiceOne.class.getMethod("cacheableMethodOne"))) + .isTrue(); + } + + @Test + public void resolveAnnotationFromClass() { + + Annotation cacheable = this.configuration.resolveAnnotation(CacheableServiceFour.class, Cacheable.class); + + assertThat(cacheable).isNotNull(); + assertThat(AnnotationUtils.getAnnotationAttributes(cacheable).get("cacheNames")) + .isEqualTo(asArray("RegionFifteen")); + } + + @Test + public void resolveAnnotationFromMethod() throws NoSuchMethodException { + + Method cacheableMethodFour = CacheableServiceFour.class.getMethod("cacheableMethodFour"); + + Annotation cachePut = this.configuration.resolveAnnotation(cacheableMethodFour, CachePut.class); + + assertThat(cachePut).isNotNull(); + assertThat(AnnotationUtils.getAnnotationAttributes(cachePut).get("cacheNames")) + .isEqualTo(asArray("RegionTwentyOne", "RegionTwentyTwo")); + } + + @Test + public void resolveAnnotationIsUnresolvable() throws NoSuchMethodException { + + Method cacheableMethodFive = CacheableServiceFour.class.getMethod("cacheableMethodFive"); + + assertThat(this.configuration.resolveAnnotation(cacheableMethodFive, Cacheable.class)).isNull(); + } + + @Test + public void resolveBeanClassIsUnresolvable() throws ClassNotFoundException { + + AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn("non.existing.bean.Class"); + when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenThrow(new ClassNotFoundException("TEST")); + + assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, null).orElse(null)).isNull(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)) + .resolveBeanClass(eq(Thread.currentThread().getContextClassLoader())); + + verifyNoMoreInteractions(mockBeanDefinition); + } + + @Test + public void resolveBeanClassLoaderReturnsRegistryClassLoader() { + + ClassLoader mockClassLoader = mock(ClassLoader.class); + + DefaultListableBeanFactory mockBeanDefinitionRegistry = mock(DefaultListableBeanFactory.class); + + when(mockBeanDefinitionRegistry.getBeanClassLoader()).thenReturn(mockClassLoader); + + assertThat(this.configuration.resolveBeanClassLoader(mockBeanDefinitionRegistry)).isEqualTo(mockClassLoader); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanClassLoader(); + } + + @Test + public void resolveBeanClassLoaderReturnsThreadContextClassLoader() { + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + + assertThat(this.configuration.resolveBeanClassLoader(mockBeanDefinitionRegistry)) + .isEqualTo(Thread.currentThread().getContextClassLoader()); + + verifyZeroInteractions(mockBeanDefinitionRegistry); + } + + @Test + public void resolveBeanClassNameReturnsBeanDefinitionBeanClassName() { + + BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class); + + assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)) + .isEqualTo(CacheableServiceOne.class.getName()); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verifyNoMoreInteractions(mockBeanDefinition); + } + + @Test + public void resolveBeanClassNameReturnsFactoryMethodReturnType() { + + AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class); + + MethodMetadata mockMethodMetadata = mock(MethodMetadata.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(null); + when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod"); + when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockMethodMetadata); + when(mockMethodMetadata.getReturnTypeName()).thenReturn(CacheableServiceTwo.class.getName()); + + assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)) + .isEqualTo(CacheableServiceTwo.class.getName()); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata(); + } + + @Test + public void resolveBeanClassNameWithNoFactoryMethodMetadataReturnsEmpty() { + + AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(null); + when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod"); + when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(null); + + assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata(); + verifyNoMoreInteractions(mockBeanDefinition); + } + + @Test + public void resolveBeanClassNameWithNonAnnotatedBeanDefinitionReturnsEmpty() { + + BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(null); + when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod"); + + assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodName(); + verifyNoMoreInteractions(mockBeanDefinition); + } + + @Test + public void resolveBeanClassNameWithNoFactoryMethodNameReturnsEmpty() { + + BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); + + when(mockBeanDefinition.getBeanClassName()).thenReturn(null); + when(mockBeanDefinition.getFactoryMethodName()).thenReturn(" "); + + assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull(); + + verify(mockBeanDefinition, times(1)).getBeanClassName(); + verify(mockBeanDefinition, times(1)).getFactoryMethodName(); + verifyNoMoreInteractions(mockBeanDefinition); + } + + @Test + public void safeResolveTypeReturnsType() { + assertThat(this.configuration.safeResolveType(() -> Object.class)).isEqualTo(Object.class); + } + + @Test + public void safeResolveTypeThrowingClassNotFoundExceptionReturnsNull() { + assertThat(this.configuration.safeResolveType(() -> { throw new ClassNotFoundException("TEST"); })).isNull(); + } + + @Service + @Cacheable(cacheNames = { "RegionOne", "RegionTwo" }) + @SuppressWarnings("unused") + static class CacheableServiceOne { + + public void cacheableMethodOne() {} + + } + + @Service + @Cacheable(cacheNames = { "RegionTwo" }) + @CachePut("RegionSix") + @SuppressWarnings("unused") + static class CacheableServiceTwo { + + @Cacheable("RegionThree") + public void cacheableMethodOne() {} + + @CacheEvict(cacheNames = { "RegionThree", "RegionFour" }) + public void cacheableMethodTwo() {} + + @CachePut(cacheNames = "RegionFive") + public void cacheableMethodThree() {} + + } + + @Service + @Caching( + cacheable = { @Cacheable(cacheNames = { "RegionSix", "RegionSeven" }), @Cacheable("RegionEight") }, + evict = { @CacheEvict(cacheNames = { "RegionNine", "RegionTen"}), @CacheEvict("RegionEleven") }, + put = { @CachePut(cacheNames = { "RegionTwelve", "RegionThirteen" }), @CachePut("RegionFourteen") } + ) + @Cacheable("RegionFifteen") + @CachePut(cacheNames = { "RegionSixteen", "RegionSeventeen" }) + @SuppressWarnings("unused") + static class CacheableServiceThree { + + @Caching( + cacheable = { @Cacheable(cacheNames = { "RegionSix", "RegionTwelve" }), @Cacheable("RegionEighteen") }, + put = { @CachePut({ "RegionTen", "RegionNineteen" }), @CachePut("RegionTwenty") } + ) + public void cacheableMethodOne() {} + + @CachePut("RegionTwentyOne") + public void cacheableMethodTwo() {} + + @Cacheable("RegionTwentyFive") + public void cacheableMethodFour() {} + + } + + @SuppressWarnings("unused") + static class CacheableServiceFour extends CacheableServiceThree { + + @Cacheable({ "RegionEleven", "RegionTwentyTwo", "RegionTwentyThree", "RegionTwentyFour" }) + public void cacheableMethodThree() {} + + @Override + @CachePut({ "RegionTwentyOne", "RegionTwentyTwo" }) + public void cacheableMethodFour() {} + + public void cacheableMethodFive() {} + + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsConfigurationUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java similarity index 99% rename from src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsConfigurationUnitTests.java rename to src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java index 95033f28..f89a2fc0 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java @@ -87,7 +87,7 @@ import org.springframework.data.gemfire.mapping.annotation.ReplicateRegion; * @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration * @since 1.9.0 */ -public class EnableEntityDefinedRegionsConfigurationUnitTests { +public class EnableEntityDefinedRegionsUnitTests { private static final AtomicInteger MOCK_ID = new AtomicInteger(0); diff --git a/src/test/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessorUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessorUnitTests.java index 86f69833..88144f82 100644 --- a/src/test/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/support/AutoRegionLookupBeanPostProcessorUnitTests.java @@ -77,9 +77,12 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { } protected Region mockRegion(String regionFullPath) { + Region mockRegion = mock(Region.class); + when(mockRegion.getFullPath()).thenReturn(regionFullPath); when(mockRegion.getName()).thenReturn(toRegionName(regionFullPath)); + return mockRegion; } @@ -90,6 +93,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void setAndGetBeanFactory() { + autoRegionLookupBeanPostProcessor.setBeanFactory(mockBeanFactory); assertThat(autoRegionLookupBeanPostProcessor.getBeanFactory()).isSameAs(mockBeanFactory); @@ -97,6 +101,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void setBeanFactoryToIncompatibleBeanFactoryType() { + BeanFactory mockBeanFactory = mock(BeanFactory.class); exception.expect(IllegalArgumentException.class); @@ -108,7 +113,9 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { } @Test + @SuppressWarnings("all") public void setBeanFactoryToNull() { + exception.expect(IllegalArgumentException.class); exception.expectCause(is(nullValue(Throwable.class))); exception.expectMessage(String.format("BeanFactory [null] must be an instance of %s", @@ -119,15 +126,17 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void getBeanFactoryUninitialized() { + exception.expect(IllegalStateException.class); exception.expectCause(is(nullValue(Throwable.class))); - exception.expectMessage("BeanFactory was not properly initialized"); + exception.expectMessage("BeanFactory was not properly configured"); autoRegionLookupBeanPostProcessor.getBeanFactory(); } @Test public void postProcessBeforeInitializationReturnsBean() { + Object bean = new Object(); assertThat(autoRegionLookupBeanPostProcessor.postProcessBeforeInitialization(bean, "test")).isSameAs(bean); @@ -135,6 +144,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void postProcessAfterInitializationWithNonGemFireCacheBean() { + Object bean = new Object(); AutoRegionLookupBeanPostProcessor autoRegionLookupBeanPostProcessorSpy = @@ -147,8 +157,11 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void registerCacheRegionsAsBeansIsSuccessful() { - Set> expected = CollectionUtils.asSet(mockRegion("one"), mockRegion("two"), mockRegion("three")); - final Set> actual = new HashSet>(expected.size()); + + Set> expected = CollectionUtils.asSet(mockRegion("one"), + mockRegion("two"), mockRegion("three")); + + Set> actual = new HashSet<>(expected.size()); AutoRegionLookupBeanPostProcessor autoRegionLookupBeanPostProcessor = new AutoRegionLookupBeanPostProcessor() { @Override void registerCacheRegionAsBean(Region region) { @@ -173,9 +186,10 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void registerCacheRegionAsBeanIsSuccessful() { + Region mockRegion = mockRegion("Example"); - when(mockRegion.subregions(anyBoolean())).thenReturn(Collections.>emptySet()); + when(mockRegion.subregions(anyBoolean())).thenReturn(Collections.emptySet()); when(mockBeanFactory.containsBean(anyString())).thenReturn(false); autoRegionLookupBeanPostProcessor.setBeanFactory(mockBeanFactory); @@ -190,11 +204,12 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void registerCacheRegionAsBeanRegistersSubRegionIgnoresRootRegion() { + Region mockRootRegion = mockRegion("Root"); Region mockSubRegion = mockRegion("/Root/Sub"); - when(mockRootRegion.subregions(anyBoolean())).thenReturn(CollectionUtils.>asSet(mockSubRegion)); - when(mockSubRegion.subregions(anyBoolean())).thenReturn(Collections.>emptySet()); + when(mockRootRegion.subregions(anyBoolean())).thenReturn(CollectionUtils.asSet(mockSubRegion)); + when(mockSubRegion.subregions(anyBoolean())).thenReturn(Collections.emptySet()); when(mockBeanFactory.containsBean(eq("Root"))).thenReturn(true); when(mockBeanFactory.containsBean(eq("/Root/Sub"))).thenReturn(false); @@ -215,6 +230,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void registerNullCacheRegionAsBeanDoesNothing() { + autoRegionLookupBeanPostProcessor.setBeanFactory(mockBeanFactory); autoRegionLookupBeanPostProcessor.registerCacheRegionAsBean(null); @@ -223,6 +239,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void getBeanNameReturnsRegionFullPath() { + Region mockRegion = mockRegion("/Parent/Child"); assertThat(autoRegionLookupBeanPostProcessor.getBeanName(mockRegion)).isEqualTo("/Parent/Child"); @@ -233,6 +250,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void getBeanNameReturnsRegionName() { + Region mockRegion = mockRegion("/Example"); assertThat(autoRegionLookupBeanPostProcessor.getBeanName(mockRegion)).isEqualTo("Example"); @@ -243,7 +261,10 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void nullSafeSubRegionsWhenSubRegionsIsNotNull() { - Set> mockSubRegions = CollectionUtils.asSet(mockRegion("one"), mockRegion("two")); + + Set> mockSubRegions = + CollectionUtils.asSet(mockRegion("one"), mockRegion("two")); + Region mockRegion = mockRegion("parent"); when(mockRegion.subregions(anyBoolean())).thenReturn(mockSubRegions); @@ -255,6 +276,7 @@ public class AutoRegionLookupBeanPostProcessorUnitTests { @Test public void nullSafeSubRegionsWhenSubRegionsIsNull() { + Region mockRegion = mockRegion("parent"); when(mockRegion.subregions(anyBoolean())).thenReturn(null); diff --git a/src/test/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests.java index f49f309a..d7da0fdc 100644 --- a/src/test/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/support/PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests.java @@ -34,8 +34,6 @@ import org.apache.geode.cache.Region; import org.apache.geode.cache.asyncqueue.AsyncEventQueue; import org.apache.geode.internal.cache.PartitionedRegion; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -68,49 +66,49 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests { } protected ConfigurableListableBeanFactory mockBeanFactory(final Map beanDefinitions) { - final ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class); + + ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class); when(mockBeanFactory.getBeanDefinitionNames()).thenReturn(toStringArray(beanDefinitions.keySet())); - when(mockBeanFactory.getBeanNamesForType(isA(Class.class))).then(new Answer() { - @Override - public String[] answer(InvocationOnMock invocation) throws Throwable { - Object[] arguments = invocation.getArguments(); + when(mockBeanFactory.getBeanNamesForType(isA(Class.class))).then(invocation -> { - assertThat(arguments).isNotNull(); - assertThat(arguments.length).isEqualTo(1); - assertThat(arguments[0]).isInstanceOf(Class.class); + Object[] arguments = invocation.getArguments(); - Class beanType = (Class) arguments[0]; + assertThat(arguments).isNotNull(); + assertThat(arguments.length).isEqualTo(1); + assertThat(arguments[0]).isInstanceOf(Class.class); - List beanNames = new ArrayList(beanDefinitions.size()); + Class beanType = (Class) arguments[0]; - for (Map.Entry entry : beanDefinitions.entrySet()) { - BeanDefinition beanDefinition = entry.getValue(); + List beanNames = new ArrayList<>(beanDefinitions.size()); - if (isBeanType(beanDefinition, beanType)) { - beanNames.add(entry.getKey()); - } + for (Map.Entry entry : beanDefinitions.entrySet()) { + BeanDefinition beanDefinition = entry.getValue(); + + if (isBeanType(beanDefinition, beanType)) { + beanNames.add(entry.getKey()); } - - return toStringArray(beanNames); } + + return toStringArray(beanNames); }); - when(mockBeanFactory.getBeanDefinition(anyString())).then(new Answer() { - @Override - public BeanDefinition answer(InvocationOnMock invocation) throws Throwable { - Object[] arguments = invocation.getArguments(); - assertThat(arguments).isNotNull(); - assertThat(arguments.length).isEqualTo(1); - return beanDefinitions.get(String.valueOf(arguments[0])); - } + when(mockBeanFactory.getBeanDefinition(anyString())).then(invocation -> { + + Object[] arguments = invocation.getArguments(); + + assertThat(arguments).isNotNull(); + assertThat(arguments.length).isEqualTo(1); + + return beanDefinitions.get(String.valueOf(arguments[0])); }); return mockBeanFactory; } protected static BeanDefinitionBuilder newBeanDefinitionBuilder(Object beanClassObject, String... dependencies) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); if (beanClassObject instanceof Class) { @@ -124,6 +122,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests { } protected static BeanDefinitionBuilder addDependsOn(BeanDefinitionBuilder builder, String... dependencies) { + for (String dependency : dependencies) { builder.addDependsOn(dependency); } @@ -181,6 +180,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests { @Test public void initializedPdxDiskStoreAwareBeanFactoryPostProcessor() { + PdxDiskStoreAwareBeanFactoryPostProcessor postProcessor = new PdxDiskStoreAwareBeanFactoryPostProcessor("testPdxDiskStoreName"); @@ -191,6 +191,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessorUnitTests { @Test @SuppressWarnings("all") public void postProcessBeanFactory() { + Map beanDefinitions = new HashMap(13); beanDefinitions.put("someBean", defineBean("org.company.app.domain.SomeBean", "someOtherBean")); diff --git a/src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java b/src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java new file mode 100644 index 00000000..a5c3b387 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/support/MapBuilder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 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.test.support; + +import java.util.HashMap; +import java.util.Map; + +/** + * The {@link MapBuilder} class employs the Builder Software Design Pattern to build a {@link Map}. + * + * @author John Blum + * @see java.util.Map + * @since 2.0.0 + */ +public class MapBuilder { + + public static MapBuilder newMapBuilder() { + return new MapBuilder<>(); + } + + private final Map map = new HashMap<>(); + + public MapBuilder put(KEY key, VALUE value) { + this.map.put(key, value); + return this; + } + + public MapBuilder remove(KEY key) { + this.map.remove(key); + return this; + } + + public Map build() { + return this.map; + } +}