diff --git a/pom.xml b/pom.xml index 6a9a9208..35914404 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ 2.7.7 1.3.2 3.6.2 + 1.0.0 9.1.1 2.0.2 1.01 @@ -47,6 +48,14 @@ + + + javax.cache + cache-api + ${cache-api.version} + true + + @@ -55,7 +64,7 @@ 1.0.1 test - + javax.interceptor javax.interceptor-api diff --git a/src/main/java/org/springframework/data/gemfire/cache/config/GemfireCachingConfiguration.java b/src/main/java/org/springframework/data/gemfire/cache/config/GemfireCachingConfiguration.java index b9467b81..dcf97f0a 100644 --- a/src/main/java/org/springframework/data/gemfire/cache/config/GemfireCachingConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/cache/config/GemfireCachingConfiguration.java @@ -26,8 +26,13 @@ import org.springframework.data.gemfire.cache.GemfireCacheManager; * The {@link GemfireCachingConfiguration} class is a Spring {@link Configuration @Configuration} class * used to configure Pivotal GemFire or Apache Geode as the caching provider in Spring's Cache Abstraction. * - * Additionally, this Spring {@link Configuration} class also enables the Spring Cache Abstraction - * with {@link EnableCaching}. + * This {@link Configuration @Configuration} class is specifically responsible for declaring and registering + * Spring Data GemFire/Geode's {@link GemfireCacheManager} implementation to properly enable either Pivotal GemFire + * or Apache Geode as the caching provider used with Springs Cache Abstraction. + * + * Additionally, this Spring {@link Configuration @Configuration} class also enables the Spring Cache Abstraction + * by declaring Spring's {@link EnableCaching} annotation for the user extending or importing this class using + * the SDG provided {@link EnableGemfireCaching} annotation. * * @author John Blum * @see org.apache.geode.cache.GemFireCache diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java index 6062bc3d..f5c3b5e6 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CachingDefinedRegionsConfiguration.java @@ -16,14 +16,17 @@ package org.springframework.data.gemfire.config.annotation; +import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static org.springframework.data.gemfire.util.ArrayUtils.asArray; import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; +import static org.springframework.data.gemfire.util.StreamUtils.concat; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -32,6 +35,11 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.cache.annotation.CacheDefaults; +import javax.cache.annotation.CacheRemove; +import javax.cache.annotation.CacheRemoveAll; +import javax.cache.annotation.CacheResult; + import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionShortcut; @@ -45,7 +53,6 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; @@ -105,21 +112,10 @@ import org.springframework.util.StringUtils; @Configuration public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfigSupport implements ImportAware { - private static final Class[] CLASS_CACHE_ANNOTATION_TYPES; - - private static final Class[] METHOD_CACHE_ANNOTATION_TYPES = - asArray(Cacheable.class, CacheEvict.class, CachePut.class); - - static { - - List annotationTypes = new ArrayList<>(); - - Collections.addAll(annotationTypes, METHOD_CACHE_ANNOTATION_TYPES); - annotationTypes.add(CacheConfig.class); - - CLASS_CACHE_ANNOTATION_TYPES = annotationTypes.toArray(new Class[annotationTypes.size()]); - - } + private final CacheNameResolver composableCacheNameResolver = type -> + asList(new Jsr107CacheAnnotationsCacheNameResolver(), new SpringCacheAnnotationsCacheNameResolver()).stream() + .flatMap(cacheNameResolver -> cacheNameResolver.resolveCacheNames(type).stream()) + .collect(Collectors.toSet()); private ClientRegionShortcut clientRegionShortcut = ClientRegionShortcut.PROXY; @@ -142,6 +138,17 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig return EnableCachingDefinedRegions.class; } + /** + * Returns the configured {@link CacheNameResolver} to resolve all the declared cache name on Spring application + * beans/components declared and registered in the Spring container (context). + * + * @return the configured {@link CacheNameResolver} to resolve all teh caches used by the Spring application. + * @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.CacheNameResolver + */ + protected CacheNameResolver getCacheNameResolver() { + return this.composableCacheNameResolver; + } + /** * Configures the {@link ClientRegionShortcut} specifying the data management policy to use * when creating a client {@link Region}. @@ -273,7 +280,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig @Bean @SuppressWarnings("all") - public BeanDefinitionRegistryPostProcessor cacheAbstractionAnnotationsRegionBeanDefinitionRegistrar() { + public BeanDefinitionRegistryPostProcessor cachingAnnotationsRegionBeanDefinitionRegistrar() { return new BeanDefinitionRegistryPostProcessorSupport() { @@ -284,24 +291,6 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig }; } - @Bean - @SuppressWarnings("all") - public BeanPostProcessor cacheAbstractionAnnotationsRegionBeanRegistrar(ConfigurableBeanFactory beanFactory) { - - return new BeanPostProcessor() { - - @Nullable @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - - if (isNotInfrastructureBean(bean)) { - registerRegionBeans(collectCacheNames(bean.getClass()), beanFactory); - } - - return bean; - } - }; - } - void registerBeanDefinitions(BeanDefinitionRegistry registry) { for (String beanName : registry.getBeanDefinitionNames()) { @@ -310,73 +299,11 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig if (isNotInfrastructureBean(beanDefinition)) { resolveBeanClass(beanDefinition, registry).ifPresent(beanClass -> - registerRegionBeanDefinitions(collectCacheNames(beanClass), registry)); + registerRegionBeanDefinitions(getCacheNameResolver().resolveCacheNames(beanClass), registry)); } } } - @SuppressWarnings("unchecked") - private Set collectCacheNames(Class type) { - - Set cacheNames = new HashSet<>(); - - cacheNames.addAll(collectCachingCacheNames(type)); - cacheNames.addAll(collectCacheNames(type, CLASS_CACHE_ANNOTATION_TYPES)); - - stream(type.getMethods()).forEach(method -> { - if (isUserLevelMethod(method)) { - cacheNames.addAll(collectCachingCacheNames(method)); - cacheNames.addAll(collectCacheNames(method, METHOD_CACHE_ANNOTATION_TYPES)); - } - }); - - return cacheNames; - } - - @SuppressWarnings("all") - Set collectCacheNames(AnnotatedElement annotatedElement, - Class... annotationTypes) { - - Stream cacheNames = stream(nullSafeArray(annotationTypes, Class.class)) - .map(annotationType -> resolveAnnotation(annotatedElement, annotationType)) - .flatMap(annotation -> collectCacheNames((Annotation) annotation).stream()); - - return cacheNames.collect(Collectors.toSet()); - } - - private Set collectCacheNames(Annotation annotation) { - - return Optional.ofNullable(annotation) - .map(this::getAnnotationAttributes) - .map(annotationAttributes -> annotationAttributes.getStringArray("cacheNames")) - .map(CollectionUtils::asSet) - .orElse(Collections.emptySet()); - } - - private Set collectCachingCacheNames(AnnotatedElement annotatedElement) { - - Set cacheNames = new HashSet<>(); - - Optional.ofNullable(resolveAnnotation(annotatedElement, Caching.class)) - .ifPresent(caching -> { - - cacheNames.addAll(stream(nullSafeArray(caching.cacheable(), Cacheable.class)) - .flatMap(cacheable -> collectCacheNames(cacheable).stream()) - .collect(Collectors.toSet())); - - cacheNames.addAll(stream(nullSafeArray(caching.evict(), CacheEvict.class)) - .flatMap(cacheEvict -> collectCacheNames(cacheEvict).stream()) - .collect(Collectors.toSet())); - - cacheNames.addAll(stream(nullSafeArray(caching.put(), CachePut.class)) - .flatMap(cachePut -> collectCacheNames(cachePut).stream()) - .collect(Collectors.toSet())); - - }); - - return cacheNames; - } - private BeanDefinitionRegistry registerRegionBeanDefinitions(Set cacheNames, BeanDefinitionRegistry registry) { @@ -400,6 +327,24 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig return registry; } + @Bean + @SuppressWarnings("all") + public BeanPostProcessor cachingAnnotationsRegionBeanRegistrar(ConfigurableBeanFactory beanFactory) { + + return new BeanPostProcessor() { + + @Nullable @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + + if (isNotInfrastructureBean(bean)) { + registerRegionBeans(getCacheNameResolver().resolveCacheNames(bean.getClass()), beanFactory); + } + + return bean; + } + }; + } + private ConfigurableBeanFactory registerRegionBeans(Set cacheNames, ConfigurableBeanFactory beanFactory) { cacheNames.forEach(cacheName -> { @@ -432,4 +377,148 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig return beanFactory; } + + /** + * {@link CacheNameResolver} is a {@link FunctionalInterface} declaring a contract for all implementations + * used to resolve all cache names declared and used by a Spring application. A resolver typically inspects + * all the application beans/components declared and registered in the Spring container (context) setup by + * the application to determine whether the application components require caching behavior. + * + * @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.Jsr107CacheAnnotationsCacheNameResolver + * @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.SpringCacheAnnotationsCacheNameResolver + */ + @FunctionalInterface + protected interface CacheNameResolver { + Set resolveCacheNames(Class type); + } + + /** + * {@link AbstractCacheNameResolver} is an abstract base class encapsulating reusable functionality common + * to all {@link CacheNameResolver} implementations. + * + * Current implementations support inlude JSR-107, JCache API annotation and Spring's Cache Abstraction annotations. + * + * @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.CacheNameResolver + */ + protected abstract class AbstractCacheNameResolver implements CacheNameResolver { + + private final String JSR_107_CACHE_NAME_ATTRIBUTE_NAME = "cacheName"; + private final String SPRING_CACHE_NAMES_ATTRIBUTE_NAME = "cacheNames"; + + private final String[] EMPTY_ARRAY = new String[0]; + + protected abstract Class[] getClassCacheAnnotationTypes(); + + protected abstract Class[] getMethodCacheAnnotationTypes(); + + @SuppressWarnings("unchecked") + protected Class[] append(Class[] annotationTypes, Class... additionalAnnotationTypes) { + + List annotationTypeList = new ArrayList<>(Arrays.asList(annotationTypes)); + + Collections.addAll(annotationTypeList, additionalAnnotationTypes); + + return annotationTypeList.toArray(new Class[annotationTypeList.size()]); + } + + protected Set resolveCacheNames(Annotation annotation) { + + return Optional.ofNullable(annotation) + .map(it -> getAnnotationAttributes(it)) + .map(annotationAttributes -> { + + String attributeName = annotationAttributes.containsKey(SPRING_CACHE_NAMES_ATTRIBUTE_NAME) + ? SPRING_CACHE_NAMES_ATTRIBUTE_NAME : JSR_107_CACHE_NAME_ATTRIBUTE_NAME; + + return annotationAttributes.containsKey(attributeName) + ? annotationAttributes.getStringArray(attributeName) + : EMPTY_ARRAY; + + }) + .map(CollectionUtils::asSet) + .orElse(Collections.emptySet()); + } + + @Override + @SuppressWarnings("unchecked") + public Set resolveCacheNames(Class type) { + + Set cacheNames = new HashSet<>(); + + cacheNames.addAll(resolveCacheNames(type, getClassCacheAnnotationTypes())); + + stream(type.getMethods()) + .filter(method -> isUserLevelMethod(method)) + .forEach(method -> cacheNames.addAll(resolveCacheNames(method, getMethodCacheAnnotationTypes()))); + + return cacheNames; + } + + @SuppressWarnings("all") + protected Set resolveCacheNames(AnnotatedElement annotatedElement, + Class... annotationTypes) { + + Stream cacheNames = stream(nullSafeArray(annotationTypes, Class.class)) + .map(annotationType -> resolveAnnotation(annotatedElement, annotationType)) + .flatMap(annotation -> resolveCacheNames((Annotation) annotation).stream()); + + return cacheNames.collect(Collectors.toSet()); + } + } + + protected class Jsr107CacheAnnotationsCacheNameResolver extends AbstractCacheNameResolver { + + @Override + @SuppressWarnings("unchecked") + protected Class[] getClassCacheAnnotationTypes() { + return append(getMethodCacheAnnotationTypes(), CacheDefaults.class); + } + + @Override + protected Class[] getMethodCacheAnnotationTypes() { + return asArray(javax.cache.annotation.CachePut.class, CacheRemove.class, + CacheRemoveAll.class, CacheResult.class); + } + } + + protected class SpringCacheAnnotationsCacheNameResolver extends AbstractCacheNameResolver { + + @Override + @SuppressWarnings("unchecked") + protected Class[] getClassCacheAnnotationTypes() { + return append(getMethodCacheAnnotationTypes(), Caching.class); + } + + @Override + protected Class[] getMethodCacheAnnotationTypes() { + return asArray(Cacheable.class, CacheEvict.class, CachePut.class); + } + + @Override + @SuppressWarnings("unchecked") + public Set resolveCacheNames(Class type) { + + Set cacheNames = super.resolveCacheNames(type); + + cacheNames.addAll(resolveCachingCacheNames(type)); + + stream(type.getMethods()).filter(method -> isUserLevelMethod(method)) + .forEach(method -> cacheNames.addAll(resolveCachingCacheNames(method))); + + return cacheNames; + } + + @SuppressWarnings("unchecked") + private Set resolveCachingCacheNames(AnnotatedElement annotatedElement) { + + Set cacheNames = new HashSet<>(); + + Optional.ofNullable(resolveAnnotation(annotatedElement, Caching.class)).ifPresent(caching -> + concat(stream(caching.cacheable()), stream(caching.evict()), stream(caching.put())) + .flatMap(cacheAnnotation -> resolveCacheNames(cacheAnnotation).stream()) + .collect(Collectors.toCollection(() -> cacheNames))); + + return cacheNames; + } + } } diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableLocator.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableLocator.java index 5f19b344..acb6eecc 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableLocator.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableLocator.java @@ -68,7 +68,7 @@ public @interface EnableLocator { * * Defaults to {@literal 10334}. * - * Use the {@literal spring.data.gemfire.locator.host} property in {@literal application.properties}. + * Use the {@literal spring.data.gemfire.locator.port} property in {@literal application.properties}. */ int port() default LocatorConfiguration.DEFAULT_LOCATOR_PORT; diff --git a/src/main/java/org/springframework/data/gemfire/util/ArrayUtils.java b/src/main/java/org/springframework/data/gemfire/util/ArrayUtils.java index f071d107..6ffce0dd 100644 --- a/src/main/java/org/springframework/data/gemfire/util/ArrayUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/ArrayUtils.java @@ -49,7 +49,7 @@ public abstract class ArrayUtils { * @return the given {@code array} if not {@literal null} or empty otherwise return the {@code defaultArray}. */ public static T[] defaultIfEmpty(T[] array, T[] defaultArray) { - return (!ObjectUtils.isEmpty(array) ? array : defaultArray); + return !ObjectUtils.isEmpty(array) ? array : defaultArray; } /** @@ -76,7 +76,7 @@ public abstract class ArrayUtils { * @see #getFirst(Object[], Object) */ public static T getFirst(T[] array, T defaultValue) { - return (isEmpty(array) ? defaultValue : array[0]); + return isEmpty(array) ? defaultValue : array[0]; } /** @@ -91,8 +91,9 @@ public abstract class ArrayUtils { * @see java.lang.reflect.Array#newInstance(Class, int) */ public static Object[] insert(Object[] originalArray, int position, Object element) { - Object[] newArray = (Object[]) Array.newInstance(originalArray.getClass().getComponentType(), - originalArray.length + 1); + + Object[] newArray = + (Object[]) Array.newInstance(originalArray.getClass().getComponentType(), originalArray.length + 1); // copy all elements before the given position (here, position refers to the length, or number of elements @@ -106,7 +107,8 @@ public abstract class ArrayUtils { // copy remaining elements from originalArray, starting at position, to new array if (position < originalArray.length) { - System.arraycopy(originalArray, position, newArray, position + 1, originalArray.length - position); + System.arraycopy(originalArray, position, newArray, position + 1, + originalArray.length - position); } return newArray; @@ -120,7 +122,7 @@ public abstract class ArrayUtils { * @see #length(Object...) */ public static boolean isEmpty(Object[] array) { - return (length(array) == 0); + return length(array) == 0; } /** @@ -130,7 +132,7 @@ public abstract class ArrayUtils { * @return the length of the given array or 0 if the array reference is null. */ public static int length(Object[] array) { - return (array != null ? array.length : 0); + return array != null ? array.length : 0; } /** @@ -145,7 +147,7 @@ public abstract class ArrayUtils { */ @SuppressWarnings("unchecked") public static T[] nullSafeArray(T[] array, Class componentType) { - return (array != null ? array : (T[]) Array.newInstance(componentType, 0)); + return array != null ? array : (T[]) Array.newInstance(componentType, 0); } /** @@ -159,8 +161,9 @@ public abstract class ArrayUtils { * @see java.lang.reflect.Array#newInstance(Class, int) */ public static Object[] remove(Object[] originalArray, int position) { - Object[] newArray = (Object[]) Array.newInstance(originalArray.getClass().getComponentType(), - originalArray.length - 1); + + Object[] newArray = + (Object[]) Array.newInstance(originalArray.getClass().getComponentType(), originalArray.length - 1); // copy all elements before position (here, position refers to the length, or number of elements to be copied if (position > 0) { @@ -169,7 +172,8 @@ public abstract class ArrayUtils { // copy remaining elements after position from the originalArray if (position < originalArray.length - 1) { - System.arraycopy(originalArray, position + 1, newArray, position, originalArray.length - 1 - position); + System.arraycopy(originalArray, position + 1, newArray, position, + originalArray.length - 1 - position); } return newArray; @@ -184,7 +188,9 @@ public abstract class ArrayUtils { * @see java.util.Arrays#sort(Object[]) */ public static > T[] sort(T[] array) { + Arrays.sort(array); + return array; } } diff --git a/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java b/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java index 73393781..ed0f8a91 100644 --- a/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/CacheUtils.java @@ -23,7 +23,6 @@ import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheClosedException; import org.apache.geode.cache.CacheFactory; 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.ClientCacheFactory; import org.apache.geode.distributed.DistributedSystem; @@ -67,13 +66,16 @@ public abstract class CacheUtils extends DistributedSystemUtils { /* (non-Javadoc) */ public static boolean isDurable(ClientCache clientCache) { - DistributedSystem distributedSystem = getDistributedSystem(clientCache); - // NOTE technically the following code snippet would be more useful/valuable but is not "testable"! //((InternalDistributedSystem) distributedSystem).getConfig().getDurableClientId(); - return (isConnected(distributedSystem) && StringUtils.hasText(distributedSystem.getProperties() - .getProperty(DURABLE_CLIENT_ID_PROPERTY_NAME, null))); + return Optional.ofNullable(clientCache) + .map(CacheUtils::getDistributedSystem) + .filter(DistributedSystem::isConnected) + .map(DistributedSystem::getProperties) + .map(properties -> properties.getProperty(DURABLE_CLIENT_ID_PROPERTY_NAME, null)) + .filter(StringUtils::hasText) + .isPresent(); } /* (non-Javadoc) */ @@ -91,6 +93,7 @@ public abstract class CacheUtils extends DistributedSystemUtils { /* (non-Javadoc) */ public static boolean closeCache() { + try { CacheFactory.getAnyInstance().close(); return true; @@ -102,6 +105,7 @@ public abstract class CacheUtils extends DistributedSystemUtils { /* (non-Javadoc) */ public static boolean closeClientCache() { + try { ClientCacheFactory.getAnyInstance().close(); return true; @@ -113,6 +117,7 @@ public abstract class CacheUtils extends DistributedSystemUtils { /* (non-Javadoc) */ public static Cache getCache() { + try { return CacheFactory.getAnyInstance(); } @@ -123,6 +128,7 @@ public abstract class CacheUtils extends DistributedSystemUtils { /* (non-Javadoc) */ public static ClientCache getClientCache() { + try { return ClientCacheFactory.getAnyInstance(); } @@ -135,9 +141,4 @@ public abstract class CacheUtils extends DistributedSystemUtils { public static GemFireCache resolveGemFireCache() { return Optional.ofNullable(getClientCache()).orElseGet(CacheUtils::getCache); } - - /* (non-Javadoc) */ - public static String toRegionPath(String regionName) { - return String.format("%1$s%2$s", Region.SEPARATOR, regionName); - } } diff --git a/src/main/java/org/springframework/data/gemfire/util/CollectionUtils.java b/src/main/java/org/springframework/data/gemfire/util/CollectionUtils.java index e11556eb..431ecaea 100644 --- a/src/main/java/org/springframework/data/gemfire/util/CollectionUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/CollectionUtils.java @@ -16,6 +16,7 @@ package org.springframework.data.gemfire.util; +import static java.util.stream.StreamSupport.stream; import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; import java.util.ArrayList; @@ -26,6 +27,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; @@ -61,11 +63,10 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.Collection */ public static > T addAll(T collection, Iterable iterable) { - Assert.notNull(collection, "Collection must not be null"); - for (E element : nullSafeIterable(iterable)) { - collection.add(element); - } + Assert.notNull(collection, "Collection is required"); + + stream(nullSafeIterable(iterable).spliterator(), false).forEach(collection::add); return collection; } @@ -79,8 +80,11 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio */ @SafeVarargs public static Set asSet(T... elements) { + Set set = new HashSet<>(elements.length); + Collections.addAll(set, elements); + return Collections.unmodifiableSet(set); } @@ -93,6 +97,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.Collection#contains(Object) */ public static boolean containsAny(Collection collection, Object... elements) { + if (collection != null) { for (Object element : nullSafeArray(elements, Object.class)) { if (collection.contains(element)) { @@ -104,20 +109,6 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio return false; } - /** - * Returns the given {@link Iterable} if not {@literal null} or empty, otherwise returns the {@code defaultIterable}. - * - * @param concrete {@link Class} type of the {@link Iterable}. - * @param {@link Class} type of the elements in the {@link Iterable Iterables}. - * @param iterable {@link Iterable} to evaluate. - * @param defaultIterable {@link Iterable} to return if the given {@code iterable} is {@literal null} or empty. - * @return {@code iterable} if not {@literal null} or empty otherwise return {@code defaultIterable}. - * @see java.lang.Iterable - */ - public static > T defaultIfEmpty(T iterable, T defaultIterable) { - return (iterable != null && iterable.iterator().hasNext() ? iterable : defaultIterable); - } - /** * Returns an empty {@link Iterable} object. * @@ -168,7 +159,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.Collection */ public static Collection nullSafeCollection(Collection collection) { - return (collection != null ? collection : Collections.emptyList()); + return collection != null ? collection : Collections.emptyList(); } /** @@ -182,7 +173,21 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.Iterator */ public static Iterable nullSafeIterable(Iterable iterable) { - return (iterable != null ? iterable : Collections::emptyIterator); + return iterable != null ? iterable : Collections::emptyIterator; + } + + /** + * Returns the given {@link Iterable} if not {@literal null} or empty, otherwise returns the {@code defaultIterable}. + * + * @param concrete {@link Class} type of the {@link Iterable}. + * @param {@link Class} type of the elements in the {@link Iterable Iterables}. + * @param iterable {@link Iterable} to evaluate. + * @param defaultIterable {@link Iterable} to return if the given {@code iterable} is {@literal null} or empty. + * @return {@code iterable} if not {@literal null} or empty otherwise return {@code defaultIterable}. + * @see java.lang.Iterable + */ + public static > T nullSafeIterable(T iterable, T defaultIterable) { + return Optional.ofNullable(iterable).filter(it -> it.iterator().hasNext()).orElse(defaultIterable); } /** @@ -196,7 +201,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.List */ public static List nullSafeList(List list) { - return (list != null ? list : Collections.emptyList()); + return list != null ? list : Collections.emptyList(); } /** @@ -212,7 +217,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio */ @SuppressWarnings("all") public static Map nullSafeMap(Map map) { - return (map != null ? map : Collections.emptyMap()); + return map != null ? map : Collections.emptyMap(); } /** @@ -226,7 +231,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.Set */ public static Set nullSafeSet(Set set) { - return (set != null ? set : Collections.emptySet()); + return set != null ? set : Collections.emptySet(); } /** @@ -239,7 +244,9 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.List */ public static > List sort(List list) { + Collections.sort(list); + return list; } @@ -257,7 +264,8 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio * @see java.util.List */ public static List subList(List source, int... indices) { - Assert.notNull(source, "List must not be null"); + + Assert.notNull(source, "List is required"); List result = new ArrayList<>(indices.length); @@ -270,6 +278,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio /* (non-Javadoc) */ public static String toString(Map map) { + StringBuilder builder = new StringBuilder("{\n"); int count = 0; diff --git a/src/main/java/org/springframework/data/gemfire/util/DistributedSystemUtils.java b/src/main/java/org/springframework/data/gemfire/util/DistributedSystemUtils.java index 3d8a5655..7b06df36 100644 --- a/src/main/java/org/springframework/data/gemfire/util/DistributedSystemUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/DistributedSystemUtils.java @@ -57,7 +57,8 @@ public abstract class DistributedSystemUtils extends SpringUtils { String durableClientId, Integer durableClientTimeout) { if (StringUtils.hasText(durableClientId)) { - Assert.notNull(gemfireProperties, "gemfireProperties must not be null"); + + Assert.notNull(gemfireProperties, "gemfireProperties are required"); gemfireProperties.setProperty(DURABLE_CLIENT_ID_PROPERTY_NAME, durableClientId); @@ -71,7 +72,7 @@ public abstract class DistributedSystemUtils extends SpringUtils { /* (non-Javadoc) */ public static boolean isConnected(DistributedSystem distributedSystem) { - return (distributedSystem != null && distributedSystem.isConnected()); + return Optional.ofNullable(distributedSystem).filter(DistributedSystem::isConnected).isPresent(); } /* (non-Javadoc) */ diff --git a/src/main/java/org/springframework/data/gemfire/util/RegionUtils.java b/src/main/java/org/springframework/data/gemfire/util/RegionUtils.java index 36f35f56..b7a4a27e 100644 --- a/src/main/java/org/springframework/data/gemfire/util/RegionUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/RegionUtils.java @@ -23,10 +23,12 @@ import org.apache.geode.cache.RegionAttributes; import org.springframework.util.StringUtils; /** - * The RegionUtils class... + * The {@link RegionUtils} class is an abstract utility class for working with {@link Region Regions}. * * @author John Blum - * @since 1.0.0 + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.RegionAttributes + * @since 2.0.0 */ public abstract class RegionUtils extends CacheUtils { @@ -38,4 +40,9 @@ public abstract class RegionUtils extends CacheUtils { .filter(StringUtils::hasText) .isPresent(); } + + /* (non-Javadoc) */ + public static String toRegionPath(String regionName) { + return String.format("%1$s%2$s", Region.SEPARATOR, regionName); + } } diff --git a/src/main/java/org/springframework/data/gemfire/util/StreamUtils.java b/src/main/java/org/springframework/data/gemfire/util/StreamUtils.java new file mode 100644 index 00000000..a4f798b1 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/util/StreamUtils.java @@ -0,0 +1,51 @@ +/* + * 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.util; + +import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; + +import java.util.stream.Stream; + +/** + * The {@link StreamUtils} class is a abstract utility class for working with {@link Stream Streams}. + * + * @author John Blum + * @see java.util.stream.Stream + * @since 2.0.2 + */ +public abstract class StreamUtils { + + /** + * Concatenates an array of {@link Stream Streams} into a single, continuous {@link Stream}. + * + * @param {@link Class type} of elements in the {@link Stream Streams}. + * @param streams array of {@link Stream Streams} to concatenate. + * @return the concatenated array of {@link Stream Streams} as a single, continuous {@link Stream}. + * @see java.util.stream.Stream + */ + @SuppressWarnings("unchecked") + public static Stream concat(Stream... streams) { + + Stream concatenatedStream = Stream.empty(); + + for (Stream stream : nullSafeArray(streams, Stream.class)) { + concatenatedStream = Stream.concat(concatenatedStream, stream); + } + + return concatenatedStream; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/GemfireUtilsTest.java b/src/test/java/org/springframework/data/gemfire/GemfireUtilsTest.java index 7622d0ee..4ae3a31d 100644 --- a/src/test/java/org/springframework/data/gemfire/GemfireUtilsTest.java +++ b/src/test/java/org/springframework/data/gemfire/GemfireUtilsTest.java @@ -45,6 +45,7 @@ public class GemfireUtilsTest { @Test public void isClientWithClientIsTrue() { + ClientCache mockClient = mock(ClientCache.class); assertThat(GemfireUtils.isClient(mockClient), is(true)); @@ -54,6 +55,7 @@ public class GemfireUtilsTest { @Test public void isClientWithNonClientIsFalse() { + Cache mockCache = mock(Cache.class); assertThat(GemfireUtils.isClient(mockCache), is(false)); @@ -63,7 +65,9 @@ public class GemfireUtilsTest { @Test public void isDurableWithDurableClientIsTrue() { + ClientCache mockClientCache = mock(ClientCache.class); + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class); Properties gemfireProperties = new Properties(); @@ -83,7 +87,9 @@ public class GemfireUtilsTest { @Test public void isDurableWhenNotDurableClientIsFalse() { + ClientCache mockClientCache = mock(ClientCache.class); + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class); Properties gemfireProperties = new Properties(); @@ -103,7 +109,9 @@ public class GemfireUtilsTest { @Test public void isDurableWhenDistributedSystemIsNotConnectedIsFalse() { + ClientCache mockClientCache = mock(ClientCache.class); + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class); when(mockClientCache.getDistributedSystem()).thenReturn(mockDistributedSystem); @@ -118,6 +126,7 @@ public class GemfireUtilsTest { @Test public void isPeerWithPeerIsTrue() { + Cache mockCache = mock(Cache.class); assertThat(GemfireUtils.isPeer(mockCache), is(true)); @@ -127,6 +136,7 @@ public class GemfireUtilsTest { @Test public void isPeerWithNonPeerIsFalse() { + ClientCache mockClientCache = mock(ClientCache.class); assertThat(GemfireUtils.isPeer(mockClientCache), is(false)); diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java index c6abb7e1..3267a5bd 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsIntegrationTests.java @@ -20,11 +20,16 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.atomic.AtomicBoolean; +import javax.cache.annotation.CacheDefaults; +import javax.cache.annotation.CacheRemoveAll; +import javax.cache.annotation.CacheResult; + import org.apache.geode.cache.GemFireCache; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationContext; @@ -48,44 +53,58 @@ public class EnableCachingDefinedRegionsIntegrationTests { private ApplicationContext applicationContext; @Autowired - private CacheableEchoService echoService; + private JCacheEchoService jcacheEchoService; + + @Autowired + private SpringCacheableEchoService springEchoService; @Autowired private GemFireCache gemfireCache; @Before public void setup() { - - //System.err.printf("@Cacheable Beans [%s]%n", - // Arrays.toString(applicationContext.getBeanNamesForAnnotation(Cacheable.class))); - assertThat(this.gemfireCache).isNotNull(); - - //System.err.printf("Cache Regions [%s]%n", this.gemfireCache.rootRegions().stream() - // .map(Region::getFullPath).collect(Collectors.toSet())); } @Test public void cacheRegionsExists() { - assertThat(gemfireCache.getRegion("/Example")).isNotNull(); assertThat(gemfireCache.getRegion("/Echo")).isNotNull(); + assertThat(gemfireCache.getRegion("/JCacheOne")).isNotNull(); + assertThat(gemfireCache.getRegion("/JCacheTwo")).isNotNull(); + assertThat(gemfireCache.getRegion("/SpringOne")).isNotNull(); } @Test - public void echoServiceOperationsAreSuccessful() { + public void echoServiceCachingWithJCacheIsSuccessful() { - assertThat(echoService.isCacheMiss()).isFalse(); - assertThat(echoService.echo("one")).isEqualTo("one"); - assertThat(echoService.isCacheMiss()).isTrue(); - assertThat(echoService.echo("two")).isEqualTo("two"); - assertThat(echoService.isCacheMiss()).isTrue(); - assertThat(echoService.echo("one")).isEqualTo("one"); - assertThat(echoService.isCacheMiss()).isFalse(); - assertThat(echoService.echo("three")).isEqualTo("three"); - assertThat(echoService.isCacheMiss()).isTrue(); - assertThat(echoService.echo("two")).isEqualTo("two"); - assertThat(echoService.isCacheMiss()).isFalse(); + assertThat(jcacheEchoService.isCacheMiss()).isFalse(); + assertThat(jcacheEchoService.echo("four")).isEqualTo("four"); + assertThat(jcacheEchoService.isCacheMiss()).isTrue(); + assertThat(jcacheEchoService.echo("five")).isEqualTo("five"); + assertThat(jcacheEchoService.isCacheMiss()).isTrue(); + assertThat(jcacheEchoService.echo("four")).isEqualTo("four"); + assertThat(jcacheEchoService.isCacheMiss()).isFalse(); + assertThat(jcacheEchoService.echo("six")).isEqualTo("six"); + assertThat(jcacheEchoService.isCacheMiss()).isTrue(); + assertThat(jcacheEchoService.echo("five")).isEqualTo("five"); + assertThat(jcacheEchoService.isCacheMiss()).isFalse(); + } + + @Test + public void echoServiceCachingWithSpringIsSuccessful() { + + assertThat(springEchoService.isCacheMiss()).isFalse(); + assertThat(springEchoService.echo("one")).isEqualTo("one"); + assertThat(springEchoService.isCacheMiss()).isTrue(); + assertThat(springEchoService.echo("two")).isEqualTo("two"); + assertThat(springEchoService.isCacheMiss()).isTrue(); + assertThat(springEchoService.echo("one")).isEqualTo("one"); + assertThat(springEchoService.isCacheMiss()).isFalse(); + assertThat(springEchoService.echo("three")).isEqualTo("three"); + assertThat(springEchoService.isCacheMiss()).isTrue(); + assertThat(springEchoService.echo("two")).isEqualTo("two"); + assertThat(springEchoService.isCacheMiss()).isFalse(); } @PeerCacheApplication(name = "EnableCachingDefinedRegionsIntegrationTests", logLevel = "warning") @@ -93,18 +112,28 @@ public class EnableCachingDefinedRegionsIntegrationTests { static class TestConfiguration { @Bean - CacheableEchoService echoService() { - return new CacheableEchoService(); + JCacheEchoService jcacheEchoService() { + return new JCacheEchoService(); } @Bean - TestService testService() { - return new CachingTestService(); + @Qualifier("Spring") + SpringCacheableEchoService springEchoService() { + return new SpringCacheableEchoService(); + } + + @Bean + TestService testServiceOne() { + return new SpringCachingTestService(); + } + + @Bean + TestService testServiceTwo() { + return new Jsr107CachingTestService(); } } - @Service - static class CacheableEchoService { + static abstract class AbstractCacheableService { private final AtomicBoolean cacheMiss = new AtomicBoolean(false); @@ -112,9 +141,27 @@ public class EnableCachingDefinedRegionsIntegrationTests { return this.cacheMiss.compareAndSet(true, false); } + public void setCacheMiss() { + this.cacheMiss.set(true); + } + } + + @Service + static class JCacheEchoService extends AbstractCacheableService { + + @CacheResult(cacheName = "Echo") + public Object echo(String key) { + setCacheMiss(); + return key; + } + } + + @Service + static class SpringCacheableEchoService extends AbstractCacheableService { + @Cacheable("Echo") public Object echo(String key) { - this.cacheMiss.set(true); + setCacheMiss(); return key; } } @@ -123,9 +170,19 @@ public class EnableCachingDefinedRegionsIntegrationTests { Object testMethod(String key); } - static class CachingTestService implements TestService { + @CacheDefaults(cacheName = "JCacheOne") + @CacheRemoveAll(cacheName = "SpringOne") + static class Jsr107CachingTestService implements TestService { - @CachePut("Example") + @CacheResult(cacheName = "JCacheTwo") + public Object testMethod(String key) { + return null; + } + } + + static class SpringCachingTestService implements TestService { + + @CachePut("SpringOne") public Object testMethod(String key) { return "test"; } diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java index 48da903b..f1e74821 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegionsUnitTests.java @@ -33,6 +33,11 @@ import java.util.Arrays; import java.util.Map; import java.util.Set; +import javax.cache.annotation.CacheDefaults; +import javax.cache.annotation.CacheRemove; +import javax.cache.annotation.CacheRemoveAll; +import javax.cache.annotation.CacheResult; + import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.client.ClientRegionShortcut; import org.junit.Before; @@ -88,7 +93,7 @@ public class EnableCachingDefinedRegionsUnitTests { return mockBeanDefinition; } catch (ClassNotFoundException cause) { - throw newRuntimeException(cause, "Mock for class [%s] failed", beanClass.getName()); + throw newRuntimeException(cause, "Creating a mock for class [%s] failed", beanClass.getName()); } } @@ -111,6 +116,16 @@ public class EnableCachingDefinedRegionsUnitTests { return mockBeanDefinitionRegistry; } + @Test + public void annotationTypeIsEnableCachingDefinedRegions() { + assertThat(this.configuration.getAnnotationType()).isEqualTo(EnableCachingDefinedRegions.class); + } + + @Test + public void getCacheNameResolverIsConfiguredProperly() { + assertThat(this.configuration.getCacheNameResolver()).isNotNull(); + } + @Test public void setGetAndResolveClientRegionShortcut() { @@ -319,13 +334,15 @@ public class EnableCachingDefinedRegionsUnitTests { verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) .registerBeanDefinition(anyString(), any(BeanDefinition.class)); - registeredRegionBeanNames.forEach(beanName -> + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); verify(mockBeanDefinitionRegistry, times(1)) - .registerBeanDefinition(eq(beanName), any(BeanDefinition.class))); + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); } @Test - public void cacheableServiceOneTwoThreeRegistersTwentyTwoRegionBeans() { + public void cacheableServiceOneTwoAndThreeRegistersTwentyTwoRegionBeans() { Map registeredBeanDefinitions = MapBuilder.newMapBuilder() .put("cacheableServiceOne", mockBeanDefinition(CacheableServiceOne.class)) @@ -345,45 +362,110 @@ public class EnableCachingDefinedRegionsUnitTests { verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); - verify(mockBeanDefinitionRegistry, times(1)) - .getBeanDefinition(eq("cacheableServiceOne")); - - verify(mockBeanDefinitionRegistry, times(1)) - .getBeanDefinition(eq("cacheableServiceTwo")); - - verify(mockBeanDefinitionRegistry, times(1)) - .getBeanDefinition(eq("cacheableServiceThree")); + Arrays.asList("cacheableServiceOne", "cacheableServiceTwo", "cacheableServiceThree").forEach(beanName -> + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinition(eq(beanName))); verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) .registerBeanDefinition(anyString(), any(BeanDefinition.class)); - registeredRegionBeanNames.forEach(beanName -> + registeredRegionBeanNames.forEach(beanName -> { + + int wantedNumberOfInvocations = "RegionSix".equals(beanName) || "RegionTwo".equals(beanName) ? 2 : 1; + + verify(mockBeanDefinitionRegistry, times(wantedNumberOfInvocations)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) - .registerBeanDefinition(eq(beanName), any(BeanDefinition.class))); + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); } @Test - @SuppressWarnings("unchecked") - public void collectCacheNamesForCacheableAndCachePutOnCacheableServiceThreeRegistersRegionFifteenSixteenSeventeen() { + public void cacheableServiceFiveRegistersRegionOneTwoThreeFourFiveSixAndSeven() { - assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, Cacheable.class, CachePut.class)) - .containsExactly("RegionFifteen", "RegionSixteen", "RegionSeventeen"); + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceFive", mockBeanDefinition(CacheableServiceFive.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionOne", "RegionTwo", "RegionThree", "RegionFour", "RegionFive", + "RegionSix", "RegionSeven"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceFive")); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); } @Test - @SuppressWarnings("unchecked") - public void collectCacheNamesForCacheableOnCacheableServiceThreeRegistersRegionFifteen() { + public void cacheableServiceSixRegistersRegionOneTwoThreeFourAndFive() { - assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, Cacheable.class)) - .containsExactly("RegionFifteen"); + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceSix", mockBeanDefinition(CacheableServiceSix.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionOne", "RegionTwo", "RegionThree", "RegionFour", "RegionFive"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceSix")); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); } @Test - @SuppressWarnings("unchecked") - public void collectCacheNamesForCachePutOnCacheableServiceThreeRegistersRegionSixteenSeventeen() { + public void cacheableServiceSevenRegistersRegionOneThroughEleven() { - assertThat(this.configuration.collectCacheNames(CacheableServiceThree.class, CachePut.class)) - .containsExactly("RegionSixteen", "RegionSeventeen"); + Map registeredBeanDefinitions = MapBuilder.newMapBuilder() + .put("cacheableServiceSeven", mockBeanDefinition(CacheableServiceSeven.class)) + .build(); + + BeanDefinitionRegistry mockBeanDefinitionRegistry = mockBeanDefinitionRegistry(registeredBeanDefinitions); + + this.configuration.registerBeanDefinitions(mockBeanDefinitionRegistry); + + Set registeredRegionBeanNames = + asSet("RegionOne", "RegionTwo", "RegionThree", "RegionFour", "RegionFive", "RegionSix", + "RegionSeven", "RegionEight", "RegionNine", "RegionTen", "RegionEleven"); + + verify(mockBeanDefinitionRegistry, times(1)).getBeanDefinitionNames(); + + verify(mockBeanDefinitionRegistry, times(1)) + .getBeanDefinition(eq("cacheableServiceSeven")); + + verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size())) + .registerBeanDefinition(anyString(), any(BeanDefinition.class)); + + registeredRegionBeanNames.forEach(beanName -> { + verify(mockBeanDefinitionRegistry, times(1)).containsBeanDefinition(eq(beanName)); + verify(mockBeanDefinitionRegistry, times(1)) + .registerBeanDefinition(eq(beanName), any(BeanDefinition.class)); + }); } @Service @@ -450,4 +532,63 @@ public class EnableCachingDefinedRegionsUnitTests { public void cacheableMethodFive() {} } + + @CacheDefaults(cacheName = "RegionOne") + @CacheRemoveAll(cacheName = "RegionSix") + @CacheResult(cacheName = "RegionSeven") + @SuppressWarnings("unused") + static class CacheableServiceFive { + + @javax.cache.annotation.CachePut(cacheName = "RegionTwo") + public void cacheableMethodOne() {} + + @CacheRemove(cacheName = "RegionThree") + public void cacheableMethodTwo() {} + + @CacheRemoveAll(cacheName = "RegionFour") + public void cacheableMethodThree() {} + + @CacheResult(cacheName = "RegionFive") + public void cacheableMethodFour() {} + + } + + @CacheDefaults(cacheName = "RegionOne") + @javax.cache.annotation.CachePut(cacheName = "RegionOne") + @CacheRemoveAll(cacheName = "RegionFive") + @SuppressWarnings("unused") + static class CacheableServiceSix { + + @javax.cache.annotation.CachePut(cacheName = "RegionTwo") + @javax.cache.annotation.CacheResult(cacheName = "RegionOne") + public void cacheableMethodOne() {} + + @CacheRemove(cacheName = "RegionThree") + public void cacheableMethodTwo() {} + + @CacheRemoveAll(cacheName = "RegionThree") + public void cacheableMethodThree() {} + + @CacheResult(cacheName = "RegionFour") + @CacheRemove(cacheName = "RegionFive") + public void cacheableMethodFour() {} + + } + + @Caching( + cacheable = { @Cacheable({ "RegionSix", "RegionSeven", "RegionEight" }), @Cacheable("RegionNine")}, + put = @CachePut("RegionTwo") + ) + @CacheDefaults(cacheName = "RegionOne") + static class CacheableServiceSeven extends CacheableServiceSix { + + @CachePut("RegionTen") + @CacheRemove(cacheName = "RegionFive") + @CacheResult(cacheName = "RegionEleven") + public void cacheableMethodFive() {} + + @Cacheable("RegionThree") + public void cacheableMethodSix() {} + + } } 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 b443223f..b25cd7dc 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 @@ -24,8 +24,8 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.springframework.data.gemfire.util.CacheUtils.toRegionPath; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList; +import static org.springframework.data.gemfire.util.RegionUtils.toRegionPath; import java.util.HashSet; import java.util.List; @@ -51,7 +51,6 @@ import org.apache.geode.cache.client.ClientRegionFactory; import org.apache.geode.cache.client.ClientRegionShortcut; import org.junit.After; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ConfigurableApplicationContext; @@ -344,14 +343,11 @@ public class EnableEntityDefinedRegionsUnitTests { } protected static ClientCache mockClientCache() { + ClientCache mockClientCache = mock(ClientCache.class, mockName("ClientCache")); - Answer> createClientRegionFactory = new Answer>() { - @Override @SuppressWarnings("unchecked") - public ClientRegionFactory answer(InvocationOnMock invocation) throws Throwable { - return mockClientRegionFactory(invocation.getArgument(0)); - } - }; + Answer> createClientRegionFactory = + invocation -> mockClientRegionFactory(invocation.getArgument(0)); when(mockClientCache.createClientRegionFactory(any(ClientRegionShortcut.class))) .thenAnswer(createClientRegionFactory); @@ -525,6 +521,7 @@ public class EnableEntityDefinedRegionsUnitTests { } /* (non-Javadoc) */ + @SuppressWarnings("unused") protected static Answer newSetter(Class parameterType, AtomicReference argument, R returnValue) { return invocation -> { diff --git a/src/test/java/org/springframework/data/gemfire/util/CollectionUtilsUnitTests.java b/src/test/java/org/springframework/data/gemfire/util/CollectionUtilsUnitTests.java index 818b52d0..380ecab8 100644 --- a/src/test/java/org/springframework/data/gemfire/util/CollectionUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/util/CollectionUtilsUnitTests.java @@ -44,6 +44,7 @@ import org.junit.rules.ExpectedException; * Unit tests for {@link CollectionUtils}. * * @author John Blum + * @see java.lang.Iterable * @see java.util.Collection * @see java.util.Collections * @see java.util.Enumeration @@ -63,6 +64,7 @@ public class CollectionUtilsUnitTests { @Test public void addAllIterableElementsToList() { + List target = new ArrayList<>(Arrays.asList(1, 2, 3)); Set source = new HashSet<>(Arrays.asList(1, 2, 3)); @@ -75,6 +77,7 @@ public class CollectionUtilsUnitTests { @Test public void addAllIterableElementsToSet() { + Set target = new HashSet<>(Arrays.asList(1, 2, 3)); Set source = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5)); @@ -87,15 +90,17 @@ public class CollectionUtilsUnitTests { @Test public void addIterableToNullCollection() { + exception.expect(IllegalArgumentException.class); exception.expectCause(is(nullValue(Throwable.class))); - exception.expectMessage("Collection must not be null"); + exception.expectMessage("Collection is required"); CollectionUtils.addAll(null, Collections.emptySet()); } @Test public void addEmptyIterableToCollection() { + Collection target = new ArrayList<>(Arrays.asList(1, 2, 3)); target = CollectionUtils.addAll(target, Collections.emptyList()); @@ -107,6 +112,7 @@ public class CollectionUtilsUnitTests { @Test public void addNullIterableToCollection() { + Collection target = new ArrayList<>(Arrays.asList(1, 2, 3)); target = CollectionUtils.addAll(target, null); @@ -118,6 +124,7 @@ public class CollectionUtilsUnitTests { @Test public void asSetContainsAllArrayElements() { + Object[] elements = { "a", "b", "c" }; Set set = CollectionUtils.asSet(elements); @@ -129,6 +136,7 @@ public class CollectionUtilsUnitTests { @Test public void asSetContainsUniqueArrayElements() { + Object[] elements = { 1, 2, 1 }; Set set = CollectionUtils.asSet(elements); @@ -140,6 +148,7 @@ public class CollectionUtilsUnitTests { @Test(expected = UnsupportedOperationException.class) public void asSetReturnsUnmodifiableSet() { + Set set = CollectionUtils.asSet(1, 2, 3); assertThat(set).isNotNull(); @@ -176,36 +185,9 @@ public class CollectionUtilsUnitTests { assertThat(CollectionUtils.containsAny(null, 1)).isFalse(); } - @Test - public void defaultIfEmptyWithNonNullNonEmptyIterableReturnsIterable() { - Iterable iterable = Collections.singleton(1); - Iterable defaultIterable = Collections.singleton(2); - - assertThat(CollectionUtils.defaultIfEmpty(iterable, defaultIterable)).isSameAs(iterable); - } - - @Test - public void defaultIfEmptyWithEmptyIterableReturnsDefault() { - Iterable iterable = Collections.emptySet(); - Iterable defaultIterable = Collections.singleton(2); - - assertThat(CollectionUtils.defaultIfEmpty(iterable, defaultIterable)).isSameAs(defaultIterable); - } - - @Test - public void defaultIfEmptyWithNullIterableReturnsDefault() { - Iterable defaultIterable = Collections.singleton(2); - - assertThat(CollectionUtils.defaultIfEmpty(null, defaultIterable)).isSameAs(defaultIterable); - } - - @Test - public void defaultIfEmptyWithNullIterableAndNullDefaultReturnsNull() { - assertThat(CollectionUtils.defaultIfEmpty((Iterable) null, null)).isNull(); - } - @Test public void emptyIterableReturnsEmptyIterable() { + Iterable iterable = CollectionUtils.emptyIterable(); assertThat(iterable).isNotNull(); @@ -216,6 +198,7 @@ public class CollectionUtilsUnitTests { @Test @SuppressWarnings("unchecked") public void iterableEnumeration() { + Enumeration mockEnumeration = mock(Enumeration.class, "MockEnumeration"); when(mockEnumeration.hasMoreElements()).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(false); @@ -241,6 +224,7 @@ public class CollectionUtilsUnitTests { @Test @SuppressWarnings("unchecked") public void iterableIterator() { + Iterator mockIterator = mock(Iterator.class, "MockIterator"); when(mockIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(false); @@ -265,6 +249,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeCollectionWithNonNullCollection() { + Collection mockCollection = mock(Collection.class); assertThat(CollectionUtils.nullSafeCollection(mockCollection)).isSameAs(mockCollection); @@ -272,6 +257,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeCollectionWithNullCollection() { + Collection collection = CollectionUtils.nullSafeCollection(null); assertThat(collection).isNotNull(); @@ -281,6 +267,7 @@ public class CollectionUtilsUnitTests { @Test @SuppressWarnings("unchecked") public void nullSafeIterableWithNonNullIterable() { + Iterable mockIterable = mock(Iterable.class); assertThat(CollectionUtils.nullSafeIterable(mockIterable)).isSameAs(mockIterable); @@ -288,6 +275,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeIterableWithNullIterable() { + Iterable iterable = CollectionUtils.nullSafeIterable(null); assertThat(iterable).isNotNull(); @@ -295,8 +283,10 @@ public class CollectionUtilsUnitTests { assertThat(iterable.iterator().hasNext()).isFalse(); } + @SuppressWarnings("all") @Test(expected = IllegalStateException.class) public void nullSafeIterableIterator() { + Iterable iterable = CollectionUtils.nullSafeIterable(null); assertThat(iterable).isNotNull(); @@ -323,8 +313,40 @@ public class CollectionUtilsUnitTests { } } + @Test + public void nullSafeIterableWithNonNullNonEmptyIterableReturnsIterable() { + + Iterable iterable = Collections.singleton(1); + Iterable defaultIterable = Collections.singleton(2); + + assertThat(CollectionUtils.nullSafeIterable(iterable, defaultIterable)).isSameAs(iterable); + } + + @Test + public void nullSafeIterableWithEmptyIterableReturnsDefault() { + + Iterable iterable = Collections.emptySet(); + Iterable defaultIterable = Collections.singleton(2); + + assertThat(CollectionUtils.nullSafeIterable(iterable, defaultIterable)).isSameAs(defaultIterable); + } + + @Test + public void nullSafeIterableWithNullIterableReturnsDefault() { + + Iterable defaultIterable = Collections.singleton(2); + + assertThat(CollectionUtils.nullSafeIterable(null, defaultIterable)).isSameAs(defaultIterable); + } + + @Test + public void nullSafeIterableWithNullIterableAndNullDefaultReturnsNull() { + assertThat(CollectionUtils.nullSafeIterable((Iterable) null, null)).isNull(); + } + @Test public void nullSafeListWithNonNullList() { + List mockList = mock(List.class); assertThat(CollectionUtils.nullSafeList(mockList)).isSameAs(mockList); @@ -332,6 +354,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeListWithNullList() { + List list = CollectionUtils.nullSafeList(null); assertThat(list).isNotNull(); @@ -340,6 +363,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeMapWithNonNullMap() { + Map mockMap = mock(Map.class); assertThat(CollectionUtils.nullSafeMap(mockMap)).isSameAs(mockMap); @@ -347,6 +371,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeMapWithNullMap() { + Map map = CollectionUtils.nullSafeMap(null); assertThat(map).isNotNull(); @@ -355,6 +380,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeSetWithNonNullSet() { + Set mockSet = mock(Set.class); assertThat(CollectionUtils.nullSafeSet(mockSet)).isSameAs(mockSet); @@ -362,6 +388,7 @@ public class CollectionUtilsUnitTests { @Test public void nullSafeSetWithNullSet() { + Set set = CollectionUtils.nullSafeSet(null); assertThat(set).isNotNull(); @@ -370,6 +397,7 @@ public class CollectionUtilsUnitTests { @Test public void sortIsSuccessful() { + List list = new ArrayList(Arrays.asList(2, 3, 1)); List sortedList = CollectionUtils.sort(list); @@ -379,6 +407,7 @@ public class CollectionUtilsUnitTests { @Test public void subListFromListWithIndexesReturnsSubList() { + List list = Arrays.asList(0, 1, 2, 3); List subList = CollectionUtils.subList(list, 1, 3); @@ -390,6 +419,7 @@ public class CollectionUtilsUnitTests { @Test public void subListFromListWithNoIndexesReturnsEmptyList() { + List subList = CollectionUtils.subList(Arrays.asList(0, 1, 2)); assertThat(subList).isNotNull(); @@ -403,9 +433,10 @@ public class CollectionUtilsUnitTests { @Test public void subListWithNullSourceListThrowsIllegalArgumentException() { + exception.expect(IllegalArgumentException.class); exception.expectCause(is(nullValue(Throwable.class))); - exception.expectMessage("List must not be null"); + exception.expectMessage("List is required"); CollectionUtils.subList(null, 1, 2, 3); } diff --git a/src/test/java/org/springframework/data/gemfire/util/StreamUtilsTests.java b/src/test/java/org/springframework/data/gemfire/util/StreamUtilsTests.java new file mode 100644 index 00000000..265c3905 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/util/StreamUtilsTests.java @@ -0,0 +1,77 @@ +/* + * 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.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Test; + +/** + * Unit tests for {@link StreamUtils}. + * + * @author John Blum + * @see java.util.stream.Stream + * @see org.junit.Test + * @see org.springframework.data.gemfire.util.StreamUtils + * @since 2.0.0 + */ +public class StreamUtilsTests { + + @Test + @SuppressWarnings("unchecked") + public void concatNoStreams() { + + Stream stream = StreamUtils.concat(); + + assertThat(stream).isNotNull(); + assertThat(stream.count()).isEqualTo(0); + } + + @Test + @SuppressWarnings("unchecked") + public void concatOneStream() { + + Stream stream = StreamUtils.concat(Stream.of(1, 2, 3)); + + assertThat(stream).isNotNull(); + assertThat(stream.collect(Collectors.toList())).containsExactly(1, 2, 3); + } + + @Test + @SuppressWarnings("unchecked") + public void concatTwoStreams() { + + Stream stream = StreamUtils.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6)); + + assertThat(stream).isNotNull(); + assertThat(stream.collect(Collectors.toList())).containsExactly(1, 2, 3, 4, 5, 6); + } + + @Test + @SuppressWarnings("unchecked") + public void concatThreeStreams() { + + Stream stream = + StreamUtils.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6), Stream.of(7, 8, 9)); + + assertThat(stream).isNotNull(); + assertThat(stream.collect(Collectors.toList())).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9); + } +}