From fd3d318d9eb15c2fec0f70d0fbda18a418bc2136 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 22 Sep 2017 13:40:20 -0700 Subject: [PATCH] SGF-672 - Use Geode's DEFAULT Pool when a Pool cannot be resolved from the Spring context. --- .../client/ClientCacheFactoryBean.java | 3 + .../client/ClientRegionFactoryBean.java | 24 +- .../data/gemfire/client/PoolFactoryBean.java | 3 + .../EnableEntityDefinedRegions.java | 18 + .../gemfire/config/annotation/EnablePool.java | 6 +- .../EntityDefinedRegionsConfiguration.java | 287 ++++----- .../config/annotation/IndexConfiguration.java | 2 +- .../AbstractAnnotationConfigSupport.java | 50 +- .../EmbeddedServiceConfigurationSupport.java | 5 - ...emFireCacheTypeAwareRegionFactoryBean.java | 17 +- .../GemFireComponentClassTypeScanner.java | 107 ++-- .../mapping/annotation/ClientRegion.java | 8 +- .../mapping/annotation/LocalRegion.java | 2 +- .../mapping/annotation/PartitionRegion.java | 2 +- .../mapping/annotation/ReplicateRegion.java | 2 +- .../data/gemfire/util/SpringUtils.java | 23 +- .../client/ClientCacheIntegrationTests.java | 168 ++++++ .../data/gemfire/client/ClientCacheTest.java | 63 -- .../client/ClientRegionFactoryBeanTest.java | 59 +- .../client/ClientRegionIntegrationTests.java | 72 +++ ...ClientRegionWithCacheLoaderWriterTest.java | 9 +- .../LocalOnlyClientCacheIntegrationTest.java | 25 +- .../client/MultipleClientCacheTest.java | 58 -- .../EnableEntityDefinedRegionsUnitTests.java | 6 +- .../config/xml/ClientRegionNamespaceTest.java | 1 + .../test/mock/MockGemFireObjectsSupport.java | 568 +++++++++++++----- .../gemfire/test/mock/MockObjectsSupport.java | 36 ++ .../mock/annotation/EnableGemFireMocking.java | 11 + .../GemFireMockingConfiguration.java | 60 +- .../MockGemFireObjectsBeanPostProcessor.java | 53 +- ...eObjectsApplicationContextInitializer.java | 2 +- .../gemfire/util/SpringUtilsUnitTests.java | 75 ++- ...gionWithCacheLoaderWriterTest-context.xml} | 1 - .../gemfire/client/client-cache-no-close.xml | 20 - .../data/gemfire/client/client-cache.xml | 20 - .../data/gemfire/config/xml/client-ns.xml | 1 + 36 files changed, 1247 insertions(+), 620 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java delete mode 100644 src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java delete mode 100644 src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java rename src/test/resources/org/springframework/data/gemfire/client/{clientcache-with-region-using-cache-loader-writer.xml => ClientRegionWithCacheLoaderWriterTest-context.xml} (97%) delete mode 100644 src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml delete mode 100644 src/test/resources/org/springframework/data/gemfire/client/client-cache.xml diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java index d0b4d1c9..8578bad6 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -337,6 +337,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat * @see #findPool(String) */ Pool resolvePool() { + Pool localPool = getPool(); if (localPool == null) { @@ -411,6 +412,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { + if (isReadyForEvents()) { try { this.fetchCache().readyForEvents(); @@ -772,6 +774,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat * @see #getReadyForEvents() */ public boolean isReadyForEvents() { + Boolean readyForEvents = getReadyForEvents(); if (readyForEvents != null) { diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java index db7a42bc..4860ef41 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -40,6 +40,7 @@ import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientRegionFactory; import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.cache.client.Pool; +import org.apache.geode.cache.client.PoolManager; import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; @@ -75,6 +76,9 @@ import org.springframework.util.StringUtils; @SuppressWarnings("unused") public class ClientRegionFactoryBean extends RegionLookupFactoryBean implements DisposableBean { + public static final String DEFAULT_POOL_NAME = "DEFAULT"; + public static final String GEMFIRE_POOL_NAME = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME; + private boolean close = false; private boolean destroy = false; @@ -278,8 +282,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean } } else { - resolvedShortcut = (isPersistent() ? ClientRegionShortcut.LOCAL_PERSISTENT - : ClientRegionShortcut.LOCAL); + resolvedShortcut = (isPersistent() ? ClientRegionShortcut.LOCAL_PERSISTENT : ClientRegionShortcut.LOCAL); } } @@ -292,14 +295,17 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean /* (non-Javadoc) */ private String resolvePoolName() { - String poolName = this.poolName; + return Optional.of(getPoolName()).filter(this::isPoolResolvable).orElse(null); + } - if (!StringUtils.hasText(poolName)) { - String defaultPoolName = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME; - poolName = (getBeanFactory().containsBean(defaultPoolName) ? defaultPoolName : poolName); - } + /* (non-Javadoc) */ + private String getPoolName() { + return Optional.ofNullable(this.poolName).filter(StringUtils::hasText).orElse(GEMFIRE_POOL_NAME); + } - return poolName; + /* (non-Javadoc) */ + private boolean isPoolResolvable(String poolName) { + return (getBeanFactory().containsBean(poolName) || (PoolManager.find(poolName) != null)); } /* (non-Javadoc) */ @@ -424,6 +430,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean private Region registerInterests(Region region) { stream(nullSafeArray(this.interests, Interest.class)).forEach(interest -> { + if (interest.isRegexType()) { region.registerInterestRegex((String) interest.getKey(), interest.getPolicy(), interest.isDurable(), interest.isReceiveValues()); @@ -447,6 +454,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean public void destroy() throws Exception { Optional.ofNullable(getObject()).ifPresent(region -> { + if (isClose()) { if (!region.getRegionService().isClosed()) { try { diff --git a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java index 1a11b3f0..8410e2aa 100644 --- a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java @@ -290,6 +290,7 @@ public class PoolFactoryBean extends AbstractFactoryBeanSupport implements protected PoolFactory configure(PoolFactory poolFactory) { Optional.ofNullable(poolFactory).ifPresent(it -> { + it.setFreeConnectionTimeout(this.freeConnectionTimeout); it.setIdleTimeout(this.idleTimeout); it.setLoadConditioningInterval(this.loadConditioningInterval); @@ -395,7 +396,9 @@ public class PoolFactoryBean extends AbstractFactoryBeanSupport implements /* (non-Javadoc) */ public Pool getPool() { + return Optional.ofNullable(this.pool).orElseGet(() -> new PoolAdapter() { + @Override public boolean isDestroyed() { Pool pool = PoolFactoryBean.this.pool; 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 3675fe60..b9672545 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 @@ -25,10 +25,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.Pool; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.annotation.AliasFor; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.mapping.annotation.ClientRegion; /** * The {@link EnableEntityDefinedRegions} annotation marks a Spring {@link Configuration @Configuration} application @@ -46,6 +49,11 @@ import org.springframework.core.annotation.AliasFor; * @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration * @see org.springframework.data.gemfire.config.annotation.IndexConfiguration * @see org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean + * @see org.springframework.data.gemfire.mapping.annotation.ClientRegion + * @see org.springframework.data.gemfire.mapping.annotation.LocalRegion + * @see org.springframework.data.gemfire.mapping.annotation.PartitionRegion + * @see org.springframework.data.gemfire.mapping.annotation.Region + * @see org.springframework.data.gemfire.mapping.annotation.ReplicateRegion * @since 1.9.0 */ @Target(ElementType.TYPE) @@ -111,6 +119,16 @@ public @interface EnableEntityDefinedRegions { */ ComponentScan.Filter[] includeFilters() default {}; + /** + * When this annotation is applied in a cache client application, the {@literal poolName} attribute refers to + * the default name of the GemFire/Geode {@link Pool} assigned to the client {@link Region Region(s)}. + * + * This value can be overridden by annotating entities with th e{@link ClientRegion} annotation. + * + * Defaults to {@literal DEFAULT}. + */ + String poolName() default ClientRegionFactoryBean.DEFAULT_POOL_NAME; + /** * Determines whether the created {@link Region} will have strongly-typed key and value constraints * based on the ID and {@link Class} type of application persistent entity. diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePool.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePool.java index efcdfe32..9128cfc3 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePool.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnablePool.java @@ -146,9 +146,9 @@ public @interface EnablePool { /** * Specifies the {@link String name} of the client {@link Pool} in Pivotal GemFire/Apache Geode, which is also - * used as the Spring bean name in the container as well as the name - * (e.g. {@literal spring.data.gemfire.pool..max-connections} used in the resolution of {@link Pool} - * properties from {@literal application.properties} that are specific to this {@link Pool}. + * used as the name of the bean registered in the Spring container as well as the name used in the resolution + * of {@link Pool} properties from {@literal application.properties} + * (e.g. {@literal spring.data.gemfire.pool..max-connections}), that are specific to this {@link Pool}. */ String name(); diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EntityDefinedRegionsConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EntityDefinedRegionsConfiguration.java index 37e59670..58b845a5 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EntityDefinedRegionsConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EntityDefinedRegionsConfiguration.java @@ -22,6 +22,7 @@ import static org.springframework.data.gemfire.util.ArrayUtils.defaultIfEmpty; import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; +import static org.springframework.data.gemfire.util.SpringUtils.safeGetValue; import java.lang.annotation.Annotation; import java.util.Collections; @@ -37,10 +38,7 @@ import java.util.stream.Collectors; import org.apache.geode.cache.Region; import org.apache.geode.cache.client.ClientRegionShortcut; import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -50,7 +48,6 @@ import org.springframework.beans.factory.support.ManagedList; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AspectJTypeFilter; @@ -66,6 +63,7 @@ import org.springframework.data.gemfire.RegionLookupFactoryBean; import org.springframework.data.gemfire.ReplicatedRegionFactoryBean; import org.springframework.data.gemfire.ScopeType; import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport; import org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean; import org.springframework.data.gemfire.config.annotation.support.GemFireComponentClassTypeScanner; import org.springframework.data.gemfire.config.xml.GemfireConstants; @@ -76,7 +74,7 @@ import org.springframework.data.gemfire.mapping.annotation.ClientRegion; import org.springframework.data.gemfire.mapping.annotation.LocalRegion; import org.springframework.data.gemfire.mapping.annotation.PartitionRegion; import org.springframework.data.gemfire.mapping.annotation.ReplicateRegion; -import org.springframework.data.gemfire.util.CollectionUtils; +import org.springframework.data.gemfire.util.SpringUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -91,9 +89,6 @@ import org.springframework.util.StringUtils; * @see java.lang.ClassLoader * @see java.lang.annotation.Annotation * @see org.apache.geode.cache.Region - * @see org.springframework.beans.factory.BeanClassLoaderAware - * @see org.springframework.beans.factory.BeanFactory - * @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.beans.factory.config.BeanDefinition * @see org.springframework.beans.factory.support.BeanDefinitionBuilder * @see org.springframework.beans.factory.support.BeanDefinitionRegistry @@ -116,8 +111,9 @@ import org.springframework.util.StringUtils; * @see org.springframework.data.gemfire.mapping.annotation.Region * @since 1.9.0 */ -public class EntityDefinedRegionsConfiguration - implements BeanClassLoaderAware, BeanFactoryAware, ImportBeanDefinitionRegistrar { +@SuppressWarnings("unused") +public class EntityDefinedRegionsConfiguration extends AbstractAnnotationConfigSupport + implements ImportBeanDefinitionRegistrar { protected static final Class DEFAULT_REGION_FACTORY_BEAN_CLASS = GemFireCacheTypeAwareRegionFactoryBean.class; @@ -134,10 +130,6 @@ public class EntityDefinedRegionsConfiguration DEFAULT_REGION_FACTORY_BEAN_CLASS); } - private BeanFactory beanFactory; - - private ClassLoader beanClassLoader; - private GemfireMappingContext mappingContext; @Autowired(required = false) @@ -153,112 +145,20 @@ public class EntityDefinedRegionsConfiguration * @see java.lang.annotation.Annotation * @see java.lang.Class */ + @Override protected Class getAnnotationType() { return EnableEntityDefinedRegions.class; } /** - * Returns the name of the {@link Annotation} type that configures and creates {@link Region Regions} - * for application persistent entities. + * Registers {@link Region} bean definitions in the Spring context for all application domain object + * that have been identified as {@link GemfirePersistentEntity persistent entities}. * - * @return the name of the {@link Annotation} type that configures and creates {@link Region Regions} - * for application persistent entities. - * @see java.lang.Class#getName() - * @see #getAnnotationType() - */ - protected String getAnnotationTypeName() { - return getAnnotationType().getName(); - } - - /** - * Returns the simple name of the {@link Annotation} type that configures and creates {@link Region Regions} - * for application persistent entities. - * - * @return the simple name of the {@link Annotation} type that configures and creates {@link Region Regions} - * for application persistent entities. - * @see java.lang.Class#getSimpleName() - * @see #getAnnotationType() - */ - @SuppressWarnings("unused") - protected String getAnnotationTypeSimpleName() { - return getAnnotationType().getSimpleName(); - } - - /** - * @inheritDoc - */ - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.beanClassLoader = classLoader; - } - - /* (non-Javadoc) */ - @SuppressWarnings("unused") - protected ClassLoader getBeanClassLoader() { - return this.beanClassLoader; - } - - /** - * @inheritDoc - */ - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - /* (non-Javadoc) */ - protected BeanFactory getBeanFactory() { - return this.beanFactory; - } - - /* (non-Javadoc) */ - protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) { - return isAnnotationPresent(importingClassMetadata, getAnnotationTypeName()); - } - - /* (non-Javadoc) */ - protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, String annotationName) { - return importingClassMetadata.hasAnnotation(annotationName); - } - - /* (non-Javadoc) */ - protected AnnotationAttributes getAnnotationAttributes(Annotation annotation) { - return AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(annotation)); - } - - /* (non-Javadoc) */ - protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) { - return getAnnotationAttributes(importingClassMetadata, getAnnotationTypeName()); - } - - /* (non-Javadoc) */ - protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata, - String annotationName) { - - return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationName)); - } - - /* (non-Javadoc) */ - protected GemfirePersistentEntity getPersistentEntity(Class persistentEntityType) { - return resolveMappingContext().getPersistentEntity(persistentEntityType); - } - - /* (non-Javadoc) */ - protected GemfireMappingContext resolveMappingContext() { - return Optional.ofNullable(this.mappingContext).orElseGet(() -> { - try { - this.mappingContext = getBeanFactory().getBean(GemfireMappingContext.class); - } - catch (Throwable ignore) { - this.mappingContext = new GemfireMappingContext(); - } - - return this.mappingContext; - }); - } - - /** - * @inheritDoc + * @param importingClassMetadata {@link Class} with the {@link EnableEntityDefinedRegions} annotation. + * @param registry {@link BeanDefinitionRegistry} used to register the {@link Region} bean definitions + * in the Spring context. + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.core.type.AnnotationMetadata */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { @@ -267,6 +167,8 @@ public class EntityDefinedRegionsConfiguration AnnotationAttributes enableEntityDefinedRegionsAttributes = getAnnotationAttributes(importingClassMetadata); + String poolName = enableEntityDefinedRegionsAttributes.getString("poolName"); + boolean strict = enableEntityDefinedRegionsAttributes.getBoolean("strict"); newGemFireComponentClassTypeScanner(importingClassMetadata, enableEntityDefinedRegionsAttributes).scan() @@ -274,7 +176,7 @@ public class EntityDefinedRegionsConfiguration GemfirePersistentEntity persistentEntity = getPersistentEntity(persistentEntityClass); - registerRegionBeanDefinition(persistentEntity, strict, registry); + registerRegionBeanDefinition(persistentEntity, poolName, strict, registry); postProcess(importingClassMetadata, registry, persistentEntity); }); } @@ -290,7 +192,7 @@ public class EntityDefinedRegionsConfiguration return GemFireComponentClassTypeScanner.from(resolvedBasePackages).with(resolveBeanClassLoader()) .withExcludes(resolveExcludes(enableEntityDefinedRegionsAttributes)) .withIncludes(resolveIncludes(enableEntityDefinedRegionsAttributes)) - .withIncludes(regionAnnotatedPersistentEntityTypeFilters()); + .withIncludes(resolveRegionAnnotatedPersistentEntityTypeFilters()); } /* (non-Javadoc) */ @@ -315,12 +217,6 @@ public class EntityDefinedRegionsConfiguration return resolvedBasePackages; } - /* (non-Javadoc) */ - protected ClassLoader resolveBeanClassLoader() { - return Optional.ofNullable(this.beanClassLoader) - .orElseGet(() -> Thread.currentThread().getContextClassLoader()); - } - /* (non-Javadoc) */ protected Iterable resolveExcludes(AnnotationAttributes enableEntityDefinedRegionsAttributes) { return parseFilters(enableEntityDefinedRegionsAttributes.getAnnotationArray("excludeFilters")); @@ -332,25 +228,31 @@ public class EntityDefinedRegionsConfiguration } /* (non-Javadoc) */ - private Iterable parseFilters(AnnotationAttributes[] componentScanFilterAttributes) { + @SuppressWarnings("unchecked") + protected Iterable resolveRegionAnnotatedPersistentEntityTypeFilters() { - Set typeFilters = new HashSet<>(); - - stream(nullSafeArray(componentScanFilterAttributes, AnnotationAttributes.class)) - .forEach(filterAttributes -> CollectionUtils.addAll(typeFilters, typeFiltersFor(filterAttributes))); - - return typeFilters; + return org.springframework.data.gemfire.mapping.annotation.Region.REGION_ANNOTATION_TYPES.stream() + .map(AnnotationTypeFilter::new) + .collect(Collectors.toSet()); + } + + private Iterable parseFilters(AnnotationAttributes[] componentScanFilterAttributes) { + + return stream(nullSafeArray(componentScanFilterAttributes, AnnotationAttributes.class)) + .flatMap(filterAttributes -> typeFiltersFor(filterAttributes).stream()) + .collect(Collectors.toSet()); } - /* (non-Javadoc) */ @SuppressWarnings("unchecked") - private Iterable typeFiltersFor(AnnotationAttributes filterAttributes) { + private Set typeFiltersFor(AnnotationAttributes filterAttributes) { Set typeFilters = new HashSet<>(); + FilterType filterType = filterAttributes.getEnum("type"); stream(nullSafeArray(filterAttributes.getClassArray("value"), Class.class)) .forEach(filterClass -> { + switch (filterType) { case ANNOTATION: Assert.isAssignable(Annotation.class, filterClass, @@ -383,7 +285,7 @@ public class EntityDefinedRegionsConfiguration "Illegal filter type [%s] when 'patterns' are specified", filterType); } } - }); + }); return typeFilters; } @@ -396,29 +298,61 @@ public class EntityDefinedRegionsConfiguration * @return a {@link String} array. */ private String[] nullSafeGetPatterns(AnnotationAttributes filterAttributes) { - try { - return nullSafeArray(filterAttributes.getStringArray("pattern"), String.class); - } - catch (IllegalArgumentException ignore) { - return new String[0]; - } + + return SpringUtils.safeGetValue(() -> + nullSafeArray(filterAttributes.getStringArray("pattern"), String.class), () -> new String[0]); } - /* (non-Javadoc) */ - @SuppressWarnings("unchecked") - protected Iterable regionAnnotatedPersistentEntityTypeFilters() { - - Set regionAnnotatedPersistentEntityTypeFilters = new HashSet<>(); - - org.springframework.data.gemfire.mapping.annotation.Region.REGION_ANNOTATION_TYPES.forEach( - annotationType -> regionAnnotatedPersistentEntityTypeFilters.add(new AnnotationTypeFilter(annotationType))); - - return regionAnnotatedPersistentEntityTypeFilters; + /** + * Returns the associated {@link GemfirePersistentEntity persistent entity} for the given application + * domain object type. + * + * @param persistentEntityType {@link Class type} of the application domain object used to lookup + * the {@link GemfirePersistentEntity persistent entity} from the {@link @GemfireMappingContext mapping context}. + * @return the {@link GemfirePersistentEntity persistent entity} for the given application domain object type. + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + * @see java.lang.Class + */ + protected GemfirePersistentEntity getPersistentEntity(Class persistentEntityType) { + return resolveMappingContext().getPersistentEntity(persistentEntityType); } - /* (non-Javadoc) */ - protected void registerRegionBeanDefinition(GemfirePersistentEntity persistentEntity, boolean strict, - BeanDefinitionRegistry registry) { + /** + * Resolves the {@link GemfireMappingContext mapping context} by returning the configured + * {@link GemfireMappingContext mapping context} if present, or attempts to lookup + * the {@link GemfireMappingContext mapping context} from the configured {@link BeanFactory}. + * If the lookup is unsuccessful, then this method will return a new {@link GemfireMappingContext mapping context}. + * + * @return the resolved {@link GemfireMappingContext mapping context}. + * @see org.springframework.data.gemfire.mapping.GemfireMappingContext + */ + protected GemfireMappingContext resolveMappingContext() { + + return Optional.ofNullable(this.mappingContext).orElseGet(() -> { + try { + this.mappingContext = beanFactory().getBean(GemfireMappingContext.class); + } + catch (Throwable ignore) { + this.mappingContext = new GemfireMappingContext(); + } + + return this.mappingContext; + }); + } + + /** + * Registers an individual bean definition in the Spring container for the {@link Region} determined from + * the application domain object, {@link GemfirePersistentEntity persistent entity}. + * + * @param persistentEntity {@link GemfirePersistentEntity} from which to the resolve the {@link Region}. + * @param strict boolean value indicating whether the key and value constraints on the {@link Region} should be set. + * @param registry {@link BeanDefinitionRegistry} used to register the {@link Region} bean definition + * in the Spring context. + * @see org.springframework.beans.factory.support.BeanDefinitionRegistry + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + */ + protected void registerRegionBeanDefinition(GemfirePersistentEntity persistentEntity, String poolName, + boolean strict, BeanDefinitionRegistry registry) { BeanDefinitionBuilder regionFactoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(resolveRegionFactoryBeanClass(persistentEntity)) @@ -426,19 +360,29 @@ public class EntityDefinedRegionsConfiguration .addPropertyValue("regionConfigurers", resolveRegionConfigurers()) .addPropertyValue("close", false); - setRegionAttributes(persistentEntity, regionFactoryBeanBuilder, strict); + setRegionAttributes(persistentEntity, regionFactoryBeanBuilder, poolName, strict); registry.registerBeanDefinition(persistentEntity.getRegionName(), regionFactoryBeanBuilder.getBeanDefinition()); } /* (non-Javadoc) */ - private List resolveRegionConfigurers() { + @SuppressWarnings("unchecked") + protected Class resolveRegionFactoryBeanClass( + GemfirePersistentEntity persistentEntity) { + + return Optional.>ofNullable( + regionAnnotationToRegionFactoryBeanClass.get(persistentEntity.getRegionAnnotationType())) + .orElse(DEFAULT_REGION_FACTORY_BEAN_CLASS); + } + + /* (non-Javadoc) */ + protected List resolveRegionConfigurers() { return Optional.ofNullable(this.regionConfigurers) .filter(regionConfigurers -> !regionConfigurers.isEmpty()) .orElseGet(() -> - Optional.ofNullable(this.beanFactory) + Optional.ofNullable(beanFactory()) .filter(beanFactory -> beanFactory instanceof ListableBeanFactory) .map(beanFactory -> { Map beansOfType = ((ListableBeanFactory) beanFactory) @@ -450,21 +394,12 @@ public class EntityDefinedRegionsConfiguration ); } - /* (non-Javadoc) */ - @SuppressWarnings("unchecked") - protected Class resolveRegionFactoryBeanClass( - GemfirePersistentEntity persistentEntity) { - - return Optional.>ofNullable( - regionAnnotationToRegionFactoryBeanClass.get(persistentEntity.getRegionAnnotationType())) - .orElse(DEFAULT_REGION_FACTORY_BEAN_CLASS); - } - /* (non-Javadoc) */ protected BeanDefinitionBuilder setRegionAttributes(GemfirePersistentEntity persistentEntity, - BeanDefinitionBuilder regionFactoryBeanBuilder, boolean strict) { + BeanDefinitionBuilder regionFactoryBeanBuilder, String poolName, boolean strict) { Optional.ofNullable(persistentEntity.getRegionAnnotation()).ifPresent(regionAnnotation -> { + AnnotationAttributes regionAnnotationAttributes = getAnnotationAttributes(regionAnnotation); if (strict) { @@ -473,10 +408,11 @@ public class EntityDefinedRegionsConfiguration } if (regionAnnotationAttributes.containsKey("diskStoreName")) { + String diskStoreName = regionAnnotationAttributes.getString("diskStoreName"); - setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "diskStoreName", - diskStoreName, ""); + setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "diskStoreName", diskStoreName, + ""); if (StringUtils.hasText(diskStoreName)) { regionFactoryBeanBuilder.addDependsOn(diskStoreName); @@ -506,7 +442,7 @@ public class EntityDefinedRegionsConfiguration regionAnnotationAttributes.getBoolean("ignoreJta"), false); } - setClientRegionAttributes(regionAnnotationAttributes, regionFactoryBeanBuilder); + setClientRegionAttributes(regionAnnotationAttributes, poolName, regionFactoryBeanBuilder); setPartitionRegionAttributes(regionAnnotationAttributes, regionFactoryBeanBuilder, regionAttributesFactoryBeanBuilder); @@ -519,7 +455,7 @@ public class EntityDefinedRegionsConfiguration /* (non-Javadoc) */ protected Class resolveDomainType(GemfirePersistentEntity persistentEntity) { - return Optional.ofNullable(persistentEntity.getType()).orElse(Object.class); + return persistentEntity.getType(); } /* (non-Javadoc) */ @@ -550,9 +486,9 @@ public class EntityDefinedRegionsConfiguration /* (non-Javadoc) */ protected BeanDefinitionBuilder setClientRegionAttributes(AnnotationAttributes regionAnnotationAttributes, - BeanDefinitionBuilder regionFactoryBeanBuilder) { + String poolName, BeanDefinitionBuilder regionFactoryBeanBuilder) { - if (regionAnnotationAttributes.containsKey("poolName")) { + if (isPoolNameConfigured(regionAnnotationAttributes, poolName)) { setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "poolName", regionAnnotationAttributes.getString("poolName"), null); } @@ -565,6 +501,15 @@ public class EntityDefinedRegionsConfiguration return regionFactoryBeanBuilder; } + private boolean isPoolNameConfigured(AnnotationAttributes regionAnnotationAttributes, String poolName) { + return (regionAnnotationAttributes.containsKey("poolName") + || !ClientRegionFactoryBean.DEFAULT_POOL_NAME.equals(poolName)); + } + + private String resolvePoolName(AnnotationAttributes regionAnnotationAttributes, String poolName) { + return safeGetValue(() -> regionAnnotationAttributes.getString("poolName"), poolName); + } + /* (non-Javadoc) */ protected BeanDefinitionBuilder setPartitionRegionAttributes(AnnotationAttributes regionAnnotationAttributes, BeanDefinitionBuilder regionFactoryBeanBuilder, BeanDefinitionBuilder regionAttributesFactoryBeanBuilder) { diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java index 56820b58..6697f447 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java @@ -276,7 +276,7 @@ public class IndexConfiguration extends EntityDefinedRegionsConfiguration { return Optional.ofNullable(this.indexConfigurers) .filter(indexConfigurers -> !indexConfigurers.isEmpty()) .orElseGet(() -> - Optional.of(getBeanFactory()) + Optional.of(beanFactory()) .filter(beanFactory -> beanFactory instanceof ListableBeanFactory) .map(beanFactory -> { Map beansOfType = ((ListableBeanFactory) beanFactory) diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/AbstractAnnotationConfigSupport.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/AbstractAnnotationConfigSupport.java index 5bff35ec..c55214c6 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/AbstractAnnotationConfigSupport.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/AbstractAnnotationConfigSupport.java @@ -20,6 +20,7 @@ package org.springframework.data.gemfire.config.annotation.support; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -40,7 +41,10 @@ import org.springframework.context.EnvironmentAware; import org.springframework.context.expression.BeanFactoryAccessor; import org.springframework.context.expression.EnvironmentAccessor; import org.springframework.context.expression.MapAccessor; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; @@ -186,12 +190,39 @@ public abstract class AbstractAnnotationConfigSupport return LogFactory.getLog(getClass()); } + /* (non-Javadoc) */ + protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) { + return isAnnotationPresent(importingClassMetadata, getAnnotationTypeName()); + } + + /* (non-Javadoc) */ + protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, String annotationName) { + return importingClassMetadata.hasAnnotation(annotationName); + } + + /* (non-Javadoc) */ + protected AnnotationAttributes getAnnotationAttributes(Annotation annotation) { + return AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(annotation)); + } + + /* (non-Javadoc) */ + protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) { + return getAnnotationAttributes(importingClassMetadata, getAnnotationTypeName()); + } + + /* (non-Javadoc) */ + protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata, + String annotationName) { + + return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationName)); + } + /** * Returns the cache application {@link java.lang.annotation.Annotation} type pertaining to this configuration. * * @return the cache application {@link java.lang.annotation.Annotation} type used by this application. */ - protected abstract Class getAnnotationType(); + protected abstract Class getAnnotationType(); /** * Returns the fully-qualified {@link Class#getName() class name} of the cache application @@ -238,6 +269,19 @@ public abstract class AbstractAnnotationConfigSupport return this.beanClassLoader; } + /** + * Resolves the {@link ClassLoader bean ClassLoader} to the configured {@link ClassLoader} + * or the {@link Thread#getContextClassLoader() Thread Context ClassLoader}. + * + * @return the configured {@link ClassLoader} or the + * {@link Thread#getContextClassLoader() Thread Context ClassLoader}. + * @see java.lang.Thread#getContextClassLoader() + * @see #beanClassLoader() + */ + protected ClassLoader resolveBeanClassLoader() { + return Optional.ofNullable(beanClassLoader()).orElseGet(() -> Thread.currentThread().getContextClassLoader()); + } + /** * {@inheritDoc} */ @@ -282,8 +326,10 @@ public abstract class AbstractAnnotationConfigSupport } /** + * Returns a reference to the {@link EvaluationContext} used to evaluate SpEL expressions. * - * @return + * @return a reference to the {@link EvaluationContext} used to evaluate SpEL expressions. + * @see org.springframework.expression.EvaluationContext */ protected EvaluationContext evaluationContext() { return this.evaluationContext; diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java index 5edb7d42..4730508f 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java @@ -132,11 +132,6 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota return !CollectionUtils.isEmpty(properties); } - /* (non-Javadoc) */ - protected Map getAnnotationAttributes(AnnotationMetadata importingClassMetadata) { - return importingClassMetadata.getAnnotationAttributes(getAnnotationTypeName()); - } - /* (non-Javadoc) */ protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry, Properties customGemFireProperties) { diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java index cbc4bd9d..256cfb56 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java @@ -30,13 +30,13 @@ import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.client.ClientRegionShortcut; +import org.apache.geode.cache.client.PoolManager; import org.springframework.beans.factory.FactoryBean; import org.springframework.data.gemfire.GemfireUtils; import org.springframework.data.gemfire.GenericRegionFactoryBean; import org.springframework.data.gemfire.RegionLookupFactoryBean; import org.springframework.data.gemfire.client.ClientRegionFactoryBean; import org.springframework.data.gemfire.config.annotation.RegionConfigurer; -import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.util.StringUtils; /** @@ -47,7 +47,6 @@ import org.springframework.util.StringUtils; * @author John Blum * @see org.apache.geode.cache.GemFireCache * @see org.apache.geode.cache.Region - * @see org.springframework.beans.factory.FactoryBean * @see org.springframework.data.gemfire.GenericRegionFactoryBean * @see org.springframework.data.gemfire.RegionLookupFactoryBean * @see org.springframework.data.gemfire.RegionFactoryBean @@ -109,12 +108,13 @@ public class GemFireCacheTypeAwareRegionFactoryBean extends RegionLookupFa clientRegionFactory.setCache(gemfireCache); clientRegionFactory.setClose(isClose()); clientRegionFactory.setKeyConstraint(getKeyConstraint()); - clientRegionFactory.setPoolName(getPoolName()); clientRegionFactory.setRegionConfigurers(this.regionConfigurers); clientRegionFactory.setRegionName(regionName); clientRegionFactory.setShortcut(getClientRegionShortcut()); clientRegionFactory.setValueConstraint(getValueConstraint()); + resolvePoolName().ifPresent(clientRegionFactory::setPoolName); + clientRegionFactory.afterPropertiesSet(); return clientRegionFactory.getObject(); @@ -137,6 +137,7 @@ public class GemFireCacheTypeAwareRegionFactoryBean extends RegionLookupFa GenericRegionFactoryBean serverRegionFactory = new GenericRegionFactoryBean<>(); serverRegionFactory.setAttributes(getRegionAttributes()); + serverRegionFactory.setBeanFactory(getBeanFactory()); serverRegionFactory.setCache(gemfireCache); serverRegionFactory.setClose(isClose()); serverRegionFactory.setDataPolicy(getDataPolicy()); @@ -201,7 +202,15 @@ public class GemFireCacheTypeAwareRegionFactoryBean extends RegionLookupFa protected String getPoolName() { return Optional.ofNullable(this.poolName).filter(StringUtils::hasText) - .orElse(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME); + .orElse(ClientRegionFactoryBean.GEMFIRE_POOL_NAME); + } + + protected Optional resolvePoolName() { + return Optional.of(getPoolName()).filter(this::isPoolResolvable); + } + + private boolean isPoolResolvable(String poolName) { + return (getBeanFactory().containsBean(poolName) || (PoolManager.find(poolName) != null)); } /** diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java index 91753d0c..aa027d79 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java @@ -17,23 +17,29 @@ package org.springframework.data.gemfire.config.annotation.support; +import static java.util.stream.StreamSupport.stream; +import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; +import static org.springframework.data.gemfire.util.CollectionUtils.asSet; +import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable; + import java.util.Collections; import java.util.HashSet; import java.util.Iterator; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.type.filter.TypeFilter; -import org.springframework.data.gemfire.util.ArrayUtils; -import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * The {@link GemFireComponentClassTypeScanner} class is a classpath component scanner used to search @@ -44,6 +50,7 @@ import org.springframework.util.ClassUtils; * @see org.springframework.context.ConfigurableApplicationContext * @see org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider * @see org.springframework.core.env.Environment + * @see org.springframework.core.type.filter.TypeFilter * @since 1.9.0 */ @SuppressWarnings("unused") @@ -59,8 +66,7 @@ public class GemFireComponentClassTypeScanner implements Iterable { * @see #GemFireComponentClassTypeScanner(Set) */ public static GemFireComponentClassTypeScanner from(String... basePackages) { - return new GemFireComponentClassTypeScanner(CollectionUtils.asSet( - ArrayUtils.nullSafeArray(basePackages, String.class))); + return new GemFireComponentClassTypeScanner(asSet(nullSafeArray(basePackages, String.class))); } /** @@ -73,21 +79,16 @@ public class GemFireComponentClassTypeScanner implements Iterable { * @see #GemFireComponentClassTypeScanner(Set) */ public static GemFireComponentClassTypeScanner from(Iterable basePackages) { - Set basePackageSet = new HashSet(); - - for (String basePackage : CollectionUtils.nullSafeIterable(basePackages)) { - basePackageSet.add(basePackage); - } - - return new GemFireComponentClassTypeScanner(basePackageSet); + return new GemFireComponentClassTypeScanner(stream(basePackages.spliterator(), false) + .collect(Collectors.toSet())); } private ClassLoader entityClassLoader; private ConfigurableApplicationContext applicationContext; - private Set excludes = new HashSet(); - private Set includes = new HashSet(); + private Set excludes = new HashSet<>(); + private Set includes = new HashSet<>(); protected final Log log = LogFactory.getLog(getClass()); @@ -102,7 +103,7 @@ public class GemFireComponentClassTypeScanner implements Iterable { * @see java.util.Set */ protected GemFireComponentClassTypeScanner(Set basePackages) { - Assert.notEmpty(basePackages, "Base packages must be specified"); + Assert.notEmpty(basePackages, "Base packages is required"); this.basePackages = basePackages; } @@ -120,21 +121,24 @@ public class GemFireComponentClassTypeScanner implements Iterable { * Returns an unmodifiable {@link Set} of base packages to scan for GemFire components. * * @return an unmodifiable {@link Set} of base packages to scan for GemFire components. + * @see java.util.Set */ protected Set getBasePackages() { return Collections.unmodifiableSet(this.basePackages); } /** - * Returns a reference to the {@link ClassLoader} to find and load GemFire application persistent entity classes. + * Returns a reference to the {@link ClassLoader} used to find and load GemFire application + * persistent entity classes. * - * @return a {@link ClassLoader} to find and load GemFire application persistent entity classes. + * @return the {@link ClassLoader} used to find and load GemFire application persistent entity classes. * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader() * @see java.lang.Thread#getContextClassLoader() * @see java.lang.ClassLoader * @see #getApplicationContext() */ protected ClassLoader getEntityClassLoader() { + ConfigurableApplicationContext applicationContext = getApplicationContext(); return (this.entityClassLoader != null ? this.entityClassLoader @@ -152,8 +156,10 @@ public class GemFireComponentClassTypeScanner implements Iterable { * @see #getApplicationContext() */ protected Environment getEnvironment() { - ConfigurableApplicationContext applicationContext = getApplicationContext(); - return (applicationContext != null ? applicationContext.getEnvironment() : new StandardEnvironment()); + + return Optional.ofNullable(getApplicationContext()) + .map(ConfigurableApplicationContext::getEnvironment) + .orElse(new StandardEnvironment()); } /** @@ -162,6 +168,7 @@ public class GemFireComponentClassTypeScanner implements Iterable { * * @return a collection of {@link TypeFilter} objects * @see org.springframework.core.type.filter.TypeFilter + * @see java.lang.Iterable */ protected Iterable getExcludes() { return this.excludes; @@ -173,46 +180,49 @@ public class GemFireComponentClassTypeScanner implements Iterable { * * @return a collection of {@link TypeFilter} objects * @see org.springframework.core.type.filter.TypeFilter + * @see java.lang.Iterable */ protected Iterable getIncludes() { return this.includes; } - /** - * @inheritDoc - */ @Override public Iterator iterator() { return getBasePackages().iterator(); } /** - * Scans the {@link Set} of base packages searching for GemFire application components accepted by the filters - * of this scanner. + * Scans the {@link Set} of base packages searching for GemFire application components + * accepted by the filters of this scanner. * - * @return a {@link Set} of GemFire application component {@link Class} types. + * @return a {@link Set} of GemFire application component {@link Class} types found on the classpath. * @see #newClassPathScanningCandidateComponentProvider(boolean) * @see java.util.Set */ public Set> scan() { - Set> componentClasses = new HashSet>(); + + Set> componentClasses = new CopyOnWriteArraySet<>(); ClassLoader entityClassLoader = getEntityClassLoader(); ClassPathScanningCandidateComponentProvider componentProvider = newClassPathScanningCandidateComponentProvider(); - for (String packageName : this) { - for (BeanDefinition beanDefinition : componentProvider.findCandidateComponents(packageName)) { - try { - componentClasses.add(ClassUtils.forName(beanDefinition.getBeanClassName(), entityClassLoader)); - } - catch (ClassNotFoundException ignore) { - log.warn(String.format("Class not found for component type [%s]", - beanDefinition.getBeanClassName())); - } - } - } + stream(this.spliterator(), true) + .flatMap(packageName -> componentProvider.findCandidateComponents(packageName).stream()) + .forEach(beanDefinition -> { + Optional.ofNullable(beanDefinition.getBeanClassName()) + .filter(StringUtils::hasText) + .ifPresent(beanClassName -> { + try { + componentClasses.add(ClassUtils.forName(beanClassName, entityClassLoader)); + } + catch (ClassNotFoundException ignore) { + log.warn(String.format("Class for component type [%s] not found", + beanDefinition.getBeanClassName())); + } + }); + }); return componentClasses; } @@ -245,13 +255,8 @@ public class GemFireComponentClassTypeScanner implements Iterable { ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(useDefaultFilters, getEnvironment()); - for (TypeFilter exclude : excludes) { - componentProvider.addExcludeFilter(exclude); - } - - for (TypeFilter include : includes) { - componentProvider.addIncludeFilter(include); - } + this.excludes.forEach(componentProvider::addExcludeFilter); + this.includes.forEach(componentProvider::addIncludeFilter); return componentProvider; } @@ -270,29 +275,23 @@ public class GemFireComponentClassTypeScanner implements Iterable { /* (non-Javadoc) */ public GemFireComponentClassTypeScanner withExcludes(TypeFilter... excludes) { - return withExcludes(CollectionUtils.asSet(ArrayUtils.nullSafeArray(excludes, TypeFilter.class))); + return withExcludes(asSet(nullSafeArray(excludes, TypeFilter.class))); } /* (non-Javadoc) */ public GemFireComponentClassTypeScanner withExcludes(Iterable excludes) { - for (TypeFilter exclude : CollectionUtils.nullSafeIterable(excludes)) { - this.excludes.add(exclude); - } - + stream(nullSafeIterable(excludes).spliterator(), false).forEach(this.excludes::add); return this; } /* (non-Javadoc) */ public GemFireComponentClassTypeScanner withIncludes(TypeFilter... includes) { - return withIncludes(CollectionUtils.asSet(ArrayUtils.nullSafeArray(includes, TypeFilter.class))); + return withIncludes(asSet(nullSafeArray(includes, TypeFilter.class))); } /* (non-Javadoc) */ public GemFireComponentClassTypeScanner withIncludes(Iterable includes) { - for (TypeFilter include : CollectionUtils.nullSafeIterable(includes)) { - this.includes.add(include); - } - + stream(nullSafeIterable(includes).spliterator(), false).forEach(this.includes::add); return this; } } diff --git a/src/main/java/org/springframework/data/gemfire/mapping/annotation/ClientRegion.java b/src/main/java/org/springframework/data/gemfire/mapping/annotation/ClientRegion.java index 2bea0ba9..ea8f0fff 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/annotation/ClientRegion.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/annotation/ClientRegion.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.cache.client.Pool; import org.springframework.core.annotation.AliasFor; -import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; /** * {@link Annotation} defining the Client {@link org.apache.geode.cache.Region} in which the application @@ -83,7 +83,7 @@ public @interface ClientRegion { String diskStoreName() default ""; /** - * Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous. + * Determines whether disk-based operations (used in overflow and persistence) are synchronous or asynchronous. * * Defaults to {@literal synchronous}. */ @@ -102,9 +102,9 @@ public @interface ClientRegion { * data access operations sent to the corresponding {@link org.apache.geode.cache.Region} * on the GemFire/Geode Server. * - * Defaults to {@literal gemfirePool}. + * Defaults to {@literal DEFAULT}. */ - String poolName() default GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME; + String poolName() default ClientRegionFactoryBean.DEFAULT_POOL_NAME; /** * {@link ClientRegionShortcut} used by this persistent entity's client {@link org.apache.geode.cache.Region} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/annotation/LocalRegion.java b/src/main/java/org/springframework/data/gemfire/mapping/annotation/LocalRegion.java index 8b320abc..6fcd183e 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/annotation/LocalRegion.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/annotation/LocalRegion.java @@ -79,7 +79,7 @@ public @interface LocalRegion { String diskStoreName() default ""; /** - * Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous. + * Determines whether disk-based operations (used in overflow and persistence) are synchronous or asynchronous. * * Defaults to {@literal synchronous}. */ diff --git a/src/main/java/org/springframework/data/gemfire/mapping/annotation/PartitionRegion.java b/src/main/java/org/springframework/data/gemfire/mapping/annotation/PartitionRegion.java index f87099ee..b5647666 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/annotation/PartitionRegion.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/annotation/PartitionRegion.java @@ -91,7 +91,7 @@ public @interface PartitionRegion { String diskStoreName() default ""; /** - * Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous. + * Determines whether disk-based operations (used in overflow and persistence) are synchronous or asynchronous. * * Defaults to {@literal synchronous}. */ diff --git a/src/main/java/org/springframework/data/gemfire/mapping/annotation/ReplicateRegion.java b/src/main/java/org/springframework/data/gemfire/mapping/annotation/ReplicateRegion.java index d3144832..9ab4108e 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/annotation/ReplicateRegion.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/annotation/ReplicateRegion.java @@ -80,7 +80,7 @@ public @interface ReplicateRegion { String diskStoreName() default ""; /** - * Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous. + * Determines whether disk-based operations (used in overflow and persistence) are synchronous or asynchronous. * * Defaults to {@literal synchronous}. */ diff --git a/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java b/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java index d1055e30..d557670d 100644 --- a/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.function.Supplier; import org.springframework.beans.factory.BeanFactory; @@ -87,17 +88,27 @@ public abstract class SpringUtils { } /* (non-Javadoc) */ - public static T safeGetValue(Supplier supplier) { - return safeGetValue(supplier, null); + public static T safeGetValue(Supplier valueSupplier) { + return safeGetValue(valueSupplier, (T) null); } /* (non-Javadoc) */ - public static T safeGetValue(Supplier supplier, T defaultValue) { + public static T safeGetValue(Supplier valueSupplier, T defaultValue) { + return safeGetValue(valueSupplier, (Supplier) () -> defaultValue); + } + + /* (non-Javadoc) */ + public static T safeGetValue(Supplier valueSupplier, Supplier defaultValueSupplier) { + return safeGetValue(valueSupplier, (Function) exception -> defaultValueSupplier.get()); + } + + /* (non-Javadoc) */ + public static T safeGetValue(Supplier valueSupplier, Function exceptionHandler) { try { - return supplier.get(); + return valueSupplier.get(); } - catch (Throwable ignore) { - return defaultValue; + catch (Throwable cause) { + return exceptionHandler.apply(cause); } } } diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java new file mode 100644 index 00000000..2d65b63f --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java @@ -0,0 +1,168 @@ +/* + * Copyright 2010-2013 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.client; + +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.Test; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking; +import org.springframework.data.gemfire.test.support.IOUtils; + +/** + * Integration tests for {@link org.apache.geode.cache.client.ClientCache}. + * + * @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.apache.geode.cache.client.ClientCache + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking + */ +public class ClientCacheIntegrationTests { + + private ConfigurableApplicationContext newApplicationContext(Class... annotatedClasses) { + return new AnnotationConfigApplicationContext(annotatedClasses); + } + + private boolean testClientCacheClose(Class clientCacheConfiguration) { + + ConfigurableApplicationContext applicationContext = null; + + try { + applicationContext = newApplicationContext(clientCacheConfiguration); + + ClientCache clientCache = applicationContext.getBean(ClientCache.class); + + assertThat(clientCache.isClosed()).isFalse(); + + applicationContext.close(); + + return clientCache.isClosed(); + } + finally { + IOUtils.close(applicationContext); + } + } + + @Test + public void clientCacheIsClosed() { + assertThat(testClientCacheClose(ClosingClientCacheConfiguration.class)).isTrue(); + } + + @Test + public void clientCacheIsNotClosed() { + assertThat(testClientCacheClose(CloseSuppressingClientCacheConfiguration.class)).isFalse(); + } + + @Test + @SuppressWarnings("unchecked") + public void multipleClientCachesAreTheSame() { + + ConfigurableApplicationContext applicationContextOne = newApplicationContext(MultiClientCacheConfiguration.class); + ConfigurableApplicationContext applicationContextTwo = newApplicationContext(MultiClientCacheConfiguration.class); + + ClientCache clientCacheOne = applicationContextOne.getBean(ClientCache.class); + ClientCache clientCacheTwo = applicationContextTwo.getBean(ClientCache.class); + + assertThat(clientCacheOne).isNotNull(); + assertThat(clientCacheTwo).isSameAs(clientCacheOne); + + Region regionOne = applicationContextOne.getBean(Region.class); + Region regionTwo = applicationContextTwo.getBean(Region.class); + + assertThat(regionOne).isNotNull(); + assertThat(regionTwo).isSameAs(regionTwo); + assertThat(clientCacheOne.isClosed()).isFalse(); + assertThat(regionOne.isDestroyed()).isFalse(); + + applicationContextOne.close(); + + assertThat(clientCacheOne.isClosed()).describedAs("ClientCache was closed").isFalse(); + assertThat(regionOne.isDestroyed()).describedAs("Region was destroyed").isFalse(); + } + + @Configuration + @EnableGemFireMocking + static class ClosingClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(true); + + return clientCache; + } + } + + @Configuration + @EnableGemFireMocking + static class CloseSuppressingClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(false); + + return clientCache; + } + } + + @Configuration + @EnableGemFireMocking(useSingletonCache = true) + static class MultiClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(false); + + return clientCache; + } + + @Bean("Example") + ClientRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { + + ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean<>(); + + exampleRegion.setCache(gemfireCache); + exampleRegion.setClose(false); + exampleRegion.setDestroy(false); + exampleRegion.setLookupEnabled(true); + exampleRegion.setShortcut(ClientRegionShortcut.LOCAL); + + return exampleRegion; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java deleted file mode 100644 index 84d65d65..00000000 --- a/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2010-2013 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.client; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; - -import javax.annotation.Resource; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.Region; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author David Turanski - * @author John Blum - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "client-cache.xml", initializers = GemfireTestApplicationContextInitializer.class) -public class ClientCacheTest { - - @Resource(name = "ChallengeQuestions") - @SuppressWarnings("all") - private Region region; - - @Test - public void clientCacheIsNotClosed() { - ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( - "/org/springframework/data/gemfire/client/client-cache-no-close.xml"); - - Cache cache = context.getBean(Cache.class); - - context.close(); - - assertThat(cache.isClosed(), is(false)); - } - - @Test - public void poolNameEqualsDefault() { - assertThat(region.getAttributes().getPoolName(), is(nullValue())); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java index 50b72ffe..6c598f05 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java @@ -79,10 +79,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings({ "deprecation", "unchecked" }) public void testLookupFallbackUsingDefaultShortcut() throws Exception { + String testRegionName = "TestRegion"; ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.LOCAL))).thenReturn(mockClientRegionFactory); @@ -114,6 +117,7 @@ public class ClientRegionFactoryBeanTest { Pool mockPool = mock(Pool.class); Resource mockSnapshot = mock(Resource.class, "Snapshot"); + when(mockBeanFactory.containsBean(eq("TestPoolTwo"))).thenReturn(true); when(mockBeanFactory.isTypeMatch(eq("TestPoolTwo"), eq(Pool.class))).thenReturn(true); when(mockBeanFactory.getBean(eq("TestPoolTwo"))).thenReturn(mockPool); when(mockPool.getName()).thenReturn("TestPoolTwo"); @@ -154,8 +158,8 @@ public class ClientRegionFactoryBeanTest { verify(mockClientRegionFactory, times(1)).setRegionTimeToLive(any(ExpirationAttributes.class)); verify(mockClientRegionFactory, times(1)).setStatisticsEnabled(eq(true)); verify(mockClientRegionFactory, times(1)).setValueConstraint(eq(Number.class)); - verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPoolTwo")); verify(mockClientRegionFactory, times(1)).setDiskStoreName(eq("TestDiskStoreTwo")); + verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPoolTwo")); verify(mockClientRegionFactory, times(1)).create(eq(testRegionName)); verify(mockRegion, never()).loadSnapshot(any(InputStream.class)); } @@ -163,8 +167,11 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings({ "deprecation", "unchecked" }) public void testLookupFallbackUsingDefaultPersistentShortcut() throws Exception { + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.LOCAL_PERSISTENT))).thenReturn(mockClientRegionFactory); @@ -172,6 +179,7 @@ public class ClientRegionFactoryBeanTest { BeanFactory mockBeanFactory = mock(BeanFactory.class); + when(mockBeanFactory.containsBean(eq("TestPool"))).thenReturn(true); when(mockBeanFactory.isTypeMatch(eq("TestPool"), eq(Pool.class))).thenReturn(false); factoryBean.setAttributes(null); @@ -187,6 +195,7 @@ public class ClientRegionFactoryBeanTest { verify(mockClientCache, times(1)).createClientRegionFactory(eq(ClientRegionShortcut.LOCAL_PERSISTENT)); verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPool")); verify(mockClientRegionFactory, times(1)).create(eq("TestRegion")); + verify(mockBeanFactory, times(1)).containsBean(eq("TestPool")); verify(mockBeanFactory, never()).getBean(eq("TestPool")); verify(mockRegion, never()).loadSnapshot(any(InputStream.class)); } @@ -194,9 +203,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithSpecifiedShortcut() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockBeanFactory.containsBean(anyString())).thenReturn(true); @@ -220,9 +233,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithSubRegionCreation() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class, "RootRegion"); Region mockSubRegion = mock(Region.class, "SubRegion"); @@ -247,9 +264,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithUnspecifiedPool() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockBeanFactory.containsBean(anyString())).thenReturn(false); @@ -273,6 +294,7 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("deprecation") public void testSetDataPolicyName() throws Exception { + factoryBean.setDataPolicyName("NORMAL"); assertEquals(DataPolicy.NORMAL, TestUtils.readField("dataPolicy", factoryBean)); } @@ -280,6 +302,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) @SuppressWarnings("deprecation") public void testSetDataPolicyNameWithInvalidName() throws Exception { + try { factoryBean.setDataPolicyName("INVALID"); } @@ -294,6 +317,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsPersistent() { + assertFalse(factoryBean.isPersistent()); factoryBean.setPersistent(false); assertFalse(factoryBean.isPersistent()); @@ -303,6 +327,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsPersistentUnspecified() { + assertTrue(factoryBean.isPersistentUnspecified()); factoryBean.setPersistent(true); assertTrue(factoryBean.isPersistent()); @@ -314,6 +339,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsNotPersistent() { + assertFalse(factoryBean.isNotPersistent()); factoryBean.setPersistent(true); assertFalse(factoryBean.isNotPersistent()); @@ -323,7 +349,8 @@ public class ClientRegionFactoryBeanTest { @Test public void testCloseDestroySettings() { - final ClientRegionFactoryBean factory = new ClientRegionFactoryBean<>(); + + ClientRegionFactoryBean factory = new ClientRegionFactoryBean<>(); assertNotNull(factory); assertFalse(factory.isClose()); @@ -372,6 +399,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcut() throws Exception { + assertNull(TestUtils.readField("dataPolicy", factoryBean)); assertNull(TestUtils.readField("persistent", factoryBean)); assertNull(TestUtils.readField("shortcut", factoryBean)); @@ -380,6 +408,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutWhenNotPersistent() throws Exception { + factoryBean.setPersistent(false); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -390,6 +419,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutWhenPersistent() throws Exception { + factoryBean.setPersistent(true); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -400,6 +430,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingShortcut() throws Exception { + factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY_OVERFLOW); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -409,6 +440,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingShortcutWhenNotPersistent() throws Exception { + factoryBean.setPersistent(false); factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU); @@ -419,6 +451,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingShortcutWhenPersistent() throws Exception { + try { factoryBean.setPersistent(true); factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY); @@ -437,6 +470,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentShortcut() throws Exception { + factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -446,6 +480,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingPersistentShortcutWhenNotPersistent() throws Exception { + try { factoryBean.setPersistent(false); factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT); @@ -464,6 +499,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentShortcutWhenPersistent() throws Exception { + factoryBean.setPersistent(true); factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW); @@ -474,6 +510,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingEmptyDataPolicy() throws Exception { + factoryBean.setDataPolicy(DataPolicy.EMPTY); assertNull(TestUtils.readField("persistent", factoryBean)); @@ -483,6 +520,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingNormalDataPolicyWhenNotPersistent() throws Exception { + factoryBean.setDataPolicy(DataPolicy.NORMAL); factoryBean.setPersistent(false); @@ -493,6 +531,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingNormalDataPolicyWhenPersistent() throws Exception { + try { factoryBean.setDataPolicy(DataPolicy.NORMAL); factoryBean.setPersistent(true); @@ -510,6 +549,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicy() throws Exception { + factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); assertNull(TestUtils.readField("persistent", factoryBean)); @@ -519,6 +559,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicyWhenNotPersistent() throws Exception { + try { factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); factoryBean.setPersistent(false); @@ -536,6 +577,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicyWhenPersistent() throws Exception { + factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); factoryBean.setPersistent(true); @@ -551,7 +593,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyCallsRegionClose() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -585,7 +628,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyCallsRegionDestroy() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -620,7 +664,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyDoesNothingWhenClientRegionFactoryBeanCloseIsTrueButRegionServiceIsClosed() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -654,7 +699,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyDoesNothingWhenClientRegionFactoryBeanCloseAndDestroyAreFalse() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { @Override public Region getObject() throws Exception { @@ -673,6 +719,7 @@ public class ClientRegionFactoryBeanTest { @Test public void destroyDoesNothingWhenRegionIsNull() throws Exception { + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { @Override public Region getObject() throws Exception { return null; diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java new file mode 100644 index 00000000..734ce568 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java @@ -0,0 +1,72 @@ +/* + * 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.client; + +import static org.assertj.core.api.Assertions.assertThat; + +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.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests for client {@link Region Regions} created with SDG's {@link ClientRegionFactoryBean}. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.springframework.data.gemfire.client.ClientRegionFactoryBean + * @since 2.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class ClientRegionIntegrationTests { + + @Resource(name = "Example") + private Region example; + + @Test + public void clientRegionUsesDefaultPoolWhenUnspecified() { + assertThat(this.example.getAttributes().getPoolName()).isNull(); + } + + @ClientCacheApplication + @EnableGemFireMocking + static class ClientRegionConfiguration { + + @Bean("Example") + public ClientRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { + + ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean<>(); + + exampleRegion.setCache(gemfireCache); + exampleRegion.setClose(false); + exampleRegion.setShortcut(ClientRegionShortcut.LOCAL); + + return exampleRegion; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java index 1af5525f..ebd3813f 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java @@ -37,7 +37,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; /** * The ClientRegionWithCacheLoaderWriterTest class is a test suite of test cases testing the addition of CacheLoaders @@ -54,8 +54,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @see org.apache.geode.cache.Region * @since 1.3.3 */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "clientcache-with-region-using-cache-loader-writer.xml") +@RunWith(SpringRunner.class) +@ContextConfiguration @SuppressWarnings("unused") public class ClientRegionWithCacheLoaderWriterTest { @@ -69,6 +69,7 @@ public class ClientRegionWithCacheLoaderWriterTest { @Test public void testCacheLoaderWriter() { + assertNotNull(localAppData); assertEquals(0, localAppData.size()); @@ -107,7 +108,7 @@ public class ClientRegionWithCacheLoaderWriterTest { public static class LocalAppDataCacheWriter extends CacheWriterAdapter { - private static final Map data = new ConcurrentHashMap(REGION_SIZE); + private static final Map data = new ConcurrentHashMap<>(REGION_SIZE); @Override public void beforeUpdate(final EntryEvent event) throws CacheWriterException { diff --git a/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java index c654fd96..b3ceaffa 100644 --- a/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java @@ -42,7 +42,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.data.gemfire.RegionAttributesFactoryBean; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; /** * The LocalOnlyClientCacheIntegrationTest class... @@ -50,7 +50,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author John Blum * @since 1.0.0 */ -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(SpringRunner.class) @ContextConfiguration @SuppressWarnings("unused") public class LocalOnlyClientCacheIntegrationTest { @@ -71,26 +71,27 @@ public class LocalOnlyClientCacheIntegrationTest { @Test public void getAndPutsAreSuccessful() { - assertThat(example.put(1l, "one"), is(nullValue())); - assertThat(example.put(2l, "two"), is(nullValue())); - assertThat(example.put(3l, "three"), is(nullValue())); - assertThat(example.get(1l), is(equalTo("one"))); - assertThat(example.get(2l), is(equalTo("two"))); - assertThat(example.get(3l), is(equalTo("three"))); - assertThat(example.get(0l), is(nullValue())); - assertThat(example.get(4l), is(nullValue())); + assertThat(example.put(1L, "one"), is(nullValue())); + assertThat(example.put(2L, "two"), is(nullValue())); + assertThat(example.put(3L, "three"), is(nullValue())); + assertThat(example.get(1L), is(equalTo("one"))); + assertThat(example.get(2L), is(equalTo("two"))); + assertThat(example.get(3L), is(equalTo("three"))); + assertThat(example.get(0L), is(nullValue())); + assertThat(example.get(4L), is(nullValue())); } @Configuration static class GemFireClientCacheConfiguration { @Bean - PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { + static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Bean Properties gemfireProperties(@Value("${spring.data.gemfire.log.level:warning}") String logLevel) { + Properties gemfireProperties = new Properties(); gemfireProperties.setProperty("name", LocalOnlyClientCacheIntegrationTest.class.getSimpleName()); @@ -101,6 +102,7 @@ public class LocalOnlyClientCacheIntegrationTest { @Bean ClientCacheFactoryBean gemfireCache(@Qualifier("gemfireProperties") Properties gemfireProperties) { + ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); gemfireCache.setClose(true); @@ -128,6 +130,7 @@ public class LocalOnlyClientCacheIntegrationTest { @Bean @SuppressWarnings("unchecked") RegionAttributesFactoryBean exampleRegionAttributes() { + RegionAttributesFactoryBean exampleRegionAttributes = new RegionAttributesFactoryBean(); exampleRegionAttributes.setKeyConstraint(Long.class); diff --git a/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java b/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java deleted file mode 100644 index 2e0a3360..00000000 --- a/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2013 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.client; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.Region; -import org.junit.Test; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * @author David Turanski - * @author John Blum - */ -public class MultipleClientCacheTest { - - @Test - public void testMultipleCaches() { - String configLocation = "/org/springframework/data/gemfire/client/client-cache-no-close.xml"; - - ConfigurableApplicationContext context1 = new ClassPathXmlApplicationContext(configLocation); - ConfigurableApplicationContext context2 = new ClassPathXmlApplicationContext(configLocation); - - Cache cache1 = context1.getBean(Cache.class); - Cache cache2 = context2.getBean(Cache.class); - - assertNotNull(cache1); - assertSame(cache1, cache2); - - Region region1 = context1.getBean(Region.class); - Region region2 = context2.getBean(Region.class); - - assertNotNull(region1); - assertSame(region1, region2); - assertFalse(cache1.isClosed()); - assertFalse(region1.isDestroyed()); - - context1.close(); - - assertFalse(cache1.isClosed()); - assertFalse("region was destroyed", region1.isDestroyed()); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java index a7974814..b443223f 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java @@ -71,7 +71,6 @@ import org.springframework.data.gemfire.config.annotation.test.entities.LocalReg import org.springframework.data.gemfire.config.annotation.test.entities.NonEntity; import org.springframework.data.gemfire.config.annotation.test.entities.PartitionRegionEntity; import org.springframework.data.gemfire.config.annotation.test.entities.ReplicateRegionEntity; -import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.data.gemfire.mapping.annotation.ClientRegion; import org.springframework.data.gemfire.mapping.annotation.LocalRegion; import org.springframework.data.gemfire.mapping.annotation.PartitionRegion; @@ -192,20 +191,21 @@ public class EnableEntityDefinedRegionsUnitTests { @Test @SuppressWarnings("unchecked") public void entityClientRegionsDefined() { + applicationContext = newApplicationContext(ClientPersistentEntitiesConfiguration.class); Region sessions = applicationContext.getBean("Sessions", Region.class); assertRegion(sessions, "Sessions", String.class, ClientRegionEntity.class); assertRegionAttributes(sessions.getAttributes(), DataPolicy.NORMAL, null, true, - false, GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, null); + false, null, null); Region genericRegionEntity = applicationContext.getBean("GenericRegionEntity", Region.class); assertRegion(genericRegionEntity, "GenericRegionEntity", Long.class, GenericRegionEntity.class); assertRegionAttributes(genericRegionEntity.getAttributes(), DataPolicy.EMPTY, null, - true, false, GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, null); + true, false, null, null); assertThat(applicationContext.containsBean("CollocatedPartitionRegionEntity")).isFalse(); assertThat(applicationContext.containsBean("ContactEvents")).isFalse(); diff --git a/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java b/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java index 5727fb89..4b3ce6a9 100644 --- a/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java +++ b/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java @@ -236,6 +236,7 @@ public class ClientRegionNamespaceTest { @Test @SuppressWarnings("unchecked") public void testClientRegionWithAttributes() { + assertTrue(applicationContext.containsBean("client-with-attributes")); Region clientRegion = applicationContext.getBean("client-with-attributes", Region.class); diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java index 779fcd16..3d1eddf9 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java @@ -47,30 +47,40 @@ import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; +import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.CustomExpiry; +import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.cache.EvictionAttributes; +import org.apache.geode.cache.ExpirationAction; +import org.apache.geode.cache.ExpirationAttributes; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionService; import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.client.ClientRegionFactory; +import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.cache.client.Pool; import org.apache.geode.cache.client.PoolFactory; import org.apache.geode.cache.control.ResourceManager; import org.apache.geode.cache.server.CacheServer; import org.apache.geode.cache.server.ClientSubscriptionConfig; +import org.apache.geode.compression.Compressor; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.pdx.PdxSerializer; +import org.mockito.ArgumentMatchers; import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; import org.springframework.data.gemfire.test.support.FileSystemUtils; @@ -97,12 +107,25 @@ import org.springframework.data.gemfire.test.support.FileSystemUtils; @SuppressWarnings("unused") public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { + private static final boolean DEFAULT_USE_SINGLETON_CACHE = false; + + private static final AtomicReference singletonCache = new AtomicReference<>(null); + private static final Map diskStores = new ConcurrentHashMap<>(); private static final Map> regions = new ConcurrentHashMap<>(); private static final Map> regionAttributes = new ConcurrentHashMap<>(); + private static final String REPEATING_REGION_SEPARATOR = Region.SEPARATOR + "{2,}"; + + public static void destroy() { + singletonCache.set(null); + diskStores.clear(); + regions.clear(); + regionAttributes.clear(); + } + /* (non-Javadoc) */ private static boolean isRootRegion(Region region) { return isRootRegion(region.getFullPath()); @@ -113,9 +136,38 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return (regionPath.lastIndexOf(Region.SEPARATOR) <= 0); } + /* (non-Javadoc) */ + private static String normalizeRegionPath(String regionPath) { + + regionPath = regionPath.replaceAll(REPEATING_REGION_SEPARATOR, Region.SEPARATOR); + regionPath = regionPath.endsWith(Region.SEPARATOR) + ? regionPath.substring(0, regionPath.length() - 1) : regionPath; + + return regionPath; + } + + /* (non-Javadoc) */ + private static String toRegionName(String regionName) { + + return Optional.ofNullable(regionName) + .map(String::trim) + .map(it -> { + int lastIndexOfRegionSeparator = it.lastIndexOf(Region.SEPARATOR); + return lastIndexOfRegionSeparator < 0 ? it : it.substring(lastIndexOfRegionSeparator); + }) + .filter(it -> !it.isEmpty()) + .orElseThrow(() -> newIllegalArgumentException("Region name [%s] is required", regionName)); + } + + /* (non-Javadoc) */ private static String toRegionPath(String regionPath) { - return (StringUtils.startsWith(regionPath, Region.SEPARATOR) ? regionPath - : String.format("%1$s%2$s", Region.SEPARATOR, regionPath)); + + return Optional.ofNullable(regionPath) + .map(String::trim) + .map(it -> it.startsWith(Region.SEPARATOR) ? it : String.format("%1$s%2$s", Region.SEPARATOR, it)) + .map(MockGemFireObjectsSupport::normalizeRegionPath) + .filter(it -> !it.isEmpty()) + .orElseThrow(() -> newIllegalArgumentException("Region path [%s] is required", regionPath)); } /* (non-Javadoc) */ @@ -183,18 +235,8 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { when(mockRegionService.createPdxInstanceFactory(anyString())) .thenThrow(newUnsupportedOperationException(NOT_SUPPORTED)); - when(mockRegionService.rootRegions()).thenAnswer(invocation -> { - - Set> rootRegions = new HashSet<>(); - - for (Region region : regions.values()) { - if (isRootRegion(region)) { - rootRegions.add(region); - } - } - - return rootRegions; - }); + when(mockRegionService.rootRegions()).thenAnswer(invocation -> + regions.values().stream().filter(MockGemFireObjectsSupport::isRootRegion).collect(Collectors.toSet())); return mockRegionService; } @@ -203,7 +245,9 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { ClientCache mockClientCache = mock(ClientCache.class); - return mockCacheApi(mockClientCache); + doAnswer(newVoidAnswer(invocation -> mockClientCache.close())).when(mockClientCache).close(anyBoolean()); + + return mockClientRegionFactory(mockCacheApi(mockClientCache)); } public static GemFireCache mockGemFireCache() { @@ -320,6 +364,131 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return mockCacheServer; } + @SuppressWarnings("unchecked") + public static ClientCache mockClientRegionFactory(ClientCache mockClientCache) { + + ClientRegionFactory mockClientRegionFactory = + mock(ClientRegionFactory.class, mockObjectIdentifier("MockClientRegionFactory")); + + when(mockClientCache.createClientRegionFactory(any(ClientRegionShortcut.class))) + .thenReturn(mockClientRegionFactory); + + ExpirationAttributes DEFAULT_EXPIRATION_ATTRIBUTES = + new ExpirationAttributes(0, ExpirationAction.INVALIDATE); + + AtomicBoolean cloningEnabled = new AtomicBoolean(false); + AtomicBoolean concurrencyChecksEnabled = new AtomicBoolean(false); + AtomicBoolean diskSynchronous = new AtomicBoolean(true); + AtomicBoolean statisticsEnabled = new AtomicBoolean(false); + + AtomicInteger concurrencyLevel = new AtomicInteger(16); + AtomicInteger initialCapacity = new AtomicInteger(16); + + AtomicReference compressor = new AtomicReference<>(null); + AtomicReference> customEntryIdleTimeout = new AtomicReference<>(null); + AtomicReference> customEntryTimeToLive = new AtomicReference<>(null); + AtomicReference diskStoreName = new AtomicReference<>(null); + AtomicReference entryIdleTimeout = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference entryTimeToLive = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference evictionAttributes = + new AtomicReference<>(EvictionAttributes.createLRUEntryAttributes()); + AtomicReference> keyConstraint = new AtomicReference<>(); + AtomicReference loadFactor = new AtomicReference<>(0.75f); + AtomicReference poolName = new AtomicReference<>(null); + AtomicReference regionIdleTimeout = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference regionTimeToLive = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference> valueConstraint = new AtomicReference<>(); + + when(mockClientRegionFactory.setCloningEnabled(anyBoolean())) + .thenAnswer(newSetter(cloningEnabled, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCompressor(any(Compressor.class))) + .thenAnswer(newSetter(compressor, mockClientRegionFactory)); + + doAnswer(newSetter(concurrencyChecksEnabled, mockClientRegionFactory)) + .when(mockClientRegionFactory).setConcurrencyChecksEnabled(anyBoolean()); + + when(mockClientRegionFactory.setConcurrencyLevel(anyInt())) + .thenAnswer(newSetter(concurrencyLevel, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCustomEntryIdleTimeout(any(CustomExpiry.class))) + .thenAnswer(newSetter(customEntryIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCustomEntryTimeToLive(any(CustomExpiry.class))) + .thenAnswer(newSetter(customEntryTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setDiskStoreName(anyString())) + .thenAnswer(newSetter(diskStoreName, mockClientRegionFactory)); + + when(mockClientRegionFactory.setDiskSynchronous(anyBoolean())) + .thenAnswer(newSetter(diskSynchronous, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEntryIdleTimeout(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(entryIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEntryTimeToLive(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(entryTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEvictionAttributes(any(EvictionAttributes.class))) + .thenAnswer(newSetter(evictionAttributes, mockClientRegionFactory)); + + when(mockClientRegionFactory.setInitialCapacity(anyInt())) + .thenAnswer(newSetter(initialCapacity, mockClientRegionFactory)); + + when(mockClientRegionFactory.setKeyConstraint(any(Class.class))) + .thenAnswer(newSetter(keyConstraint, mockClientRegionFactory)); + + when(mockClientRegionFactory.setLoadFactor(anyFloat())) + .thenAnswer(newSetter(loadFactor, mockClientRegionFactory)); + + when(mockClientRegionFactory.setPoolName(anyString())) + .thenAnswer(newSetter(poolName, mockClientRegionFactory)); + + when(mockClientRegionFactory.setRegionIdleTimeout(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(regionIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setRegionTimeToLive(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(regionTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setStatisticsEnabled(anyBoolean())) + .thenAnswer(newSetter(statisticsEnabled, mockClientRegionFactory)); + + when(mockClientRegionFactory.setValueConstraint(any(Class.class))) + .thenAnswer(newSetter(valueConstraint, mockClientRegionFactory)); + + RegionAttributes mockRegionAttributes = + mock(RegionAttributes.class, mockObjectIdentifier("MockRegionAttributes")); + + when(mockRegionAttributes.getCloningEnabled()).thenAnswer(newGetter(cloningEnabled)); + when(mockRegionAttributes.getCompressor()).thenAnswer(newGetter(compressor)); + when(mockRegionAttributes.getConcurrencyChecksEnabled()).thenAnswer(newGetter(concurrencyChecksEnabled)); + when(mockRegionAttributes.getConcurrencyLevel()).thenAnswer(newGetter(concurrencyLevel)); + when(mockRegionAttributes.getCustomEntryIdleTimeout()).thenAnswer(newGetter(customEntryIdleTimeout)); + when(mockRegionAttributes.getCustomEntryTimeToLive()).thenAnswer(newGetter(customEntryTimeToLive)); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.NORMAL); + when(mockRegionAttributes.getDiskStoreName()).thenAnswer(newGetter(diskStoreName)); + when(mockRegionAttributes.isDiskSynchronous()).thenAnswer(newGetter(diskSynchronous)); + when(mockRegionAttributes.getEntryIdleTimeout()).thenAnswer(newGetter(entryIdleTimeout)); + when(mockRegionAttributes.getEntryTimeToLive()).thenAnswer(newGetter(entryTimeToLive)); + when(mockRegionAttributes.getEvictionAttributes()).thenAnswer(newGetter(evictionAttributes)); + when(mockRegionAttributes.getInitialCapacity()).thenAnswer(newGetter(initialCapacity)); + when(mockRegionAttributes.getKeyConstraint()).thenAnswer(newGetter(keyConstraint)); + when(mockRegionAttributes.getLoadFactor()).thenAnswer(newGetter(loadFactor)); + when(mockRegionAttributes.getPoolName()).thenAnswer(newGetter(poolName)); + when(mockRegionAttributes.getRegionIdleTimeout()).thenAnswer(newGetter(regionIdleTimeout)); + when(mockRegionAttributes.getRegionTimeToLive()).thenAnswer(newGetter(regionTimeToLive)); + when(mockRegionAttributes.getStatisticsEnabled()).thenAnswer(newGetter(statisticsEnabled)); + when(mockRegionAttributes.getValueConstraint()).thenAnswer(newGetter(valueConstraint)); + + when(mockClientRegionFactory.create(anyString())).thenAnswer(invocation -> + mockRegion(mockClientCache, invocation.getArgument(0), mockRegionAttributes)); + + when(mockClientRegionFactory.createSubregion(any(Region.class), anyString())).thenAnswer(invocation -> + mockSubRegion(invocation.getArgument(0), invocation.getArgument(1), mockRegionAttributes)); + + return mockClientCache; + } + public static ClientSubscriptionConfig mockClientSubscriptionConfig() { ClientSubscriptionConfig mockClientSubscriptionConfig = mock(ClientSubscriptionConfig.class); @@ -604,7 +773,66 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { }); return mockPoolFactory; + } + @SuppressWarnings("unchecked") + public static Region mockRegion(RegionService regionService, String name, + RegionAttributes regionAttributes) { + + Map data = new ConcurrentHashMap<>(); + + Region mockRegion = mock(Region.class, name); + + Set> subRegions = new CopyOnWriteArraySet<>(); + + when(mockRegion.getAttributes()).thenReturn(regionAttributes); + when(mockRegion.getFullPath()).thenReturn(toRegionPath(name)); + when(mockRegion.getName()).thenReturn(toRegionName(name)); + when(mockRegion.getRegionService()).thenReturn(regionService); + + when(mockRegion.getSubregion(anyString())).thenAnswer(invocation -> { + + String subRegionPath = toRegionPath(invocation.getArgument(0)); + String subRegionFullPath = String.format("%1$s%2$s", mockRegion.getFullPath(), subRegionPath); + + return regions.get(subRegionFullPath); + }); + + when(mockRegion.get(ArgumentMatchers.any())).thenAnswer(invocation -> + data.get(invocation.getArgument(0))); + + when(mockRegion.getEntry(ArgumentMatchers.any())).thenAnswer(invocation -> + data.entrySet().stream().filter(entry -> entry.getKey().equals(invocation.getArgument(0))).findFirst()); + + when(mockRegion.put(any(), any())).thenAnswer(invocation -> + data.put(invocation.getArgument(0), invocation.getArgument(1))); + + when(mockRegion.size()).thenAnswer(invocation -> data.size()); + + when(mockRegion.subregions(anyBoolean())).thenAnswer(invocation -> { + + boolean recursive = invocation.getArgument(0); + + return recursive ? subRegions.stream() + .flatMap(subRegion -> subRegion.subregions(true).stream()).collect(Collectors.toSet()) + : subRegions; + }); + + regions.put(mockRegion.getFullPath(), (Region) mockRegion); + + return mockRegion; + } + + public static Region mockSubRegion(Region parent, String name, + RegionAttributes regionAttributes) { + + String subRegionName = String.format("%1$s%2$s", parent.getFullPath(), toRegionPath(name)); + + Region mockSubRegion = mockRegion(parent.getRegionService(), subRegionName, regionAttributes); + + parent.subregions(false).add(mockSubRegion); + + return mockSubRegion; } public static ResourceManager mockResourceManager() { @@ -644,195 +872,235 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return mockResourceManager; } + private static T rememberMockedGemFireCache(T mockedGemFireCache, + boolean useSingletonCache) { + + return Optional.ofNullable(mockedGemFireCache) + .map(it -> { + if (useSingletonCache) { + singletonCache.compareAndSet(null, mockedGemFireCache); + } + + return mockedGemFireCache; + }) + .orElseThrow(() -> newIllegalArgumentException("GemFireCache is required")); + } + + @SuppressWarnings("unchecked") + private static Optional resolveMockedGemFireCache(boolean useSingletonCache) { + return Optional.ofNullable((T) singletonCache.get()).filter(it -> useSingletonCache); + } + public static CacheFactory spyOn(CacheFactory cacheFactory) { + return spyOn(cacheFactory, DEFAULT_USE_SINGLETON_CACHE); + } + + public static CacheFactory spyOn(CacheFactory cacheFactory, boolean useSingletonCache) { CacheFactory cacheFactorySpy = spy(cacheFactory); - Cache mockCache = mockPeerCache(); + Cache resolvedMockCache = MockGemFireObjectsSupport.resolveMockedGemFireCache(useSingletonCache) + .orElseGet(() -> { - AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); - AtomicBoolean pdxPersistent = new AtomicBoolean(false); - AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + Cache mockCache = mockPeerCache(); - AtomicReference pdxDiskStoreName = new AtomicReference<>(null); - AtomicReference pdxSerializer = new AtomicReference<>(null); + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); - doAnswer(newSetter(pdxDiskStoreName, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxDiskStore(anyString()); + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); - doAnswer(newSetter(pdxIgnoreUnreadFields, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); + doAnswer(newSetter(pdxDiskStoreName, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxDiskStore(anyString()); - doAnswer(newSetter(pdxPersistent, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxPersistent(anyBoolean()); + doAnswer(newSetter(pdxIgnoreUnreadFields, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); - doAnswer(newSetter(pdxReadSerialized, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxReadSerialized(anyBoolean()); + doAnswer(newSetter(pdxPersistent, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxPersistent(anyBoolean()); - doAnswer(newSetter(pdxSerializer, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); + doAnswer(newSetter(pdxReadSerialized, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxReadSerialized(anyBoolean()); - doReturn(mockCache).when(cacheFactorySpy).create(); + doAnswer(newSetter(pdxSerializer, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); - when(mockCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); - when(mockCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); - when(mockCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); - when(mockCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); - when(mockCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + when(mockCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); + when(mockCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); + when(mockCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); + when(mockCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); + when(mockCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + + return mockCache; + }); + + doReturn(rememberMockedGemFireCache(resolvedMockCache, useSingletonCache)).when(cacheFactorySpy).create(); return cacheFactorySpy; } public static ClientCacheFactory spyOn(ClientCacheFactory clientCacheFactory) { + return spyOn(clientCacheFactory, DEFAULT_USE_SINGLETON_CACHE); + } + + public static ClientCacheFactory spyOn(ClientCacheFactory clientCacheFactory, boolean useSingletonCache) { ClientCacheFactory clientCacheFactorySpy = spy(clientCacheFactory); - ClientCache mockClientCache = mockClientCache(); + ClientCache resolvedMockedClientCache = + MockGemFireObjectsSupport.resolveMockedGemFireCache(useSingletonCache).orElseGet(() -> { - AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); - AtomicBoolean pdxPersistent = new AtomicBoolean(false); - AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + ClientCache mockClientCache = mockClientCache(); - AtomicReference pdxDiskStoreName = new AtomicReference<>(null); - AtomicReference pdxSerializer = new AtomicReference<>(null); - AtomicReference defaultPool = new AtomicReference<>(null); + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); - doAnswer(newSetter(pdxDiskStoreName, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxDiskStore(anyString()); + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); + AtomicReference defaultPool = new AtomicReference<>(null); - doAnswer(newSetter(pdxIgnoreUnreadFields, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); + doAnswer(newSetter(pdxDiskStoreName, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxDiskStore(anyString()); - doAnswer(newSetter(pdxPersistent, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxPersistent(anyBoolean()); + doAnswer(newSetter(pdxIgnoreUnreadFields, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); - doAnswer(newSetter(pdxReadSerialized, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxReadSerialized(anyBoolean()); + doAnswer(newSetter(pdxPersistent, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxPersistent(anyBoolean()); - doAnswer(newSetter(pdxSerializer, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); + doAnswer(newSetter(pdxReadSerialized, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxReadSerialized(anyBoolean()); - PoolFactory mockPoolFactory = mockPoolFactory(); + doAnswer(newSetter(pdxSerializer, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); - doAnswer(invocation -> { - mockPoolFactory.addLocator(invocation.getArgument(0), invocation.getArgument(1)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).addPoolLocator(anyString(), anyInt()); + PoolFactory mockPoolFactory = mockPoolFactory(); - doAnswer(invocation -> { - mockPoolFactory.addServer(invocation.getArgument(0), invocation.getArgument(1)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).addPoolServer(anyString(), anyInt()); + doAnswer(invocation -> { + mockPoolFactory.addLocator(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).addPoolLocator(anyString(), anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setFreeConnectionTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolFreeConnectionTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.addServer(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).addPoolServer(anyString(), anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setIdleTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolIdleTimeout(anyLong()); + doAnswer(invocation -> { + mockPoolFactory.setFreeConnectionTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolFreeConnectionTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setLoadConditioningInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolLoadConditioningInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setIdleTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolIdleTimeout(anyLong()); - doAnswer(invocation -> { - mockPoolFactory.setMaxConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMaxConnections(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setLoadConditioningInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolLoadConditioningInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setMinConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMinConnections(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setMaxConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMaxConnections(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setMultiuserAuthentication(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMultiuserAuthentication(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setMinConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMinConnections(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setPingInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolPingInterval(anyLong()); + doAnswer(invocation -> { + mockPoolFactory.setMultiuserAuthentication(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMultiuserAuthentication(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setPRSingleHopEnabled(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolPRSingleHopEnabled(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setPingInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolPingInterval(anyLong()); - doAnswer(invocation -> { - mockPoolFactory.setReadTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolReadTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setPRSingleHopEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolPRSingleHopEnabled(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setRetryAttempts(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolRetryAttempts(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setReadTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolReadTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setServerGroup(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolServerGroup(anyString()); + doAnswer(invocation -> { + mockPoolFactory.setRetryAttempts(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolRetryAttempts(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSocketBufferSize(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSocketBufferSize(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setServerGroup(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolServerGroup(anyString()); - doAnswer(invocation -> { - mockPoolFactory.setStatisticInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolStatisticInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSocketBufferSize(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSocketBufferSize(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionAckInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionAckInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setStatisticInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolStatisticInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionEnabled(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionEnabled(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionAckInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionAckInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionMessageTrackingTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionMessageTrackingTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionEnabled(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionRedundancy(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionRedundancy(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionMessageTrackingTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionMessageTrackingTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setThreadLocalConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolThreadLocalConnections(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionRedundancy(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionRedundancy(anyInt()); - doReturn(mockClientCache).when(clientCacheFactorySpy).create(); + doAnswer(invocation -> { + mockPoolFactory.setThreadLocalConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolThreadLocalConnections(anyBoolean()); - when(mockClientCache.getCurrentServers()).thenAnswer(invocation -> - Collections.unmodifiableSet(new HashSet<>(defaultPool.get().getServers()))); + when(mockClientCache.getCurrentServers()).thenAnswer(invocation -> + Collections.unmodifiableSet(new HashSet<>(defaultPool.get().getServers()))); - when(mockClientCache.getDefaultPool()).thenAnswer(invocation -> { + when(mockClientCache.getDefaultPool()).thenAnswer(invocation -> { - if (defaultPool.get() == null) { - defaultPool.set(mockPoolFactory.create("DEFAULT")); - } + if (defaultPool.get() == null) { + defaultPool.set(mockPoolFactory.create("DEFAULT")); + } - return defaultPool.get(); - }); + return defaultPool.get(); + }); - when(mockClientCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); - when(mockClientCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); - when(mockClientCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); - when(mockClientCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); - when(mockClientCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + when(mockClientCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); + when(mockClientCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); + when(mockClientCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); + when(mockClientCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); + when(mockClientCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + + return mockClientCache; + }); + + doReturn(rememberMockedGemFireCache(resolvedMockedClientCache, useSingletonCache)) + .when(clientCacheFactorySpy).create(); return clientCacheFactorySpy; } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java index 1f57fc89..c21c1aa5 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java @@ -17,13 +17,18 @@ package org.springframework.data.gemfire.test.mock; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; +import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.springframework.util.StringUtils; /** * The {@link MockObjectsSupport} class is an abstract base class encapsulating common operations and utilities @@ -35,6 +40,19 @@ import org.mockito.stubbing.Answer; @SuppressWarnings("unused") public abstract class MockObjectsSupport { + private static final AtomicLong mockObjectIdentifier = new AtomicLong(0L); + + private static final String DEFAULT_MOCK_OBJECT_NAME = "MockObject"; + + protected static String mockObjectIdentifier() { + return mockObjectIdentifier(DEFAULT_MOCK_OBJECT_NAME); + } + + protected static String mockObjectIdentifier(String mockObjectName) { + return String.format("%s%d", Optional.ofNullable(mockObjectName).filter(StringUtils::hasText) + .orElse(DEFAULT_MOCK_OBJECT_NAME), mockObjectIdentifier.incrementAndGet()); + } + /* (non-Javadoc) */ protected static Answer newGetter(AtomicBoolean returnValue) { return invocation -> returnValue.get(); @@ -60,6 +78,16 @@ public abstract class MockObjectsSupport { return invocation -> converter.apply(returnValue.get()); } + /* (non-Javadoc) */ + protected static Answer newGetter(Supplier returnValue) { + return invocation -> returnValue.get(); + } + + /* (non-Javadoc) */ + protected static Answer newGetter(Supplier returnValue, Function converter) { + return invocation -> converter.apply(returnValue.get()); + } + /* (non-Javadoc) */ protected static Answer newSetter(AtomicBoolean argument, R returnValue) { return invocation -> { @@ -139,4 +167,12 @@ public abstract class MockObjectsSupport { return returnValue; }; } + + /* (non-Javadoc) */ + protected static Answer newVoidAnswer(Consumer methodInvocation) { + return invocation -> { + methodInvocation.accept(invocation); + return null; + }; + } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java index bdf9499f..80f70bb8 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java @@ -23,6 +23,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.geode.cache.GemFireCache; import org.springframework.context.annotation.Import; /** @@ -33,6 +34,7 @@ import org.springframework.context.annotation.Import; * @see java.lang.annotation.Inherited * @see java.lang.annotation.Retention * @see java.lang.annotation.Target + * @see org.apache.geode.cache.GemFireCache * @see org.springframework.context.annotation.Import * @since 2.0.0 */ @@ -44,4 +46,13 @@ import org.springframework.context.annotation.Import; @SuppressWarnings("unused") public @interface EnableGemFireMocking { + /** + * Determines whether the mock {@link GemFireCache} created for Unit Tests is a Singleton. + * + * Defaults to {@literal false}. + * + * @return a boolean value indicating whether the mock {@link GemFireCache} created for Unit Tests is a Singleton. + */ + boolean useSingletonCache() default false; + } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java index 34f42dfc..78d4e798 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java @@ -16,9 +16,17 @@ package org.springframework.data.gemfire.test.mock.annotation; +import java.lang.annotation.Annotation; + import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportAware; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.test.mock.MockGemFireObjectsSupport; import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor; /** @@ -26,16 +34,64 @@ import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanP * containing bean definitions to configure GemFire Object mocking. * * @author John Blum + * @see java.lang.annotation.Annotation + * @see org.springframework.beans.factory.config.BeanPostProcessor * @see org.springframework.context.annotation.Bean * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.ImportAware + * @see org.springframework.core.annotation.AnnotationAttributes + * @see org.springframework.core.type.AnnotationMetadata + * @see org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor * @since 2.0.0 */ @SuppressWarnings("unused") @Configuration -public class GemFireMockingConfiguration { +public class GemFireMockingConfiguration implements ImportAware { + + private boolean useSingletonCache = false; + + @Override + public void setImportMetadata(AnnotationMetadata importingClassMetadata) { + + if (isAnnotationPresent(importingClassMetadata)) { + + AnnotationAttributes enableGemFireMockingAttributes = getAnnotationAttributes(importingClassMetadata); + + this.useSingletonCache = enableGemFireMockingAttributes.getBoolean("useSingletonCache"); + } + } + + private Class getAnnotationType() { + return EnableGemFireMocking.class; + } + + private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) { + return isAnnotationPresent(importingClassMetadata, getAnnotationType()); + } + + private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, + Class annotationType) { + + return importingClassMetadata.hasAnnotation(annotationType.getName()); + } + + private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) { + return getAnnotationAttributes(importingClassMetadata, getAnnotationType()); + } + + private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata, + Class annotationType) { + + return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationType.getName())); + } @Bean public BeanPostProcessor mockGemFireObjectsBeanPostProcessor() { - return MockGemFireObjectsBeanPostProcessor.INSTANCE; + return MockGemFireObjectsBeanPostProcessor.newInstance(this.useSingletonCache); + } + + @EventListener + public void releaseMockResources(ContextClosedEvent event) { + MockGemFireObjectsSupport.destroy(); } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java index 2af9ee72..4e166cf8 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java @@ -51,17 +51,32 @@ import org.springframework.lang.Nullable; */ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { - public static final MockGemFireObjectsBeanPostProcessor INSTANCE = new MockGemFireObjectsBeanPostProcessor(); + private static final boolean DEFAULT_USE_SINGLETON_CACHE = false; private static final String GEMFIRE_PROPERTIES_BEAN_NAME = "gemfireProperties"; + private volatile boolean useSingletonCache; + private final AtomicReference gemfireProperties = new AtomicReference<>(new Properties()); + public static MockGemFireObjectsBeanPostProcessor newInstance() { + return newInstance(DEFAULT_USE_SINGLETON_CACHE); + } + + public static MockGemFireObjectsBeanPostProcessor newInstance(boolean useSingletonCache) { + + MockGemFireObjectsBeanPostProcessor beanPostProcessor = new MockGemFireObjectsBeanPostProcessor(); + + beanPostProcessor.useSingletonCache = useSingletonCache; + + return beanPostProcessor; + } + @Nullable @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return (isGemFireProperties(bean, beanName) ? set((Properties) bean) - : (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean) + : (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean, this.useSingletonCache) : (bean instanceof PoolFactoryBean ? mockThePoolFactoryBean((PoolFactoryBean) bean) : bean))); } @@ -88,11 +103,11 @@ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { return gemfireProperties; } - private Object spyOnCacheFactoryBean(CacheFactoryBean bean) { + private Object spyOnCacheFactoryBean(CacheFactoryBean bean, boolean useSingletonCache) { return (bean instanceof ClientCacheFactoryBean - ? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean) - : SpyingCacheFactoryInitializer.spyOn(bean)); + ? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean, useSingletonCache) + : SpyingCacheFactoryInitializer.spyOn(bean, useSingletonCache)); } private Object mockThePoolFactoryBean(PoolFactoryBean bean) { @@ -102,28 +117,44 @@ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { protected static class SpyingCacheFactoryInitializer implements CacheFactoryBean.CacheFactoryInitializer { - public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean) { - cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer()); + public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean, boolean useSingletonCache) { + cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer(useSingletonCache)); return cacheFactoryBean; } + private final boolean useSingletonCache; + + protected SpyingCacheFactoryInitializer(boolean useSingletonCache) { + this.useSingletonCache = useSingletonCache; + } + @Override public CacheFactory initialize(CacheFactory cacheFactory) { - return MockGemFireObjectsSupport.spyOn(cacheFactory); + return MockGemFireObjectsSupport.spyOn(cacheFactory, useSingletonCache); } } protected static class SpyingClientCacheFactoryInitializer implements CacheFactoryBean.CacheFactoryInitializer { - public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean) { - clientCacheFactoryBean.setCacheFactoryInitializer(new SpyingClientCacheFactoryInitializer()); + public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean, + boolean useSingletonCache) { + + clientCacheFactoryBean.setCacheFactoryInitializer( + new SpyingClientCacheFactoryInitializer(useSingletonCache)); + return clientCacheFactoryBean; } + private final boolean useSingletonCache; + + protected SpyingClientCacheFactoryInitializer(boolean useSingletonCache) { + this.useSingletonCache = useSingletonCache; + } + @Override public ClientCacheFactory initialize(ClientCacheFactory clientCacheFactory) { - return MockGemFireObjectsSupport.spyOn(clientCacheFactory); + return MockGemFireObjectsSupport.spyOn(clientCacheFactory, this.useSingletonCache); } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java index 4cefce2f..cfae6a5e 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java @@ -36,6 +36,6 @@ public class MockGemFireObjectsApplicationContextInitializer @Override @SuppressWarnings("all") public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.INSTANCE); + applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.newInstance()); } } diff --git a/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java b/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java index e2e0f56d..c8e509db 100644 --- a/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java @@ -24,7 +24,10 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.data.gemfire.util.ArrayUtils.asArray; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException; +import java.util.function.Function; import java.util.function.Supplier; import org.junit.Test; @@ -206,18 +209,72 @@ public class SpringUtilsUnitTests { } @Test - public void safeGetValueReturnsSupplierValue() { - assertThat(SpringUtils.safeGetValue(() -> "test", null)).isEqualTo("test"); - } - - @Test - public void safeGetValueReturnsDefaultValue() { - assertThat(SpringUtils.safeGetValue(() -> { throw new RuntimeException("error"); }, "test")) - .isEqualTo("test"); + public void safeGetValueReturnsSuppliedValue() { + assertThat(SpringUtils.safeGetValue(() -> "test")).isEqualTo("test"); } @Test public void safeGetValueReturnsNull() { - assertThat(SpringUtils.safeGetValue(() -> { throw new RuntimeException("error"); })).isNull(); + assertThat(SpringUtils.safeGetValue(() -> { throw newRuntimeException("error"); })).isNull(); + } + + @Test + public void safeGetValueReturnsDefaultValue() { + assertThat(SpringUtils.safeGetValue(() -> { throw newRuntimeException("error"); }, "test")) + .isEqualTo("test"); + } + + @Test + public void safeGetValueReturnsSuppliedDefaultValue() { + + Supplier exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + Supplier defaultValueSupplier = () -> "test"; + + assertThat(SpringUtils.safeGetValue(exceptionThrowingSupplier, defaultValueSupplier)).isEqualTo("test"); + } + + @Test + public void safeGetValueHandlesExceptionReturnsValue() { + + Supplier exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + + Function exceptionHandler = exception -> { + + assertThat(exception).isInstanceOf(RuntimeException.class); + assertThat(exception).hasMessage("error"); + assertThat(exception).hasNoCause(); + + return "test"; + }; + + assertThat(SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler)).isEqualTo("test"); + } + + @Test(expected = IllegalStateException.class) + public void safeGetValueHandlesExceptionAndCanThrowException() { + + Supplier exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + + Function exceptionHandler = exception -> { + + assertThat(exception).isInstanceOf(RuntimeException.class); + assertThat(exception).hasMessage("error"); + assertThat(exception).hasNoCause(); + + throw newIllegalStateException(exception, "test"); + }; + + try { + SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("test"); + assertThat(expected).hasCauseInstanceOf(RuntimeException.class); + assertThat(expected.getCause()).hasMessage("error"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; + } } } diff --git a/src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml b/src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml similarity index 97% rename from src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml rename to src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml index f16e20b8..b6833bae 100644 --- a/src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml +++ b/src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml @@ -11,7 +11,6 @@ ClientCacheWithRegionUsingCacheLoaderWriterTest - 0 warning diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml deleted file mode 100644 index 99b44d59..00000000 --- a/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - warning - - - - - - - diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml deleted file mode 100644 index 302ff952..00000000 --- a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - warning - - - - - - - diff --git a/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml b/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml index db7d1c4d..520ac210 100644 --- a/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml +++ b/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml @@ -95,6 +95,7 @@ result-policy="${client.regex.interests.result-policy}"/> +