Review and refactor the AbstractAnnotationConfigSupport class.

* Rename the getLog() method to getLogger().
* Rename the newLog() method to newLogger().
* Reduce the use of java.util.Optional.
* Edit Javadoc.
* Annotate the API with Spring's @NonNull and @Nullable annotations.
* Rename AbstractAnnotationConfigSupportTests to AbstractAnnotationConfigSupportUnitTests.
This commit is contained in:
John Blum
2022-03-30 15:22:25 -07:00
parent 90f81bd463
commit 9d4178128f
2 changed files with 187 additions and 95 deletions

View File

@@ -16,9 +16,7 @@
*/ */
package org.springframework.data.gemfire.config.annotation.support; package org.springframework.data.gemfire.config.annotation.support;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; 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.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
@@ -50,12 +48,14 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.expression.spel.support.StandardTypeLocator; import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@@ -63,17 +63,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* The {@link AbstractAnnotationConfigSupport} class is an abstract base class encapsulating functionality * An abstract base class encapsulating functionality common to all Spring Data for Apache Geode (SDG) annotations
* common to all Annotations and configuration classes used to configure Pivotal GemFire/Apache Geode objects * and configuration classes used to configure Apache Geode objects using Spring Data for Apache Geode.
* with Spring Data GemFire or Spring Data Geode.
* *
* @author John Blum * @author John Blum
* @author Udo Kohlmeyer * @author Udo Kohlmeyer
* @see java.lang.ClassLoader * @see java.lang.ClassLoader
* @see java.lang.annotation.Annotation * @see java.lang.annotation.Annotation
* @see java.lang.reflect.AnnotatedElement
* @see org.springframework.beans.factory.BeanClassLoaderAware * @see org.springframework.beans.factory.BeanClassLoaderAware
* @see org.springframework.beans.factory.BeanFactory * @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
* @see org.springframework.beans.factory.support.AbstractBeanDefinition * @see org.springframework.beans.factory.support.AbstractBeanDefinition
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry * @see org.springframework.beans.factory.support.BeanDefinitionRegistry
@@ -90,12 +91,15 @@ public abstract class AbstractAnnotationConfigSupport
implements BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware { implements BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware {
protected static final Set<Integer> INFRASTRUCTURE_ROLES = protected static final Set<Integer> INFRASTRUCTURE_ROLES =
asSet(BeanDefinition.ROLE_INFRASTRUCTURE, BeanDefinition.ROLE_SUPPORT); CollectionUtils.asSet(BeanDefinition.ROLE_INFRASTRUCTURE, BeanDefinition.ROLE_SUPPORT);
protected static final String ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE = "org.springframework.data.gemfire"; protected static final String ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE = "org.springframework.data.gemfire";
protected static final String ORG_SPRINGFRAMEWORK_PACKAGE = "org.springframework"; protected static final String ORG_SPRINGFRAMEWORK_PACKAGE = "org.springframework";
protected static final String SPRING_DATA_GEMFIRE_PROPERTY_PREFIX = "spring.data.gemfire."; protected static final String SPRING_DATA_GEMFIRE_PROPERTY_PREFIX = "spring.data.gemfire.";
protected static final Supplier<ClassLoader> CURRENT_THREAD_CONTEXT_CLASS_LOADER =
() -> Thread.currentThread().getContextClassLoader();
private BeanFactory beanFactory; private BeanFactory beanFactory;
private ClassLoader beanClassLoader; private ClassLoader beanClassLoader;
@@ -104,7 +108,22 @@ public abstract class AbstractAnnotationConfigSupport
private final EvaluationContext evaluationContext; private final EvaluationContext evaluationContext;
private final Logger log; private final Logger logger;
/**
* Asserts the given, configured {@link Object} is not {@literal null}.
*
* @param <T> {@link Class type} of the {@link Object} to assert.
* @param object {@link Object} reference to assert as not {@literal null}.
* @param message {@link String} containing the message used in the {@link IllegalStateException}
* thrown by this assertion if the {@link Object} is {@literal null}.
* @return the given {@link Object} used as the subject of this assertion.
* @throws IllegalStateException if the {@link Object} is {@literal null}.
*/
private static <T> T assertReferenceAndReturn(T object, String message) {
Assert.state(object != null, message);
return object;
}
/** /**
* Determines whether the given {@link Number} has value. * Determines whether the given {@link Number} has value.
@@ -155,17 +174,18 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Constructs a new instance of {@link AbstractAnnotationConfigSupport}. * Constructs a new instance of {@link AbstractAnnotationConfigSupport} initialized with the given, non-required
* {@link BeanFactory}.
* *
* @param beanFactory reference to the Spring {@link BeanFactory}. * @param beanFactory reference to the Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.BeanFactory * @see org.springframework.beans.factory.BeanFactory
* @see #newEvaluationContext(BeanFactory) * @see #newEvaluationContext(BeanFactory)
* @see #newLog() * @see #newLogger()
*/ */
public AbstractAnnotationConfigSupport(@Nullable BeanFactory beanFactory) { public AbstractAnnotationConfigSupport(@Nullable BeanFactory beanFactory) {
this.evaluationContext = newEvaluationContext(beanFactory); this.evaluationContext = newEvaluationContext(beanFactory);
this.log = newLog(); this.logger = newLogger();
} }
/** /**
@@ -207,13 +227,13 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Constructs a new instance of {@link Logger} to log statements printed by Spring Data GemFire/Geode. * Constructs a new instance of {@link Logger} to log statements printed by Spring Data for Apache Geode.
* *
* @return a new instance of {@link Logger}. * @return a new instance of {@link Logger}.
* @see org.apache.commons.logging.LogFactory#getLog(Class) * @see org.slf4j.LoggerFactory#getLogger(Class)
* @see org.apache.commons.logging.Log * @see org.slf4j.Logger
*/ */
protected Logger newLog() { protected @NonNull Logger newLogger() {
return LoggerFactory.getLogger(getClass()); return LoggerFactory.getLogger(getClass());
} }
@@ -324,12 +344,14 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Null-safe method to determine whether the given {@link Object bean} is a Spring container provided * Null-safe method used to determine whether the given {@link Object bean} is a Spring container provided
* infrastructure bean. * infrastructure bean.
* *
* @param bean {@link Object} to evaluate. * @param bean {@link Object} to evaluate.
* @return {@literal true} iff the {@link Object bean} is not a Spring container infrastructure bean. * @return {@literal true} iff the {@link Object bean} is not a Spring container provided infrastructure bean.
* @see #isNotInfrastructureClass(String) * @see #isNotInfrastructureClass(String)
* @see java.lang.Object#getClass()
* @see java.lang.Class#getName()
*/ */
protected boolean isNotInfrastructureBean(@Nullable Object bean) { protected boolean isNotInfrastructureBean(@Nullable Object bean) {
@@ -341,12 +363,12 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Null-safe method to determine whether the bean defined in the given {@link BeanDefinition} * Null-safe method used to determine whether the bean defined by the given {@link BeanDefinition}
* is a Spring container provided infrastructure bean. * is a Spring container provided infrastructure bean.
* *
* @param beanDefinition {@link BeanDefinition} to evaluate. * @param beanDefinition {@link BeanDefinition} to evaluate.
* @return {@literal true} iff the bean defined in the given {@link BeanDefinition} is not a Spring container * @return {@literal true} iff the bean defined by the given {@link BeanDefinition} is not a Spring container
* infrastructure bean. * provided infrastructure bean.
* @see org.springframework.beans.factory.config.BeanDefinition * @see org.springframework.beans.factory.config.BeanDefinition
* @see #isNotInfrastructureClass(BeanDefinition) * @see #isNotInfrastructureClass(BeanDefinition)
* @see #isNotInfrastructureRole(BeanDefinition) * @see #isNotInfrastructureRole(BeanDefinition)
@@ -356,12 +378,12 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Null-safe method to determine whether the bean defined in the given {@link BeanDefinition} * Null-safe method used to determine whether the bean defined by the given {@link BeanDefinition}
* is a Spring container infrastructure bean based on the bean's {@link Class#getName() class type}. * is a Spring container infrastructure bean based on the bean's class name.
* *
* @param beanDefinition {@link BeanDefinition} of the bean to evaluate. * @param beanDefinition {@link BeanDefinition} of the bean to evaluate.
* @return {@literal true} iff the bean defined in the given {@link BeanDefinition} is not a Spring container * @return {@literal true} iff the bean defined in the given {@link BeanDefinition} is not a Spring container
* infrastructure bean. * infrastructure bean. Returns {@literal false} if the bean class name cannot be resolved.
* @see org.springframework.beans.factory.config.BeanDefinition * @see org.springframework.beans.factory.config.BeanDefinition
* @see #resolveBeanClassName(BeanDefinition) * @see #resolveBeanClassName(BeanDefinition)
* @see #isNotInfrastructureClass(String) * @see #isNotInfrastructureClass(String)
@@ -374,17 +396,19 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Determines whether the given {@link Class#getName() class type name} is considered a Spring container * Determines whether the given {@link Class#getName() class name} is considered a Spring container
* infrastructure type. * infrastructure type.
* *
* The class type name is considered a Spring container infrastructure type if the package name begins with * The {@link String class name} is considered a Spring container infrastructure type if the package name
* 'org.springframework', excluding 'org.springframework.data.gemfire'. * begins with {@literal org.springframework}, excluding {@literal org.springframework.data.gemfire}.
* *
* @param className {@link String} containing the name of the class type to evaluate. * @param className {@link String} containing the {@literal name} of the class type to evaluate;
* @return {@literal true} iff the given {@link Class#getName() class type name} is not considered a * must not be {@literal null}.
* Spring container infrastructure type. * @return {@literal true} iff the given {@link Class#getName() class name} is not considered a Spring container
* infrastructure type.
*/ */
protected boolean isNotInfrastructureClass(@NonNull String className) { boolean isNotInfrastructureClass(@NonNull String className) {
return className.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE) return className.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE)
|| !className.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE); || !className.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE);
} }
@@ -454,8 +478,10 @@ public abstract class AbstractAnnotationConfigSupport
* @see #getBeanClassLoader() * @see #getBeanClassLoader()
*/ */
protected @NonNull ClassLoader resolveBeanClassLoader() { protected @NonNull ClassLoader resolveBeanClassLoader() {
return Optional.ofNullable(getBeanClassLoader())
.orElseGet(() -> Thread.currentThread().getContextClassLoader()); ClassLoader beanClassLoader = getBeanClassLoader();
return beanClassLoader != null ? beanClassLoader : CURRENT_THREAD_CONTEXT_CLASS_LOADER.get();
} }
/** /**
@@ -475,8 +501,7 @@ public abstract class AbstractAnnotationConfigSupport
* @see org.springframework.beans.factory.BeanFactory * @see org.springframework.beans.factory.BeanFactory
*/ */
protected @NonNull BeanFactory getBeanFactory() { protected @NonNull BeanFactory getBeanFactory() {
return Optional.ofNullable(this.beanFactory) return assertReferenceAndReturn(this.beanFactory, "BeanFactory is required");
.orElseThrow(() -> newIllegalStateException("BeanFactory is required"));
} }
/** /**
@@ -517,8 +542,8 @@ public abstract class AbstractAnnotationConfigSupport
* @return a reference to the {@link Logger} used by this class to log {@link String messages}. * @return a reference to the {@link Logger} used by this class to log {@link String messages}.
* @see org.apache.commons.logging.Log * @see org.apache.commons.logging.Log
*/ */
protected @NonNull Logger getLog() { protected @NonNull Logger getLogger() {
return this.log; return this.logger;
} }
/** /**
@@ -538,10 +563,10 @@ public abstract class AbstractAnnotationConfigSupport
* @param message {@link Supplier} containing the {@link String message} and arguments to log. * @param message {@link Supplier} containing the {@link String message} and arguments to log.
* @see org.apache.commons.logging.Log#isDebugEnabled() * @see org.apache.commons.logging.Log#isDebugEnabled()
* @see org.apache.commons.logging.Log#debug(Object) * @see org.apache.commons.logging.Log#debug(Object)
* @see #getLog() * @see #getLogger()
*/ */
protected void logDebug(Supplier<String> message) { protected void logDebug(Supplier<String> message) {
Optional.ofNullable(getLog()) Optional.ofNullable(getLogger())
.filter(Logger::isDebugEnabled) .filter(Logger::isDebugEnabled)
.ifPresent(log -> log.debug(message.get())); .ifPresent(log -> log.debug(message.get()));
} }
@@ -563,10 +588,10 @@ public abstract class AbstractAnnotationConfigSupport
* @param message {@link Supplier} containing the {@link String message} and arguments to log. * @param message {@link Supplier} containing the {@link String message} and arguments to log.
* @see org.apache.commons.logging.Log#isInfoEnabled() * @see org.apache.commons.logging.Log#isInfoEnabled()
* @see org.apache.commons.logging.Log#info(Object) * @see org.apache.commons.logging.Log#info(Object)
* @see #getLog() * @see #getLogger()
*/ */
protected void logInfo(Supplier<String> message) { protected void logInfo(Supplier<String> message) {
Optional.ofNullable(getLog()) Optional.ofNullable(getLogger())
.filter(Logger::isInfoEnabled) .filter(Logger::isInfoEnabled)
.ifPresent(log -> log.info(message.get())); .ifPresent(log -> log.info(message.get()));
} }
@@ -588,10 +613,10 @@ public abstract class AbstractAnnotationConfigSupport
* @param message {@link Supplier} containing the {@link String message} and arguments to log. * @param message {@link Supplier} containing the {@link String message} and arguments to log.
* @see org.apache.commons.logging.Log#isWarnEnabled() * @see org.apache.commons.logging.Log#isWarnEnabled()
* @see org.apache.commons.logging.Log#warn(Object) * @see org.apache.commons.logging.Log#warn(Object)
* @see #getLog() * @see #getLogger()
*/ */
protected void logWarning(Supplier<String> message) { protected void logWarning(Supplier<String> message) {
Optional.ofNullable(getLog()) Optional.ofNullable(getLogger())
.filter(Logger::isWarnEnabled) .filter(Logger::isWarnEnabled)
.ifPresent(log -> log.info(message.get())); .ifPresent(log -> log.info(message.get()));
} }
@@ -613,16 +638,17 @@ public abstract class AbstractAnnotationConfigSupport
* @param message {@link Supplier} containing the {@link String message} and arguments to log. * @param message {@link Supplier} containing the {@link String message} and arguments to log.
* @see org.apache.commons.logging.Log#isErrorEnabled() * @see org.apache.commons.logging.Log#isErrorEnabled()
* @see org.apache.commons.logging.Log#error(Object) * @see org.apache.commons.logging.Log#error(Object)
* @see #getLog() * @see #getLogger()
*/ */
protected void logError(Supplier<String> message) { protected void logError(Supplier<String> message) {
Optional.ofNullable(getLog()) Optional.ofNullable(getLogger())
.filter(Logger::isErrorEnabled) .filter(Logger::isErrorEnabled)
.ifPresent(log -> log.info(message.get())); .ifPresent(log -> log.info(message.get()));
} }
/** /**
* Registers the {@link AbstractBeanDefinition} with the {@link BeanDefinitionRegistry} using a generated bean name. * Registers the {@link AbstractBeanDefinition} with the {@link BeanDefinitionRegistry} using a generated
* {@link String bean name}.
* *
* @param beanDefinition {@link AbstractBeanDefinition} to register. * @param beanDefinition {@link AbstractBeanDefinition} to register.
* @return the given {@link AbstractBeanDefinition}. * @return the given {@link AbstractBeanDefinition}.
@@ -642,7 +668,8 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Registers the {@link AbstractBeanDefinition} with the {@link BeanDefinitionRegistry} using a generated bean name. * Registers the {@link AbstractBeanDefinition} with the given {@link BeanDefinitionRegistry} using a generated
* {@link String bean name}.
* *
* @param beanDefinition {@link AbstractBeanDefinition} to register. * @param beanDefinition {@link AbstractBeanDefinition} to register.
* @param registry {@link BeanDefinitionRegistry} used to register the {@link AbstractBeanDefinition}. * @param registry {@link BeanDefinitionRegistry} used to register the {@link AbstractBeanDefinition}.
@@ -654,9 +681,9 @@ public abstract class AbstractAnnotationConfigSupport
protected @NonNull AbstractBeanDefinition register(@NonNull AbstractBeanDefinition beanDefinition, protected @NonNull AbstractBeanDefinition register(@NonNull AbstractBeanDefinition beanDefinition,
@Nullable BeanDefinitionRegistry registry) { @Nullable BeanDefinitionRegistry registry) {
Optional.ofNullable(registry).ifPresent(it -> if (registry != null) {
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, it) BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
); }
return beanDefinition; return beanDefinition;
} }
@@ -852,9 +879,9 @@ public abstract class AbstractAnnotationConfigSupport
*/ */
protected <A extends Annotation> A resolveAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { protected <A extends Annotation> A resolveAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
return (annotatedElement instanceof Class return annotatedElement instanceof Class
? AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType) ? AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType)
: AnnotationUtils.findAnnotation(annotatedElement, annotationType)); : AnnotationUtils.findAnnotation(annotatedElement, annotationType);
} }
/** /**
@@ -869,7 +896,9 @@ public abstract class AbstractAnnotationConfigSupport
* @see #resolveBeanClassLoader(BeanDefinitionRegistry) * @see #resolveBeanClassLoader(BeanDefinitionRegistry)
* @see #resolveBeanClass(BeanDefinition, ClassLoader) * @see #resolveBeanClass(BeanDefinition, ClassLoader)
*/ */
protected Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) { protected Optional<Class<?>> resolveBeanClass(@Nullable BeanDefinition beanDefinition,
@Nullable BeanDefinitionRegistry registry) {
return resolveBeanClass(beanDefinition, resolveBeanClassLoader(registry)); return resolveBeanClass(beanDefinition, resolveBeanClassLoader(registry));
} }
@@ -882,10 +911,13 @@ public abstract class AbstractAnnotationConfigSupport
* @return an {@link Optional} {@link Class} specifying the resolved type of the bean. * @return an {@link Optional} {@link Class} specifying the resolved type of the bean.
* @see org.springframework.beans.factory.config.BeanDefinition * @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
* @see #resolveBeanClassLoader(ConfigurableBeanFactory)
* @see #resolveBeanClass(BeanDefinition, ClassLoader) * @see #resolveBeanClass(BeanDefinition, ClassLoader)
*/ */
protected Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, ConfigurableBeanFactory beanFactory) { protected Optional<Class<?>> resolveBeanClass(@Nullable BeanDefinition beanDefinition,
return resolveBeanClass(beanDefinition, beanFactory.getBeanClassLoader()); @NonNull ConfigurableBeanFactory beanFactory) {
return resolveBeanClass(beanDefinition, resolveBeanClassLoader(beanFactory));
} }
/** /**
@@ -900,7 +932,8 @@ public abstract class AbstractAnnotationConfigSupport
* @see org.springframework.util.ClassUtils#forName(String, ClassLoader) * @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
* @see #resolveBeanClassName(BeanDefinition) * @see #resolveBeanClassName(BeanDefinition)
*/ */
protected Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, ClassLoader classLoader) { protected Optional<Class<?>> resolveBeanClass(@Nullable BeanDefinition beanDefinition,
@Nullable ClassLoader classLoader) {
Class<?> beanClass = beanDefinition instanceof AbstractBeanDefinition Class<?> beanClass = beanDefinition instanceof AbstractBeanDefinition
? safeResolveType(() -> ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(classLoader)) ? safeResolveType(() -> ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(classLoader))
@@ -908,9 +941,8 @@ public abstract class AbstractAnnotationConfigSupport
if (beanClass == null) { if (beanClass == null) {
beanClass = resolveBeanClassName(beanDefinition) beanClass = resolveBeanClassName(beanDefinition)
.map(beanClassName -> .map(beanClassName -> safeResolveType(() -> ClassUtils.forName(beanClassName, classLoader)))
safeResolveType(() -> .orElse(null);
ClassUtils.forName(beanClassName, classLoader))).orElse(null);
} }
return Optional.ofNullable(beanClass); return Optional.ofNullable(beanClass);
@@ -925,15 +957,35 @@ public abstract class AbstractAnnotationConfigSupport
* or the {@link Thread#currentThread() current Thread's} {@link Thread#getContextClassLoader() context ClassLoader}. * or the {@link Thread#currentThread() current Thread's} {@link Thread#getContextClassLoader() context ClassLoader}.
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader() * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader()
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry * @see org.springframework.beans.factory.support.BeanDefinitionRegistry
* @see java.lang.Thread#currentThread()
* @see java.lang.Thread#getContextClassLoader() * @see java.lang.Thread#getContextClassLoader()
* @see java.lang.Thread#currentThread()
*/ */
protected ClassLoader resolveBeanClassLoader(BeanDefinitionRegistry registry) { protected @NonNull ClassLoader resolveBeanClassLoader(@Nullable BeanDefinitionRegistry registry) {
return Optional.ofNullable(registry) return Optional.ofNullable(registry)
.filter(it -> it instanceof ConfigurableBeanFactory) .filter(ConfigurableBeanFactory.class::isInstance)
.map(it -> ((ConfigurableBeanFactory) it).getBeanClassLoader()) .map(ConfigurableBeanFactory.class::cast)
.orElseGet(() -> Thread.currentThread().getContextClassLoader()); .map(ConfigurableBeanFactory::getBeanClassLoader)
.orElseGet(CURRENT_THREAD_CONTEXT_CLASS_LOADER);
}
/**
* Attempts to resolve the {@link ClassLoader} used by the {@link ConfigurableBeanFactory}
* to load {@link Class} definitions of the beans created by the factory.
*
* @param beanFactory {@link ConfigurableBeanFactory} from which to resolve the {@link ClassLoader}.
* @return the resolved {@link ClassLoader} from the {@link ConfigurableBeanFactory}
* or the {@link Thread#currentThread() current Thread's} {@link Thread#getContextClassLoader() context ClassLoader}.
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader()
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
* @see java.lang.Thread#getContextClassLoader()
* @see java.lang.Thread#currentThread()
*/
protected @NonNull ClassLoader resolveBeanClassLoader(@Nullable ConfigurableBeanFactory beanFactory) {
return Optional.ofNullable(beanFactory)
.map(ConfigurableBeanFactory::getBeanClassLoader)
.orElseGet(CURRENT_THREAD_CONTEXT_CLASS_LOADER);
} }
/** /**
@@ -946,15 +998,20 @@ public abstract class AbstractAnnotationConfigSupport
*/ */
protected Optional<String> resolveBeanClassName(@Nullable BeanDefinition beanDefinition) { protected Optional<String> resolveBeanClassName(@Nullable BeanDefinition beanDefinition) {
Optional<String> beanClassName = Optional.ofNullable(beanDefinition) Optional<BeanDefinition> optionalBeanDefinition = Optional.ofNullable(beanDefinition);
Optional<String> beanClassName = optionalBeanDefinition
.map(BeanDefinition::getBeanClassName) .map(BeanDefinition::getBeanClassName)
.filter(StringUtils::hasText); .filter(StringUtils::hasText);
if (!beanClassName.isPresent()) { boolean beanClassNameNotPresent = beanClassName.isEmpty();
beanClassName = Optional.ofNullable(beanDefinition)
if (beanClassNameNotPresent) {
beanClassName = optionalBeanDefinition
.filter(AnnotatedBeanDefinition.class::isInstance) .filter(AnnotatedBeanDefinition.class::isInstance)
.filter(it -> StringUtils.hasText(it.getFactoryMethodName())) .filter(it -> StringUtils.hasText(it.getFactoryMethodName()))
.map(it -> ((AnnotatedBeanDefinition) it).getFactoryMethodMetadata()) .map(AnnotatedBeanDefinition.class::cast)
.map(AnnotatedBeanDefinition::getFactoryMethodMetadata)
.map(MethodMetadata::getReturnTypeName); .map(MethodMetadata::getReturnTypeName);
} }
@@ -1084,17 +1141,19 @@ public abstract class AbstractAnnotationConfigSupport
} }
/** /**
* Safely resolves a {@link Class type} returned by the supplied {@link TypeResolver} where the {@link Class type} * Safely resolves a {@link Class type} returned by the given {@link TypeResolver} where the {@link Class type}
* resolution might result in a {@link ClassNotFoundException}, or possibly, {@link NoClassDefFoundError}. * resolution might result in a {@link ClassNotFoundException} or {@link NoClassDefFoundError}.
* *
* @param <T> {@link Class} of the type being resolved. * @param <T> {@link Class} of the type being resolved.
* @param typeResolver {@link TypeResolver} used to resolve a specific {@link Class type}. * @param typeResolver {@link TypeResolver} used to resolve a specific {@link Class type}.
* @return the resolved {@link Class type} or {@literal null} if the {@link Class type} returned by * @return the resolved {@link Class type} or {@literal null} if the {@link Class type} returned by
* the {@link TypeResolver} could not be resolved. * the {@link TypeResolver} could not be resolved.
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport.TypeResolver * @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport.TypeResolver
* @see java.lang.ClassNotFoundException
* @see java.lang.NoClassDefFoundError
* @see java.lang.Class * @see java.lang.Class
*/ */
protected <T> Class<T> safeResolveType(TypeResolver<T> typeResolver) { protected @Nullable <T> Class<T> safeResolveType(@NonNull TypeResolver<T> typeResolver) {
try { try {
return typeResolver.resolve(); return typeResolver.resolve();
@@ -1109,7 +1168,7 @@ public abstract class AbstractAnnotationConfigSupport
* used to resolve a particular {@link Class type}. * used to resolve a particular {@link Class type}.
* *
* Implementations are free to decide on how a {@link Class type} gets resolved, such as * Implementations are free to decide on how a {@link Class type} gets resolved, such as
* with {@link Class#forName(String)} or perhaps by using {@link ClassLoader#defineClass(String, byte[], int, int)}. * with {@link Class#forName(String)} or by using {@literal ClassLoader#defineClass(String, byte[], int, int)}.
* *
* @param <T> {@link Class} of the type to resolve. * @param <T> {@link Class} of the type to resolve.
*/ */

View File

@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -37,6 +38,8 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.GemFireCache;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@@ -63,7 +66,7 @@ import org.springframework.data.gemfire.test.model.Person;
* @since 1.1.0 * @since 1.1.0
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class AbstractAnnotationConfigSupportTests { public class AbstractAnnotationConfigSupportUnitTests {
private AbstractAnnotationConfigSupport support; private AbstractAnnotationConfigSupport support;
@@ -76,14 +79,38 @@ public class AbstractAnnotationConfigSupportTests {
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class, beanClass.getSimpleName()); AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class, beanClass.getSimpleName());
when(mockBeanDefinition.getBeanClassName()).thenReturn(beanClass.getName()); doReturn(beanClass.getName()).when(mockBeanDefinition).getBeanClassName();
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION); doReturn(BeanDefinition.ROLE_APPLICATION).when(mockBeanDefinition).getRole();
return mockBeanDefinition; return mockBeanDefinition;
} }
@Test @Test
public void isNotInfrastructureBeanIsTrue() { public void isNotInfrastructureBeanWithApacheGeodeClassIsTrue() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(GemFireCache.class);
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isTrue();
verify(mockBeanDefinition, times(1)).getBeanClassName();
verify(mockBeanDefinition, times(1)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
}
@Test
public void isNotInfrastructureBeanWithObjectClassIsTrue() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class);
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isTrue();
verify(mockBeanDefinition, times(1)).getBeanClassName();
verify(mockBeanDefinition, times(1)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
}
@Test
public void isNotInfrastructureBeanWithSpringDataGeodeGemfireTemplateClassIsTrue() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(GemfireTemplate.class); BeanDefinition mockBeanDefinition = mockBeanDefinition(GemfireTemplate.class);
@@ -91,10 +118,11 @@ public class AbstractAnnotationConfigSupportTests {
verify(mockBeanDefinition, times(1)).getBeanClassName(); verify(mockBeanDefinition, times(1)).getBeanClassName();
verify(mockBeanDefinition, times(1)).getRole(); verify(mockBeanDefinition, times(1)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
public void isNotInfrastructureBeanWithInfrastructureClassIsFalse() { public void isNotInfrastructureBeanWithSpringFrameworkInfrastructureClassIsFalse() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class); BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
@@ -102,6 +130,7 @@ public class AbstractAnnotationConfigSupportTests {
verify(mockBeanDefinition, times(1)).getBeanClassName(); verify(mockBeanDefinition, times(1)).getBeanClassName();
verify(mockBeanDefinition, times(1)).getRole(); verify(mockBeanDefinition, times(1)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
@@ -109,32 +138,35 @@ public class AbstractAnnotationConfigSupportTests {
BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class); BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class);
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE); doReturn(BeanDefinition.ROLE_INFRASTRUCTURE).when(mockBeanDefinition).getRole();
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isFalse(); assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isFalse();
verify(mockBeanDefinition, never()).getBeanClassName(); verify(mockBeanDefinition, never()).getBeanClassName();
verify(mockBeanDefinition, times(1)).getRole(); verify(mockBeanDefinition, times(1)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
public void isNotInfrastructureClassWithBeanDefinitionIsTrue() { public void isNotInfrastructureClassWithObjectBeanDefinitionIsTrue() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class); BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class);
assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isTrue(); assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isTrue();
verify(mockBeanDefinition, times(1)).getBeanClassName(); verify(mockBeanDefinition, times(1)).getBeanClassName();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
public void isNotInfrastructureClassWithBeanDefinitionIsFalse() { public void isNotInfrastructureClassWithSpringInfrastructureBeanDefinitionIsFalse() {
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class); BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isFalse(); assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isFalse();
verify(mockBeanDefinition, times(1)).getBeanClassName(); verify(mockBeanDefinition, times(1)).getBeanClassName();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
@@ -145,8 +177,7 @@ public class AbstractAnnotationConfigSupportTests {
@Test @Test
public void isNotInfrastructureClassIsFalse() { public void isNotInfrastructureClassIsFalse() {
assertThat(this.support.isNotInfrastructureClass("org.springframework.SomeType")).isFalse(); assertThat(this.support.isNotInfrastructureClass("org.springframework.SomeType")).isFalse();
assertThat(this.support.isNotInfrastructureClass("org.springframework.core.type.SomeType")) assertThat(this.support.isNotInfrastructureClass("org.springframework.core.type.SomeType")).isFalse();
.isFalse();
} }
@Test @Test
@@ -154,12 +185,13 @@ public class AbstractAnnotationConfigSupportTests {
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION).thenReturn(Integer.MAX_VALUE); doReturn(BeanDefinition.ROLE_APPLICATION).doReturn(Integer.MAX_VALUE).when(mockBeanDefinition).getRole();
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue(); assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue(); assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
verify(mockBeanDefinition, times(2)).getRole(); verify(mockBeanDefinition, times(2)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
@@ -167,13 +199,14 @@ public class AbstractAnnotationConfigSupportTests {
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class); BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE) doReturn(BeanDefinition.ROLE_INFRASTRUCTURE).doReturn(BeanDefinition.ROLE_SUPPORT)
.thenReturn(BeanDefinition.ROLE_SUPPORT); .when(mockBeanDefinition).getRole();
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse(); assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse(); assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
verify(mockBeanDefinition, times(2)).getRole(); verify(mockBeanDefinition, times(2)).getRole();
verifyNoMoreInteractions(mockBeanDefinition);
} }
@Test @Test
@@ -195,9 +228,9 @@ public class AbstractAnnotationConfigSupportTests {
@Test @Test
public void requirePropertyWithNonStringValueIsSuccessful() { public void requirePropertyWithNonStringValueIsSuccessful() {
when(support.resolveProperty(anyString(), eq(Integer.class), eq(null))).thenReturn(1); when(this.support.resolveProperty(anyString(), eq(Integer.class), eq(null))).thenReturn(1);
assertThat(support.requireProperty("key", Integer.class)).isEqualTo(1); assertThat(this.support.requireProperty("key", Integer.class)).isEqualTo(1);
verify(support, times(1)) verify(support, times(1))
.resolveProperty(eq("key"), eq(Integer.class), eq(null)); .resolveProperty(eq("key"), eq(Integer.class), eq(null));
@@ -206,21 +239,21 @@ public class AbstractAnnotationConfigSupportTests {
@Test @Test
public void requirePropertyWithStringValueIsSuccessful() { public void requirePropertyWithStringValueIsSuccessful() {
when(support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn("test"); when(this.support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn("test");
assertThat(support.requireProperty("key", String.class)).isEqualTo("test"); assertThat(this.support.requireProperty("key", String.class)).isEqualTo("test");
verify(support, times(1)) verify(this.support, times(1))
.resolveProperty(eq("key"), eq(String.class), eq(null)); .resolveProperty(eq("key"), eq(String.class), eq(null));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void requirePropertyWithEmptyStringThrowsIllegalArgumentException() { public void requirePropertyWithEmptyStringThrowsIllegalArgumentException() {
when(support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn(" "); when(this.support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn(" ");
try { try {
support.requireProperty("key", String.class); this.support.requireProperty("key", String.class);
} }
catch (IllegalArgumentException expected) { catch (IllegalArgumentException expected) {
@@ -230,7 +263,7 @@ public class AbstractAnnotationConfigSupportTests {
throw expected; throw expected;
} }
finally { finally {
verify(support, times(1)) verify(this.support, times(1))
.resolveProperty(eq("key"), eq(String.class), eq(null)); .resolveProperty(eq("key"), eq(String.class), eq(null));
} }
} }
@@ -238,10 +271,10 @@ public class AbstractAnnotationConfigSupportTests {
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void requirePropertyWithNullValueThrowsIllegalArgumentException() { public void requirePropertyWithNullValueThrowsIllegalArgumentException() {
when(support.resolveProperty(anyString(), any(), eq(null))).thenReturn(null); when(this.support.resolveProperty(anyString(), any(), eq(null))).thenReturn(null);
try { try {
support.requireProperty("key", Integer.class); this.support.requireProperty("key", Integer.class);
} }
catch (IllegalArgumentException expected) { catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Property [key] is required"); assertThat(expected).hasMessage("Property [key] is required");
@@ -250,7 +283,7 @@ public class AbstractAnnotationConfigSupportTests {
throw expected; throw expected;
} }
finally { finally {
verify(support, times(1)) verify(this.support, times(1))
.resolveProperty(eq("key"), eq(Integer.class), eq(null)); .resolveProperty(eq("key"), eq(Integer.class), eq(null));
} }
} }
@@ -378,7 +411,7 @@ public class AbstractAnnotationConfigSupportTests {
@Test @Test
public void resolveBeanClassLoaderIsNullSafe() { public void resolveBeanClassLoaderIsNullSafe() {
assertThat(this.support.resolveBeanClassLoader(null)) assertThat(this.support.resolveBeanClassLoader((BeanDefinitionRegistry) null))
.isEqualTo(Thread.currentThread().getContextClassLoader()); .isEqualTo(Thread.currentThread().getContextClassLoader());
} }