DATAGEODE-225 - Allow the JSR-107, JCache API to be an optional dependency when using @EnableCachingDefinedRegions.

This commit is contained in:
John Blum
2019-08-21 00:40:23 -07:00
parent ad80c1f696
commit 5dfd9461d8
4 changed files with 137 additions and 33 deletions

View File

@@ -86,8 +86,8 @@
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<!-- Core Spring Framework -->

View File

@@ -19,7 +19,6 @@ 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;
@@ -34,11 +33,6 @@ 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;
@@ -65,6 +59,7 @@ import org.springframework.data.gemfire.config.annotation.support.AbstractAnnota
import org.springframework.data.gemfire.config.annotation.support.CacheTypeAwareRegionFactoryBean;
import org.springframework.data.gemfire.support.CompositeLifecycle;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.data.gemfire.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
@@ -109,8 +104,13 @@ import org.springframework.util.StringUtils;
@Configuration
public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
private final CacheNameResolver[] configuredCacheNameResolvers = {
new Jsr107CacheAnnotationsCacheNameResolverFactory().create(),
new SpringCacheAnnotationsCacheNameResolver()
};
private final CacheNameResolver composableCacheNameResolver = type ->
asList(new Jsr107CacheAnnotationsCacheNameResolver(), new SpringCacheAnnotationsCacheNameResolver()).stream()
asList(this.configuredCacheNameResolvers).stream()
.flatMap(cacheNameResolver -> cacheNameResolver.resolveCacheNames(type).stream())
.collect(Collectors.toSet());
@@ -346,6 +346,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
}
@Bean
@SuppressWarnings("unused")
public Lifecycle cachingDefinedRegionsCompositeLifecycleBean() {
return this.compositeLifecycle;
}
@@ -356,11 +357,11 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
* 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 Jsr107CacheAnnotationsCacheNameResolver
* @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.SpringCacheAnnotationsCacheNameResolver
*/
@FunctionalInterface
protected interface CacheNameResolver {
public interface CacheNameResolver {
Set<String> resolveCacheNames(Class<?> type);
}
@@ -372,13 +373,19 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
*
* @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.CacheNameResolver
*/
protected abstract class AbstractCacheNameResolver implements CacheNameResolver {
public abstract static class AbstractCacheNameResolver extends AbstractAnnotationConfigSupport
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];
@Override
protected final Class<? extends Annotation> getAnnotationType() {
return null;
}
protected abstract Class<? extends Annotation>[] getClassCacheAnnotationTypes();
protected abstract Class<? extends Annotation>[] getMethodCacheAnnotationTypes();
@@ -395,11 +402,12 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
protected Set<String> resolveCacheNames(Annotation annotation) {
return Optional.ofNullable(annotation)
.map(it -> getAnnotationAttributes(it))
.map(this::getAnnotationAttributes)
.map(annotationAttributes -> {
String attributeName = annotationAttributes.containsKey(SPRING_CACHE_NAMES_ATTRIBUTE_NAME)
? SPRING_CACHE_NAMES_ATTRIBUTE_NAME : JSR_107_CACHE_NAME_ATTRIBUTE_NAME;
? SPRING_CACHE_NAMES_ATTRIBUTE_NAME
: JSR_107_CACHE_NAME_ATTRIBUTE_NAME;
return annotationAttributes.containsKey(attributeName)
? annotationAttributes.getStringArray(attributeName)
@@ -407,7 +415,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
})
.map(CollectionUtils::asSet)
.orElse(Collections.emptySet());
.orElseGet(Collections::emptySet);
}
@Override
@@ -416,7 +424,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
Set<String> cacheNames = new HashSet<>(resolveCacheNames(type, getClassCacheAnnotationTypes()));
stream(type.getMethods())
.filter(method -> isUserLevelMethod(method))
.filter(this::isUserLevelMethod)
.forEach(method -> cacheNames.addAll(resolveCacheNames(method, getMethodCacheAnnotationTypes())));
return cacheNames;
@@ -434,22 +442,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
}
}
protected class Jsr107CacheAnnotationsCacheNameResolver extends AbstractCacheNameResolver {
@Override
@SuppressWarnings("unchecked")
protected Class<? extends Annotation>[] getClassCacheAnnotationTypes() {
return append(getMethodCacheAnnotationTypes(), CacheDefaults.class);
}
@Override
protected Class<? extends Annotation>[] getMethodCacheAnnotationTypes() {
return asArray(javax.cache.annotation.CachePut.class, CacheRemove.class,
CacheRemoveAll.class, CacheResult.class);
}
}
protected class SpringCacheAnnotationsCacheNameResolver extends AbstractCacheNameResolver {
protected static class SpringCacheAnnotationsCacheNameResolver extends AbstractCacheNameResolver {
@Override
@SuppressWarnings("unchecked")
@@ -469,7 +462,8 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
cacheNames.addAll(resolveCachingCacheNames(type));
stream(type.getMethods()).filter(method -> isUserLevelMethod(method))
stream(type.getMethods())
.filter(this::isUserLevelMethod)
.forEach(method -> cacheNames.addAll(resolveCachingCacheNames(method)));
return cacheNames;
@@ -481,7 +475,7 @@ public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfig
Set<String> cacheNames = new HashSet<>();
Optional.ofNullable(resolveAnnotation(annotatedElement, Caching.class)).ifPresent(caching ->
concat(stream(caching.cacheable()), stream(caching.evict()), stream(caching.put()))
StreamUtils.concat(stream(caching.cacheable()), stream(caching.evict()), stream(caching.put()))
.flatMap(cacheAnnotation -> resolveCacheNames(cacheAnnotation).stream())
.collect(Collectors.toCollection(() -> cacheNames)));

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
import java.lang.annotation.Annotation;
import javax.cache.annotation.CacheDefaults;
import javax.cache.annotation.CachePut;
import javax.cache.annotation.CacheRemove;
import javax.cache.annotation.CacheRemoveAll;
import javax.cache.annotation.CacheResult;
/**
* The {@link Jsr107CacheAnnotationsCacheNameResolver} class is a {@link CachingDefinedRegionsConfiguration.CacheNameResolver}
* implementation that can resolve JSR-107, JCache API cache annotations from a given {@link Class class type}.
*
* @author John Blum
* @see java.lang.annotation.Annotation
* @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration.AbstractCacheNameResolver
* @since 2.2.0
*/
class Jsr107CacheAnnotationsCacheNameResolver extends CachingDefinedRegionsConfiguration.AbstractCacheNameResolver {
@Override
@SuppressWarnings("unchecked")
protected Class<? extends Annotation>[] getClassCacheAnnotationTypes() {
return append(getMethodCacheAnnotationTypes(), CacheDefaults.class);
}
@Override
protected Class<? extends Annotation>[] getMethodCacheAnnotationTypes() {
return asArray(
CachePut.class,
CacheRemove.class,
CacheRemoveAll.class,
CacheResult.class
);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import java.util.Collections;
import java.util.Set;
import org.springframework.util.ClassUtils;
/**
* Factory class used to construct an instance of the {@link Jsr107CacheAnnotationsCacheNameResolver} if and only if
* the JSR-107, JCache API lib is on the application classpath.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.Jsr107CacheAnnotationsCacheNameResolver
* @since 2.2.0
*/
class Jsr107CacheAnnotationsCacheNameResolverFactory {
private final boolean jcacheApiPresent;
Jsr107CacheAnnotationsCacheNameResolverFactory() {
this.jcacheApiPresent =
ClassUtils.isPresent("javax.cache.annotation.CacheResult", getClass().getClassLoader());
}
CachingDefinedRegionsConfiguration.CacheNameResolver create() {
return this.jcacheApiPresent
? new Jsr107CacheAnnotationsCacheNameResolver()
: new NoOpCacheNameResolver();
}
private static class NoOpCacheNameResolver implements CachingDefinedRegionsConfiguration.CacheNameResolver {
@Override
public Set<String> resolveCacheNames(Class<?> type) {
return Collections.emptySet();
}
}
}