SGF-687 - Add support for configuring client and server Region data management policies in Caching-defined Regions.
This improvement allows users to configure both the client and server Region data management policies using the new clientRegionShortcut and serverRegionShortcut attributes on the EnableCachingDefinedRegions annotation, which default to o.a.g.cache.client.ClientRegionShortcut#PROXY and o.a.g.cache.RegionShortcut#PARTITION, respectively. Additionally, a user may configure the name of a dedicated Pool to use for all caching-defined client Regions. Move all common Annotation-based configuration logic from CachingDefinedRegionsConfiguration to AbstractAnnotationConfigSupport.
This commit is contained in:
@@ -1572,11 +1572,11 @@ the following CQ...
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication(subcriptionEnabled = true);
|
||||
@ClientCacheApplication(subcriptionEnabled = true)
|
||||
@EnableContinuousQueries
|
||||
class PublisherPrintApplication {
|
||||
|
||||
@ContinuousQuery(name = "DemandExceedsSupply" query =
|
||||
@ContinuousQuery(name = "DemandExceedsSupply", query =
|
||||
"SELECT book.* FROM /Books book, /Inventory inventory
|
||||
WHERE book.title = 'How to crush it in the Book business like Amazon"
|
||||
AND inventory.isbn = book.isbn
|
||||
|
||||
@@ -517,7 +517,8 @@ _Spring Data GemFire_ provides an implementation of the _Spring_
|
||||
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache[Cache Abstraction]
|
||||
to position Pivotal GemFire as a _caching provider_ in Spring's caching infrastructure.
|
||||
|
||||
To use Pivotal GemFire as a backing implementation, simply add `GemfireCacheManager` to your configuration:
|
||||
To use Pivotal GemFire as a backing implementation, a "_caching provider_" _in Spring's Cache Abstraction_,
|
||||
simply add `GemfireCacheManager` to your configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -543,7 +544,7 @@ To use Pivotal GemFire as a backing implementation, simply add `GemfireCacheMana
|
||||
----
|
||||
|
||||
NOTE: The `cache-ref` attribute on the `CacheManager` bean definition is not necessary if the default cache bean name
|
||||
is used (i.e. "gemfireCache"), that is, `<gfe:cache>` without an explicit ID.
|
||||
is used (i.e. "gemfireCache"), i.e. `<gfe:cache>` without an explicit ID.
|
||||
|
||||
When the `GemfireCacheManager` (Singleton) bean instance is declared and declarative caching is enabled
|
||||
(either in XML with `<cache:annotation-driven/>` or in JavaConfig with _Spring's_ `@EnableCaching` annotation),
|
||||
@@ -553,7 +554,7 @@ using GemFire Regions.
|
||||
These caches (i.e. Regions) must exist before the caching annotations that use them otherwise an error will occur.
|
||||
|
||||
By way of example, suppose you have a Customer Service application with a `CustomerService` application component
|
||||
that does caching...
|
||||
that performs caching...
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -588,7 +589,7 @@ XML:
|
||||
|
||||
<gfe:cache/>
|
||||
|
||||
<gfe:partitioned-region id="accontsRegion" name="Accounts" persistent="true" ...>
|
||||
<gfe:partitioned-region id="accountsRegion" name="Accounts" persistent="true" ...>
|
||||
...
|
||||
</gfe:partitioned-region>
|
||||
</beans>
|
||||
@@ -609,7 +610,9 @@ class ApplicationConfiguration {
|
||||
|
||||
@Bean
|
||||
GemfireCacheManager cacheManager() {
|
||||
return new GemfireCacheManager(gemfireCache());
|
||||
GemfireCacheManager cacheManager = GemfireCacheManager();
|
||||
cacheManager.setCache(gemfireCache());
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean("Accounts")
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.data.gemfire.config.annotation;
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
@@ -36,13 +35,13 @@ import java.util.stream.Stream;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.springframework.beans.BeanInstantiationException;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
@@ -53,16 +52,16 @@ import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
|
||||
import org.springframework.data.gemfire.config.annotation.support.BeanDefinitionRegistryPostProcessorSupport;
|
||||
import org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -72,10 +71,18 @@ import org.springframework.util.StringUtils;
|
||||
* service classes and methods.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see java.lang.reflect.AnnotatedElement
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor
|
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||
* @see org.springframework.beans.factory.support.AbstractBeanDefinition
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
|
||||
@@ -86,15 +93,17 @@ import org.springframework.util.StringUtils;
|
||||
* @see org.springframework.cache.annotation.Caching
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.ImportAware
|
||||
* @see org.springframework.core.annotation.AnnotatedElementUtils
|
||||
* @see org.springframework.core.annotation.AnnotationUtils
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.BeanDefinitionRegistryPostProcessorSupport
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class CachingDefinedRegionsConfiguration {
|
||||
public class CachingDefinedRegionsConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
|
||||
|
||||
private static final Class[] CLASS_CACHE_ANNOTATION_TYPES;
|
||||
|
||||
@@ -112,11 +121,155 @@ public class CachingDefinedRegionsConfiguration {
|
||||
|
||||
}
|
||||
|
||||
private static final Set<Integer> INFRASTRUCTURE_ROLES =
|
||||
asSet(BeanDefinition.ROLE_INFRASTRUCTURE, BeanDefinition.ROLE_SUPPORT);
|
||||
private ClientRegionShortcut clientRegionShortcut = ClientRegionShortcut.PROXY;
|
||||
|
||||
private static final String ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE = "org.springframework.data.gemfire";
|
||||
private static final String ORG_SPRINGFRAMEWORK_PACKAGE = "org.springframework";
|
||||
private RegionShortcut serverRegionShortcut = RegionShortcut.PARTITION;
|
||||
|
||||
private String poolName = ClientRegionFactoryBean.DEFAULT_POOL_NAME;
|
||||
|
||||
/**
|
||||
* Returns the {@link Annotation} {@link Class type} that configures and creates {@link Region Regions}
|
||||
* for application service {@link Method Methods} that are annotated with Spring's Cache Abstraction Annotations.
|
||||
*
|
||||
* @return the {@link Annotation} {@link Class type} that configures and creates {@link Region Regions}
|
||||
* for application service {@link Method Methods} that are annotated with Spring's Cache Abstraction Annotations.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotationType() {
|
||||
return EnableCachingDefinedRegions.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ClientRegionShortcut} specifying the data management policy to use
|
||||
* when creating a client {@link Region}.
|
||||
*
|
||||
* @param clientRegionShortcut {@link ClientRegionShortcut} specifying the data management policy
|
||||
* to use when creating a client {@link Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
*/
|
||||
public void setClientRegionShortcut(ClientRegionShortcut clientRegionShortcut) {
|
||||
this.clientRegionShortcut = clientRegionShortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured {@link ClientRegionShortcut} specifying the data management policy to use
|
||||
* when creating a client {@link Region}.
|
||||
*
|
||||
* @return an {@link Optional} {@link ClientRegionShortcut} specifying the data management policy to use
|
||||
* when creating a client {@link Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see #setClientRegionShortcut(ClientRegionShortcut)
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
protected Optional<ClientRegionShortcut> getClientRegionShortcut() {
|
||||
return Optional.ofNullable(this.clientRegionShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link ClientRegionShortcut} specifying the data management policy to use
|
||||
* when creating a client {@link Region}; defaults to {@link ClientRegionShortcut#PROXY}.
|
||||
*
|
||||
* @return the resolved {@link ClientRegionShortcut} specifying the data management policy to use
|
||||
* when creating a client {@link Region}; defaults to {@link ClientRegionShortcut#PROXY}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see #getClientRegionShortcut()
|
||||
*/
|
||||
protected ClientRegionShortcut resolveClientRegionShortcut() {
|
||||
return getClientRegionShortcut().orElse(ClientRegionShortcut.PROXY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the name of the dedicated {@link Pool} used by all caching-defined client {@link Region Regions}
|
||||
* to send and receive data between the client and server.
|
||||
*
|
||||
* @param poolName {@link String} containing the name of the dedicated {@link Pool} for all
|
||||
* caching-defined client {@link Region Regions}.
|
||||
*/
|
||||
public void setPoolName(String poolName) {
|
||||
this.poolName = poolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the dedicated {@link Pool} used by all caching-defined client {@link Region Regions}
|
||||
* to send and receive data between the client and server.
|
||||
*
|
||||
* @return an {@link Optional} {@link String name} of the dedicated {@link Pool} used by all caching-defined
|
||||
* client {@link Region Regions}.
|
||||
* @see #setPoolName(String)
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
protected Optional<String> getPoolName() {
|
||||
return Optional.ofNullable(this.poolName).filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the name of the dedicated {@link Pool} used by all caching-defined client {@link Region Regions}
|
||||
* to send and receive data between the client and server; defaults to {@literal DEFAULT}.
|
||||
*
|
||||
* @return the {@link String name} of the dedicated {@link Pool} used by all caching-defined
|
||||
* client {@link Region Regions}; defaults to {@literal DEFAULT}.
|
||||
* @see #getPoolName()
|
||||
*/
|
||||
protected String resolvePoolName() {
|
||||
return getPoolName().orElse(ClientRegionFactoryBean.DEFAULT_POOL_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}.
|
||||
*
|
||||
* @param serverRegionShortcut {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}.
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
*/
|
||||
public void setServerRegionShortcut(RegionShortcut serverRegionShortcut) {
|
||||
this.serverRegionShortcut = serverRegionShortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}.
|
||||
*
|
||||
* @return an {@link Optional} {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}.
|
||||
* @see #setServerRegionShortcut(RegionShortcut)
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
protected Optional<RegionShortcut> getServerRegionShortcut() {
|
||||
return Optional.ofNullable(this.serverRegionShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}; defaults to {@link RegionShortcut#PARTITION}.
|
||||
*
|
||||
* @return the resolved {@link RegionShortcut} specifying the data management policy to use
|
||||
* when creating a server (peer) {@link Region}; defaults to {@link RegionShortcut#PARTITION}.
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see #getServerRegionShortcut()
|
||||
*/
|
||||
protected RegionShortcut resolveServerRegionShortcut() {
|
||||
return getServerRegionShortcut().orElse(RegionShortcut.PARTITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
|
||||
if (isAnnotationPresent(importMetadata)) {
|
||||
|
||||
AnnotationAttributes enableCachingDefinedRegionsAttributes = getAnnotationAttributes(importMetadata);
|
||||
|
||||
setClientRegionShortcut(enableCachingDefinedRegionsAttributes.getEnum("clientRegionShortcut"));
|
||||
|
||||
setPoolName(enableCachingDefinedRegionsAttributes.getString("poolName"));
|
||||
|
||||
setServerRegionShortcut(enableCachingDefinedRegionsAttributes.getEnum("serverRegionShortcut"));
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
@@ -162,35 +315,6 @@ public class CachingDefinedRegionsConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotInfrastructureBean(Object bean) {
|
||||
return isNotInfrastructureClass(bean.getClass().getName());
|
||||
}
|
||||
|
||||
boolean isNotInfrastructureBean(BeanDefinition beanDefinition) {
|
||||
return (isNotInfrastructureRole(beanDefinition) && isNotInfrastructureClass(beanDefinition));
|
||||
}
|
||||
|
||||
boolean isNotInfrastructureClass(BeanDefinition beanDefinition) {
|
||||
return resolveBeanClassName(beanDefinition).filter(this::isNotInfrastructureClass).isPresent();
|
||||
}
|
||||
|
||||
boolean isNotInfrastructureClass(String className) {
|
||||
return (className.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE)
|
||||
|| !className.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE));
|
||||
}
|
||||
|
||||
boolean isNotInfrastructureRole(BeanDefinition beanDefinition) {
|
||||
return !INFRASTRUCTURE_ROLES.contains(beanDefinition.getRole());
|
||||
}
|
||||
|
||||
boolean isUserLevelMethod(Method method) {
|
||||
|
||||
return Optional.ofNullable(method)
|
||||
.filter(ClassUtils::isUserLevelMethod)
|
||||
.filter(it -> !Object.class.equals(it.getDeclaringClass()))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<String> collectCacheNames(Class<?> type) {
|
||||
|
||||
@@ -200,7 +324,6 @@ public class CachingDefinedRegionsConfiguration {
|
||||
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));
|
||||
@@ -224,8 +347,8 @@ public class CachingDefinedRegionsConfiguration {
|
||||
private Set<String> collectCacheNames(Annotation annotation) {
|
||||
|
||||
return Optional.ofNullable(annotation)
|
||||
.map(AnnotationUtils::getAnnotationAttributes)
|
||||
.map(annotationAttributes -> (String[]) annotationAttributes.get("cacheNames"))
|
||||
.map(this::getAnnotationAttributes)
|
||||
.map(annotationAttributes -> annotationAttributes.getStringArray("cacheNames"))
|
||||
.map(CollectionUtils::asSet)
|
||||
.orElse(Collections.emptySet());
|
||||
}
|
||||
@@ -242,11 +365,11 @@ public class CachingDefinedRegionsConfiguration {
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
cacheNames.addAll(stream(nullSafeArray(caching.evict(), CacheEvict.class))
|
||||
.flatMap(cacheable -> collectCacheNames(cacheable).stream())
|
||||
.flatMap(cacheEvict -> collectCacheNames(cacheEvict).stream())
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
cacheNames.addAll(stream(nullSafeArray(caching.put(), CachePut.class))
|
||||
.flatMap(cacheable -> collectCacheNames(cacheable).stream())
|
||||
.flatMap(cachePut -> collectCacheNames(cachePut).stream())
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
});
|
||||
@@ -257,9 +380,6 @@ public class CachingDefinedRegionsConfiguration {
|
||||
private BeanDefinitionRegistry registerRegionBeanDefinitions(Set<String> cacheNames,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
boolean containsGemFirePoolBeanDefinition =
|
||||
registry.containsBeanDefinition(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
|
||||
|
||||
cacheNames.forEach(cacheName -> {
|
||||
|
||||
if (!registry.containsBeanDefinition(cacheName)) {
|
||||
@@ -268,13 +388,10 @@ public class CachingDefinedRegionsConfiguration {
|
||||
BeanDefinitionBuilder.genericBeanDefinition(GemFireCacheTypeAwareRegionFactoryBean.class);
|
||||
|
||||
builder.addPropertyReference("cache", GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
|
||||
|
||||
if (!containsGemFirePoolBeanDefinition) {
|
||||
builder.addPropertyValue("poolName", GemfireUtils.DEFAULT_POOL_NAME);
|
||||
}
|
||||
|
||||
builder.addPropertyValue("clientRegionShortcut", resolveClientRegionShortcut());
|
||||
builder.addPropertyValue("poolName", resolvePoolName());
|
||||
builder.addPropertyValue("regionName", cacheName);
|
||||
builder.addPropertyValue("serverRegionShortcut", RegionShortcut.PARTITION);
|
||||
builder.addPropertyValue("serverRegionShortcut", resolveServerRegionShortcut());
|
||||
|
||||
registry.registerBeanDefinition(cacheName, builder.getBeanDefinition());
|
||||
}
|
||||
@@ -285,8 +402,6 @@ public class CachingDefinedRegionsConfiguration {
|
||||
|
||||
private ConfigurableBeanFactory registerRegionBeans(Set<String> cacheNames, ConfigurableBeanFactory beanFactory) {
|
||||
|
||||
boolean containsGemFirePoolBean = beanFactory.containsBean(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
|
||||
|
||||
cacheNames.forEach(cacheName -> {
|
||||
|
||||
if (!beanFactory.containsBean(cacheName)) {
|
||||
@@ -295,14 +410,14 @@ public class CachingDefinedRegionsConfiguration {
|
||||
GemFireCacheTypeAwareRegionFactoryBean<?, ?> regionFactoryBean =
|
||||
new GemFireCacheTypeAwareRegionFactoryBean<>();
|
||||
|
||||
regionFactoryBean.setCache(beanFactory.getBean("gemfireCache", GemFireCache.class));
|
||||
|
||||
if (!containsGemFirePoolBean) {
|
||||
regionFactoryBean.setPoolName(GemfireUtils.DEFAULT_POOL_NAME);
|
||||
}
|
||||
GemFireCache gemfireCache =
|
||||
beanFactory.getBean(GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME, GemFireCache.class);
|
||||
|
||||
regionFactoryBean.setCache(gemfireCache);
|
||||
regionFactoryBean.setClientRegionShortcut(resolveClientRegionShortcut());
|
||||
regionFactoryBean.setPoolName(resolvePoolName());
|
||||
regionFactoryBean.setRegionName(cacheName);
|
||||
regionFactoryBean.setServerRegionShortcut(RegionShortcut.PARTITION);
|
||||
regionFactoryBean.setServerRegionShortcut(resolveServerRegionShortcut());
|
||||
regionFactoryBean.afterPropertiesSet();
|
||||
|
||||
Optional.ofNullable(regionFactoryBean.getObject())
|
||||
@@ -317,70 +432,4 @@ public class CachingDefinedRegionsConfiguration {
|
||||
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
<A extends Annotation> A resolveAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
|
||||
return (annotatedElement instanceof Class
|
||||
? AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType)
|
||||
: AnnotationUtils.findAnnotation(annotatedElement, annotationType));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
|
||||
return resolveBeanClass(beanDefinition, resolveBeanClassLoader(registry));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, ClassLoader classLoader) {
|
||||
|
||||
Class<?> beanClass = (beanDefinition instanceof AbstractBeanDefinition
|
||||
? safeResolveType(() -> ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(classLoader)) : null);
|
||||
|
||||
if (beanClass == null) {
|
||||
beanClass = resolveBeanClassName(beanDefinition).map(beanClassName ->
|
||||
safeResolveType(() -> ClassUtils.forName(beanClassName, classLoader))).orElse(null);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(beanClass);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
ClassLoader resolveBeanClassLoader(BeanDefinitionRegistry registry) {
|
||||
|
||||
return (registry instanceof ConfigurableBeanFactory
|
||||
? ((ConfigurableBeanFactory) registry).getBeanClassLoader()
|
||||
: Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Optional<String> resolveBeanClassName(BeanDefinition beanDefinition) {
|
||||
|
||||
Optional<String> beanClassName =
|
||||
Optional.ofNullable(beanDefinition.getBeanClassName()).filter(StringUtils::hasText);
|
||||
|
||||
if (!beanClassName.isPresent()) {
|
||||
beanClassName = Optional.of(beanDefinition)
|
||||
.filter(it -> StringUtils.hasText(it.getFactoryMethodName()))
|
||||
.filter(it -> it instanceof AnnotatedBeanDefinition)
|
||||
.map(it -> ((AnnotatedBeanDefinition) it).getFactoryMethodMetadata())
|
||||
.map(MethodMetadata::getReturnTypeName);
|
||||
}
|
||||
|
||||
return beanClassName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Class<?> safeResolveType(TypeResolver typeResolver) {
|
||||
try {
|
||||
return typeResolver.resolve();
|
||||
}
|
||||
catch (ClassNotFoundException cause) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface TypeResolver {
|
||||
Class<?> resolve() throws ClassNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,18 +24,23 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.mapping.annotation.ClientRegion;
|
||||
|
||||
/**
|
||||
* The {@link EnableCachingDefinedRegions} annotation marks a Spring {@link Configuration @Configuration} application
|
||||
* annotated class to enable the creation of GemFire/Geode {@link Region Regions} based on Spring's Cache Abstraction
|
||||
* Annotations applied to application service methods and types.
|
||||
*
|
||||
* Additionally, this annotation also enable Spring's Cache Abstraction with SDG's {@link EnableGemfireCaching},
|
||||
* which declares Spring's {@link org.springframework.cache.annotation.EnableCaching} annotation as well as
|
||||
* declares the SDG {@link org.springframework.data.gemfire.cache.GemfireCacheManager} bean definition.
|
||||
* Additionally, this annotation enables Spring's Cache Abstraction with SDG's {@link EnableGemfireCaching} annotation,
|
||||
* which declares Spring's {@link org.springframework.cache.annotation.EnableCaching} annotation as well as declares
|
||||
* the SDG {@link org.springframework.data.gemfire.cache.GemfireCacheManager} bean definition.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.annotation.Documented
|
||||
@@ -43,9 +48,13 @@ import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
|
||||
* @see java.lang.annotation.Retention
|
||||
* @see java.lang.annotation.Target
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see org.springframework.data.gemfire.cache.config.EnableGemfireCaching
|
||||
* @see org.springframework.data.gemfire.config.annotation.CachingDefinedRegionsConfiguration
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@@ -54,6 +63,37 @@ import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
|
||||
@Documented
|
||||
@EnableGemfireCaching
|
||||
@Import(CachingDefinedRegionsConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface EnableCachingDefinedRegions {
|
||||
|
||||
/**
|
||||
* When this annotation is applied to a cache client application, the {@literal clientRegionShortcut} attribute
|
||||
* specifies the data management policy applied to client {@link Region Regions} where persistent entities are
|
||||
* only annotated with the generic {@link org.springframework.data.gemfire.mapping.annotation.Region}
|
||||
* mapping annotation, or the non-data policy specific mapping annotation.
|
||||
*
|
||||
* Defaults to {@link ClientRegionShortcut#PROXY}.
|
||||
*/
|
||||
ClientRegionShortcut clientRegionShortcut() default ClientRegionShortcut.PROXY;
|
||||
|
||||
/**
|
||||
* When this annotation is applied to a cache client application, the {@literal poolName} attribute refers to
|
||||
* the default name of the GemFire/Geode {@link Pool} assigned to client {@link Region Region(s)}.
|
||||
*
|
||||
* This value can be overridden by annotating entities with the {@link ClientRegion} annotation.
|
||||
*
|
||||
* Defaults to {@literal DEFAULT}.
|
||||
*/
|
||||
String poolName() default ClientRegionFactoryBean.DEFAULT_POOL_NAME;
|
||||
|
||||
/**
|
||||
* When this annotation is applied to a peer cache application, the {@literal serverRegionShortcut} attribute
|
||||
* specifies the data management policy applied to server {@link Region Regions} where persistent entities are
|
||||
* only annotated with the generic {@link org.springframework.data.gemfire.mapping.annotation.Region}
|
||||
* mapping annotation, or the non-data policy specific mapping annotation.
|
||||
*
|
||||
* Defaults to {@link RegionShortcut#PARTITION}.
|
||||
*/
|
||||
RegionShortcut serverRegionShortcut() default RegionShortcut.PARTITION;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,14 +17,18 @@
|
||||
|
||||
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.newIllegalStateException;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -33,6 +37,8 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
@@ -41,14 +47,17 @@ import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.expression.BeanFactoryAccessor;
|
||||
import org.springframework.context.expression.EnvironmentAccessor;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -75,6 +84,11 @@ import org.springframework.util.StringUtils;
|
||||
public abstract class AbstractAnnotationConfigSupport
|
||||
implements BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware {
|
||||
|
||||
protected static final Set<Integer> INFRASTRUCTURE_ROLES =
|
||||
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_PACKAGE = "org.springframework";
|
||||
protected static final String SPRING_DATA_GEMFIRE_PROPERTY_PREFIX = "spring.data.gemfire.";
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
@@ -87,17 +101,6 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
|
||||
private final Log log;
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link Object} has value. The {@link Object} is valuable
|
||||
* if it is not {@literal null}.
|
||||
*
|
||||
* @param value {@link Object} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@link Object} has value.
|
||||
*/
|
||||
protected static boolean hasValue(Object value) {
|
||||
return Optional.ofNullable(value).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link Number} has value. The {@link Number} is valuable
|
||||
* if it is not {@literal null} and is not equal to 0.0d.
|
||||
@@ -109,6 +112,17 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
return Optional.ofNullable(value).filter(it -> it.doubleValue() != 0.0d).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link Object} has value. The {@link Object} is valuable
|
||||
* if it is not {@literal null}.
|
||||
*
|
||||
* @param value {@link Object} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@link Object} has value.
|
||||
*/
|
||||
protected static boolean hasValue(Object value) {
|
||||
return Optional.ofNullable(value).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link String} has value. The {@link String} is valuable
|
||||
* if it is not {@literal null} or empty.
|
||||
@@ -190,27 +204,71 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
return LogFactory.getLog(getClass());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Determines whether the given {@link AnnotationMetadata type meta-data} for a particular {@link Class}
|
||||
* is annotated with the declared {@link #getAnnotationTypeName()}.
|
||||
*
|
||||
* @param importingClassMetadata {@link AnnotationMetadata type meta-data} for a particular {@link Class}.
|
||||
* @return a boolean indicating whether the particular {@link Class} is annotated with
|
||||
* the declared {@link #getAnnotationTypeName()}.
|
||||
* @see #isAnnotationPresent(AnnotationMetadata, String)
|
||||
* @see #getAnnotationTypeName()
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) {
|
||||
return isAnnotationPresent(importingClassMetadata, getAnnotationTypeName());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Determines whether the given {@link AnnotationMetadata type meta-data} for a particular {@link Class}
|
||||
* is annotated with the given {@link Annotation} defined by {@link String name}.
|
||||
*
|
||||
* @param importingClassMetadata {@link AnnotationMetadata type meta-data} for a particular {@link Class}.
|
||||
* @param annotationName {@link String name} of the {@link Annotation} of interests.
|
||||
* @return a boolean indicating whether the particular {@link Class} is annotated with
|
||||
* the given {@link Annotation} defined by {@link String name}.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, String annotationName) {
|
||||
return importingClassMetadata.hasAnnotation(annotationName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Returns the {@link AnnotationAttributes} for the given {@link Annotation}.
|
||||
*
|
||||
* @param annotation {@link Annotation} to get the {@link AnnotationAttributes} for.
|
||||
* @return the {@link AnnotationAttributes} for the given {@link Annotation}.
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see java.lang.annotation.Annotation
|
||||
*/
|
||||
protected AnnotationAttributes getAnnotationAttributes(Annotation annotation) {
|
||||
return AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(annotation));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Returns {@link AnnotationAttributes} for the declared {@link #getAnnotationTypeName()}.
|
||||
*
|
||||
* @param importingClassMetadata {@link AnnotationMetadata type meta-data} for a particular {@link Class}.
|
||||
* @return {@link AnnotationAttributes} for the declared {@link #getAnnotationTypeName()}.
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see #getAnnotationAttributes(AnnotationMetadata, String)
|
||||
* @see #getAnnotationTypeName()
|
||||
*/
|
||||
protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) {
|
||||
return getAnnotationAttributes(importingClassMetadata, getAnnotationTypeName());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Returns {@link AnnotationAttributes} for the given {@link String named} {@link Annotation} from the given
|
||||
* {@link AnnotationMetadata type meta-data}.
|
||||
*
|
||||
* @param importingClassMetadata {@link AnnotationMetadata type meta-data} for a particular {@link Class}.
|
||||
* @param annotationName {@link String name} of the {@link Annotation} of interests.
|
||||
* @return {@link AnnotationAttributes} for the given {@link String named} {@link Annotation}.
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata,
|
||||
String annotationName) {
|
||||
|
||||
@@ -250,6 +308,104 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
return getAnnotationType().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe method to determine whether the given {@link Object bean} is a Spring container provided
|
||||
* infrastructure bean.
|
||||
*
|
||||
* @param bean {@link Object} to evaluate.
|
||||
* @return {@literal true} iff the {@link Object bean} is not a Spring container infrastructure bean.
|
||||
* @see #isNotInfrastructureClass(String)
|
||||
*/
|
||||
protected boolean isNotInfrastructureBean(Object bean) {
|
||||
|
||||
return Optional.ofNullable(bean)
|
||||
.map(Object::getClass)
|
||||
.map(Class::getName)
|
||||
.filter(this::isNotInfrastructureClass).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe method to determine whether the bean defined in the given {@link BeanDefinition}
|
||||
* is a Spring container provided infrastructure bean.
|
||||
*
|
||||
* @param beanDefinition {@link BeanDefinition} to evaluate.
|
||||
* @return {@literal true} iff the bean defined in the given {@link BeanDefinition} is not a Spring container
|
||||
* infrastructure bean.
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see #isNotInfrastructureClass(BeanDefinition)
|
||||
* @see #isNotInfrastructureRole(BeanDefinition)
|
||||
*/
|
||||
protected boolean isNotInfrastructureBean(BeanDefinition beanDefinition) {
|
||||
return (isNotInfrastructureRole(beanDefinition) && isNotInfrastructureClass(beanDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe method to determine whether the bean defined in the given {@link BeanDefinition}
|
||||
* is a Spring container infrastructure bean based on the bean's {@link Class#getName() class type}.
|
||||
*
|
||||
* @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
|
||||
* infrastructure bean.
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see #resolveBeanClassName(BeanDefinition)
|
||||
* @see #isNotInfrastructureClass(String)
|
||||
*/
|
||||
protected boolean isNotInfrastructureClass(BeanDefinition beanDefinition) {
|
||||
return resolveBeanClassName(beanDefinition).filter(this::isNotInfrastructureClass).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link Class#getName() class type name} is considered a Spring container
|
||||
* infrastructure type.
|
||||
*
|
||||
* The class type name is considered a Spring container infrastructure type if the package name begins with
|
||||
* 'org.springframework', excluding 'org.springframework.data.gemfire'.
|
||||
*
|
||||
* @param className {@link String} containing the name of the class type to evaluate.
|
||||
* @return {@literal true} iff the given {@link Class#getName() class type name} is not considered a
|
||||
* Spring container infrastructure type.
|
||||
*/
|
||||
protected boolean isNotInfrastructureClass(String className) {
|
||||
return (className.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE)
|
||||
|| !className.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe method to determines whether the bean defined by the given {@link BeanDefinition}
|
||||
* is a Spring container infrastructure bean based on the bean's role.
|
||||
*
|
||||
* @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
|
||||
* infrastructure bean.
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
*/
|
||||
protected boolean isNotInfrastructureRole(BeanDefinition beanDefinition) {
|
||||
|
||||
return Optional.ofNullable(beanDefinition)
|
||||
.map(BeanDefinition::getRole)
|
||||
.filter(role -> !INFRASTRUCTURE_ROLES.contains(role))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link Method} was declared and defined by the user.
|
||||
*
|
||||
* A {@link Method} is considered a user-level {@link Method} if the {@link Method} is not
|
||||
* an {@link Object} class method, is a {@link Method#isBridge() Bridge Method}
|
||||
* or is not {@link Method#isSynthetic()} nor a Groovy method.
|
||||
*
|
||||
* @param method {@link Method} to evaluate.
|
||||
* @return a boolean value indicating whether the {@link Method} was declared/defined by the user.
|
||||
* @see java.lang.reflect.Method
|
||||
*/
|
||||
protected boolean isUserLevelMethod(Method method) {
|
||||
|
||||
return Optional.ofNullable(method)
|
||||
.filter(ClassUtils::isUserLevelMethod)
|
||||
.filter(it -> !Object.class.equals(it.getDeclaringClass()))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -513,7 +669,7 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
/* (non-Javadoc) */
|
||||
protected String asArrayProperty(String propertyNamePrefix, int index, String propertyNameSuffix) {
|
||||
return String.format("%1$s[%2$d]%3$s", propertyNamePrefix, index,
|
||||
Optional.ofNullable(propertyNamePrefix).filter(StringUtils::hasText).map("."::concat).orElse(""));
|
||||
Optional.ofNullable(propertyNameSuffix).filter(StringUtils::hasText).map("."::concat).orElse(""));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@@ -669,6 +825,109 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
.orElseThrow(() -> newIllegalArgumentException("Property [%s] is required", propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link Annotation} with the given {@link Class type} from the {@link AnnotatedElement}.
|
||||
*
|
||||
* @param <A> {@link Class Subclass type} of the resolved {@link Annotation}.
|
||||
* @param annotatedElement {@link AnnotatedElement} from which to resolve the {@link Annotation}.
|
||||
* @param annotationType {@link Class type} of the {@link Annotation} to resolve from the {@link AnnotatedElement}.
|
||||
* @return the resolved {@link Annotation}.
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see java.lang.reflect.AnnotatedElement
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
protected <A extends Annotation> A resolveAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
|
||||
return (annotatedElement instanceof Class
|
||||
? AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType)
|
||||
: AnnotationUtils.findAnnotation(annotatedElement, annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link Class type} of the bean defined by the given {@link BeanDefinition}.
|
||||
*
|
||||
* @param beanDefinition {@link BeanDefinition} defining the bean from which the {@link Class type} is resolved.
|
||||
* @param registry {@link BeanDefinitionRegistry} used to resolve the {@link ClassLoader} used to resolve
|
||||
* the bean's {@link Class type}.
|
||||
* @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.support.BeanDefinitionRegistry
|
||||
* @see #resolveBeanClassLoader(BeanDefinitionRegistry)
|
||||
* @see #resolveBeanClass(BeanDefinition, ClassLoader)
|
||||
*/
|
||||
protected Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
|
||||
return resolveBeanClass(beanDefinition, resolveBeanClassLoader(registry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link Class type} of the bean defined by the given {@link BeanDefinition}.
|
||||
*
|
||||
* @param beanDefinition {@link BeanDefinition} defining the bean from which the {@link Class type} is resolved.
|
||||
* @param classLoader {@link ClassLoader} used to resolve the bean's {@link Class type}.
|
||||
* @return an {@link Optional} resolved {@link Class type} of the bean.
|
||||
* @see java.lang.ClassLoader
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#resolveBeanClass(ClassLoader)
|
||||
* @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
|
||||
* @see #resolveBeanClassName(BeanDefinition)
|
||||
*/
|
||||
private Optional<Class<?>> resolveBeanClass(BeanDefinition beanDefinition, ClassLoader classLoader) {
|
||||
|
||||
Class<?> beanClass = (beanDefinition instanceof AbstractBeanDefinition
|
||||
? safeResolveType(() -> ((AbstractBeanDefinition) beanDefinition).resolveBeanClass(classLoader)) : null);
|
||||
|
||||
if (beanClass == null) {
|
||||
beanClass = resolveBeanClassName(beanDefinition).map(beanClassName ->
|
||||
safeResolveType(() -> ClassUtils.forName(beanClassName, classLoader))).orElse(null);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the {@link ClassLoader} used by the {@link BeanDefinitionRegistry}
|
||||
* to load {@link Class} definitions of the beans defined in the registry.
|
||||
*
|
||||
* @param registry {@link BeanDefinitionRegistry} from which to resolve the {@link ClassLoader}.
|
||||
* @return the resolved {@link ClassLoader} from the {@link BeanDefinitionRegistry}
|
||||
* 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.support.BeanDefinitionRegistry
|
||||
* @see java.lang.Thread#currentThread()
|
||||
* @see java.lang.Thread#getContextClassLoader()
|
||||
*/
|
||||
protected ClassLoader resolveBeanClassLoader(BeanDefinitionRegistry registry) {
|
||||
|
||||
return Optional.ofNullable(registry)
|
||||
.filter(it -> it instanceof ConfigurableBeanFactory)
|
||||
.map(it -> ((ConfigurableBeanFactory) it).getBeanClassLoader())
|
||||
.orElseGet(() -> Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the class type name of the bean defined by the given {@link BeanDefinition}.
|
||||
*
|
||||
* @param beanDefinition {@link BeanDefinition} defining the bean from which to resolve the class type name.
|
||||
* @return an {@link Optional} {@link String} containing the resolved class type name of the bean defined
|
||||
* by the given {@link BeanDefinition}.
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition#getBeanClassName()
|
||||
*/
|
||||
protected Optional<String> resolveBeanClassName(BeanDefinition beanDefinition) {
|
||||
|
||||
Optional<String> beanClassName =
|
||||
Optional.ofNullable(beanDefinition).map(BeanDefinition::getBeanClassName).filter(StringUtils::hasText);
|
||||
|
||||
if (!beanClassName.isPresent()) {
|
||||
beanClassName = Optional.ofNullable(beanDefinition)
|
||||
.filter(it -> it instanceof AnnotatedBeanDefinition)
|
||||
.filter(it -> StringUtils.hasText(it.getFactoryMethodName()))
|
||||
.map(it -> ((AnnotatedBeanDefinition) it).getFactoryMethodMetadata())
|
||||
.map(MethodMetadata::getReturnTypeName);
|
||||
}
|
||||
|
||||
return beanClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the property with the given {@link String name} from the Spring {@link Environment}
|
||||
* as a {@link Boolean}.
|
||||
@@ -786,4 +1045,39 @@ public abstract class AbstractAnnotationConfigSupport
|
||||
targetType, defaultValue))
|
||||
.orElse(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely resolves a {@link Class type} returned by the supplied {@link TypeResolver} where the {@link Class type}
|
||||
* resolution might result in a {@link ClassNotFoundException}, or possibly, {@link NoClassDefFoundError}.
|
||||
*
|
||||
* @param <T> {@link Class} of the type being resolved.
|
||||
* @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
|
||||
* the {@link TypeResolver} could not be resolved.
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport.TypeResolver
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
protected <T> Class<T> safeResolveType(TypeResolver<T> typeResolver) {
|
||||
|
||||
try {
|
||||
return typeResolver.resolve();
|
||||
}
|
||||
catch (ClassNotFoundException cause) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TypeResolver} is a {@link FunctionalInterface} defining a contract to encapsulated the logic
|
||||
* to resolve a particular {@link Class type}.
|
||||
*
|
||||
* Implementations are free to decide on how a {@link Class type} gets resolved, such as
|
||||
* with {@link Class#forName(String)} or perhaps using {@link ClassLoader#defineClass(String, byte[], int, int)}.
|
||||
*
|
||||
* @param <T> {@link Class} of the type to resolve.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
protected interface TypeResolver<T> {
|
||||
Class<T> resolve() throws ClassNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +67,14 @@ public class EnableCachingDefinedRegionsIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void cacheRegionsExists() {
|
||||
|
||||
assertThat(gemfireCache.getRegion("/Example")).isNotNull();
|
||||
assertThat(gemfireCache.getRegion("/Echo")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void echoServiceOperationsAreSuccessful() {
|
||||
|
||||
assertThat(echoService.isCacheMiss()).isFalse();
|
||||
assertThat(echoService.echo("one")).isEqualTo("one");
|
||||
assertThat(echoService.isCacheMiss()).isTrue();
|
||||
@@ -97,7 +99,7 @@ public class EnableCachingDefinedRegionsIntegrationTests {
|
||||
|
||||
@Bean
|
||||
TestService testService() {
|
||||
return new DefaultTestService();
|
||||
return new CachingTestService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ public class EnableCachingDefinedRegionsIntegrationTests {
|
||||
Object testMethod(String key);
|
||||
}
|
||||
|
||||
static class DefaultTestService implements TestService {
|
||||
static class CachingTestService implements TestService {
|
||||
|
||||
@CachePut("Example")
|
||||
public Object testMethod(String key) {
|
||||
|
||||
@@ -25,33 +25,27 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.test.support.MapBuilder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -73,7 +67,12 @@ import org.springframework.stereotype.Service;
|
||||
public class EnableCachingDefinedRegionsUnitTests {
|
||||
|
||||
// Subject Under Test (SUT)
|
||||
private CachingDefinedRegionsConfiguration configuration = new CachingDefinedRegionsConfiguration();
|
||||
private CachingDefinedRegionsConfiguration configuration;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.configuration = new CachingDefinedRegionsConfiguration();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private BeanDefinition mockBeanDefinition(Class<?> beanClass) {
|
||||
@@ -112,6 +111,99 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
return mockBeanDefinitionRegistry;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setGetAndResolveClientRegionShortcut() {
|
||||
|
||||
assertThat(this.configuration.getClientRegionShortcut().orElse(null))
|
||||
.isEqualTo(ClientRegionShortcut.PROXY);
|
||||
assertThat(this.configuration.resolveClientRegionShortcut()).isEqualTo(ClientRegionShortcut.PROXY);
|
||||
|
||||
this.configuration.setClientRegionShortcut(ClientRegionShortcut.LOCAL);
|
||||
|
||||
assertThat(this.configuration.getClientRegionShortcut().orElse(null))
|
||||
.isEqualTo(ClientRegionShortcut.LOCAL);
|
||||
assertThat(this.configuration.resolveClientRegionShortcut()).isEqualTo(ClientRegionShortcut.LOCAL);
|
||||
|
||||
this.configuration.setClientRegionShortcut(null);
|
||||
|
||||
assertThat(this.configuration.getClientRegionShortcut().orElse(null)).isNull();
|
||||
assertThat(this.configuration.resolveClientRegionShortcut()).isEqualTo(ClientRegionShortcut.PROXY);
|
||||
|
||||
this.configuration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
|
||||
|
||||
assertThat(this.configuration.getClientRegionShortcut().orElse(null))
|
||||
.isEqualTo(ClientRegionShortcut.CACHING_PROXY);
|
||||
assertThat(this.configuration.resolveClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setGetAndResolvePoolName() {
|
||||
|
||||
assertThat(this.configuration.getPoolName().orElse(null))
|
||||
.isEqualTo(ClientRegionFactoryBean.DEFAULT_POOL_NAME);
|
||||
assertThat(this.configuration.resolvePoolName()).isEqualTo(ClientRegionFactoryBean.DEFAULT_POOL_NAME);
|
||||
|
||||
this.configuration.setPoolName(null);
|
||||
|
||||
assertThat(this.configuration.getPoolName().orElse(null)).isNull();
|
||||
assertThat(this.configuration.resolvePoolName()).isEqualTo(ClientRegionFactoryBean.DEFAULT_POOL_NAME);
|
||||
|
||||
this.configuration.setPoolName("TestPool");
|
||||
|
||||
assertThat(this.configuration.getPoolName().orElse(null)).isEqualTo("TestPool");
|
||||
assertThat(this.configuration.resolvePoolName()).isEqualTo("TestPool");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setGetAndResolveServerRegionShortcut() {
|
||||
|
||||
assertThat(this.configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.PARTITION);
|
||||
assertThat(this.configuration.resolveServerRegionShortcut()).isEqualTo(RegionShortcut.PARTITION);
|
||||
|
||||
this.configuration.setServerRegionShortcut(RegionShortcut.LOCAL);
|
||||
|
||||
assertThat(this.configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.LOCAL);
|
||||
assertThat(this.configuration.resolveServerRegionShortcut()).isEqualTo(RegionShortcut.LOCAL);
|
||||
|
||||
this.configuration.setServerRegionShortcut(null);
|
||||
|
||||
assertThat(this.configuration.getServerRegionShortcut().orElse(null)).isNull();
|
||||
assertThat(this.configuration.resolveServerRegionShortcut()).isEqualTo(RegionShortcut.PARTITION);
|
||||
|
||||
this.configuration.setServerRegionShortcut(RegionShortcut.REPLICATE);
|
||||
|
||||
assertThat(this.configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.REPLICATE);
|
||||
assertThat(this.configuration.resolveServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setImportMetadataConfiguresClientRegionShortcutPoolNameAndServerRegionShortcut() {
|
||||
|
||||
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
|
||||
|
||||
Map<String, Object> annotationAttributes = MapBuilder.<String, Object>newMapBuilder()
|
||||
.put("clientRegionShortcut", ClientRegionShortcut.LOCAL_PERSISTENT)
|
||||
.put("poolName", "SwimmingPool")
|
||||
.put("serverRegionShortcut", RegionShortcut.PARTITION_PERSISTENT)
|
||||
.build();
|
||||
|
||||
when(mockAnnotationMetadata.hasAnnotation(eq(EnableCachingDefinedRegions.class.getName()))).thenReturn(true);
|
||||
when(mockAnnotationMetadata.getAnnotationAttributes(eq(EnableCachingDefinedRegions.class.getName())))
|
||||
.thenReturn(annotationAttributes);
|
||||
|
||||
this.configuration.setImportMetadata(mockAnnotationMetadata);
|
||||
|
||||
assertThat(this.configuration.resolveClientRegionShortcut()).isEqualTo(ClientRegionShortcut.LOCAL_PERSISTENT);
|
||||
assertThat(this.configuration.resolvePoolName()).isEqualTo("SwimmingPool");
|
||||
assertThat(this.configuration.resolveServerRegionShortcut()).isEqualTo(RegionShortcut.PARTITION_PERSISTENT);
|
||||
|
||||
verify(mockAnnotationMetadata, times(1))
|
||||
.hasAnnotation(eq(EnableCachingDefinedRegions.class.getName()));
|
||||
|
||||
verify(mockAnnotationMetadata, times(1))
|
||||
.getAnnotationAttributes(eq(EnableCachingDefinedRegions.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheableServiceOneRegistersRegionsOneAndTwo() {
|
||||
|
||||
@@ -128,9 +220,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanDefinition(eq("cacheableServiceOne"));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(2))
|
||||
.registerBeanDefinition(anyString(), any(BeanDefinition.class));
|
||||
|
||||
@@ -160,9 +249,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanDefinition(eq("cacheableServiceTwo"));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size()))
|
||||
.registerBeanDefinition(anyString(), any(BeanDefinition.class));
|
||||
|
||||
@@ -195,9 +281,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanDefinition(eq("cacheableServiceThree"));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size()))
|
||||
.registerBeanDefinition(anyString(), any(BeanDefinition.class));
|
||||
|
||||
@@ -230,9 +313,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanDefinition(eq("cacheableServiceFour"));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, never())
|
||||
.registerBeanDefinition(eq("RegionTwentyFive"), any(BeanDefinition.class));
|
||||
|
||||
@@ -274,9 +354,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
verify(mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanDefinition(eq("cacheableServiceThree"));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(3))
|
||||
.containsBeanDefinition(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME));
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(registeredRegionBeanNames.size()))
|
||||
.registerBeanDefinition(anyString(), any(BeanDefinition.class));
|
||||
|
||||
@@ -309,329 +386,6 @@ public class EnableCachingDefinedRegionsUnitTests {
|
||||
.containsExactly("RegionSixteen", "RegionSeventeen");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveBeanClassFromBeanClassName() throws ClassNotFoundException {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class);
|
||||
|
||||
when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenReturn((Class) CacheableServiceOne.class);
|
||||
|
||||
assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null))
|
||||
.isEqualTo(CacheableServiceOne.class);
|
||||
|
||||
verify(mockBeanDefinition, times(1))
|
||||
.resolveBeanClass(eq(Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
|
||||
verifyZeroInteractions(mockBeanDefinitionRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveBeanClassFromFactoryMethodReturnType() throws ClassNotFoundException {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class);
|
||||
|
||||
MethodMetadata mockMethodMetadata = mock(MethodMetadata.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(" ");
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockMethodMetadata);
|
||||
when(mockMethodMetadata.getReturnTypeName()).thenReturn(CacheableServiceTwo.class.getName());
|
||||
|
||||
assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null))
|
||||
.isEqualTo(CacheableServiceTwo.class);
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
verify(mockMethodMetadata, times(1)).getReturnTypeName();
|
||||
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
verifyNoMoreInteractions(mockMethodMetadata);
|
||||
|
||||
verifyZeroInteractions(mockBeanDefinitionRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanWithInfrastructureClassIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanWithInfrastructureRoleIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureBean(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, never()).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassWithBeanDefinitionIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureClass(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassWithBeanDefinitionIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureClass(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassIsTrue() {
|
||||
assertThat(this.configuration.isNotInfrastructureClass(CacheableServiceOne.class.getName())).isTrue();
|
||||
assertThat(this.configuration.isNotInfrastructureClass("org.example.app.MyClass")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassIsFalse() {
|
||||
assertThat(this.configuration.isNotInfrastructureClass("org.springframework.SomeType")).isFalse();
|
||||
assertThat(this.configuration.isNotInfrastructureClass("org.springframework.core.type.SomeType"))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureRoleIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION).thenReturn(Integer.MAX_VALUE);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
|
||||
assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(2)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureRoleIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.thenReturn(BeanDefinition.ROLE_SUPPORT);
|
||||
|
||||
assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
|
||||
assertThat(this.configuration.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(2)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithNullMethodReturnsFalse() {
|
||||
assertThat(this.configuration.isUserLevelMethod(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithObjectMethodReturnsFalse() throws NoSuchMethodException {
|
||||
assertThat(this.configuration.isUserLevelMethod(Object.class.getMethod("equals", Object.class)))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithUserMethodReturnsTrue() throws NoSuchMethodException {
|
||||
assertThat(this.configuration.isUserLevelMethod(CacheableServiceOne.class.getMethod("cacheableMethodOne")))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationFromClass() {
|
||||
|
||||
Annotation cacheable = this.configuration.resolveAnnotation(CacheableServiceFour.class, Cacheable.class);
|
||||
|
||||
assertThat(cacheable).isNotNull();
|
||||
assertThat(AnnotationUtils.getAnnotationAttributes(cacheable).get("cacheNames"))
|
||||
.isEqualTo(asArray("RegionFifteen"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationFromMethod() throws NoSuchMethodException {
|
||||
|
||||
Method cacheableMethodFour = CacheableServiceFour.class.getMethod("cacheableMethodFour");
|
||||
|
||||
Annotation cachePut = this.configuration.resolveAnnotation(cacheableMethodFour, CachePut.class);
|
||||
|
||||
assertThat(cachePut).isNotNull();
|
||||
assertThat(AnnotationUtils.getAnnotationAttributes(cachePut).get("cacheNames"))
|
||||
.isEqualTo(asArray("RegionTwentyOne", "RegionTwentyTwo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationIsUnresolvable() throws NoSuchMethodException {
|
||||
|
||||
Method cacheableMethodFive = CacheableServiceFour.class.getMethod("cacheableMethodFive");
|
||||
|
||||
assertThat(this.configuration.resolveAnnotation(cacheableMethodFive, Cacheable.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassIsUnresolvable() throws ClassNotFoundException {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn("non.existing.bean.Class");
|
||||
when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenThrow(new ClassNotFoundException("TEST"));
|
||||
|
||||
assertThat(this.configuration.resolveBeanClass(mockBeanDefinition, null).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1))
|
||||
.resolveBeanClass(eq(Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderReturnsRegistryClassLoader() {
|
||||
|
||||
ClassLoader mockClassLoader = mock(ClassLoader.class);
|
||||
|
||||
DefaultListableBeanFactory mockBeanDefinitionRegistry = mock(DefaultListableBeanFactory.class);
|
||||
|
||||
when(mockBeanDefinitionRegistry.getBeanClassLoader()).thenReturn(mockClassLoader);
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassLoader(mockBeanDefinitionRegistry)).isEqualTo(mockClassLoader);
|
||||
|
||||
verify(mockBeanDefinitionRegistry, times(1)).getBeanClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderReturnsThreadContextClassLoader() {
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class);
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassLoader(mockBeanDefinitionRegistry))
|
||||
.isEqualTo(Thread.currentThread().getContextClassLoader());
|
||||
|
||||
verifyZeroInteractions(mockBeanDefinitionRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameReturnsBeanDefinitionBeanClassName() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(CacheableServiceOne.class);
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null))
|
||||
.isEqualTo(CacheableServiceOne.class.getName());
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameReturnsFactoryMethodReturnType() {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
MethodMetadata mockMethodMetadata = mock(MethodMetadata.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockMethodMetadata);
|
||||
when(mockMethodMetadata.getReturnTypeName()).thenReturn(CacheableServiceTwo.class.getName());
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null))
|
||||
.isEqualTo(CacheableServiceTwo.class.getName());
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNoFactoryMethodMetadataReturnsEmpty() {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(null);
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNonAnnotatedBeanDefinitionReturnsEmpty() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNoFactoryMethodNameReturnsEmpty() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn(" ");
|
||||
|
||||
assertThat(this.configuration.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void safeResolveTypeReturnsType() {
|
||||
assertThat(this.configuration.safeResolveType(() -> Object.class)).isEqualTo(Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void safeResolveTypeThrowingClassNotFoundExceptionReturnsNull() {
|
||||
assertThat(this.configuration.safeResolveType(() -> { throw new ClassNotFoundException("TEST"); })).isNull();
|
||||
}
|
||||
|
||||
@Service
|
||||
@Cacheable(cacheNames = { "RegionOne", "RegionTwo" })
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -20,14 +20,35 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
import org.springframework.data.gemfire.test.model.Person;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractAnnotationConfigSupport}.
|
||||
@@ -35,21 +56,149 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see org.springframework.beans.factory.support.AbstractBeanDefinition
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AbstractAnnotationConfigSupportTests {
|
||||
|
||||
@Mock
|
||||
private AbstractAnnotationConfigSupport support;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.support = spy(new TestAnnotationConfigSupport());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private BeanDefinition mockBeanDefinition(Class<?> beanClass) {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class, beanClass.getSimpleName());
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(beanClass.getName());
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION);
|
||||
|
||||
return mockBeanDefinition;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(GemfireTemplate.class);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanWithInfrastructureClassIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureBeanWithInfrastructureRoleIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureBean(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, never()).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassWithBeanDefinitionIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(Object.class);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassWithBeanDefinitionIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(SpringVersion.class);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureClass(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassIsTrue() {
|
||||
assertThat(this.support.isNotInfrastructureClass("org.example.app.model.MyClass")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureClassIsFalse() {
|
||||
assertThat(this.support.isNotInfrastructureClass("org.springframework.SomeType")).isFalse();
|
||||
assertThat(this.support.isNotInfrastructureClass("org.springframework.core.type.SomeType"))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureRoleIsTrue() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_APPLICATION).thenReturn(Integer.MAX_VALUE);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
|
||||
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isTrue();
|
||||
|
||||
verify(mockBeanDefinition, times(2)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotInfrastructureRoleIsFalse() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getRole()).thenReturn(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.thenReturn(BeanDefinition.ROLE_SUPPORT);
|
||||
|
||||
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
|
||||
assertThat(this.support.isNotInfrastructureRole(mockBeanDefinition)).isFalse();
|
||||
|
||||
verify(mockBeanDefinition, times(2)).getRole();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithNullMethodReturnsFalse() {
|
||||
assertThat(this.support.isUserLevelMethod(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithObjectMethodReturnsFalse() throws NoSuchMethodException {
|
||||
assertThat(this.support.isUserLevelMethod(Object.class.getMethod("equals", Object.class)))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUserLevelMethodWithUserMethodReturnsTrue() throws NoSuchMethodException {
|
||||
assertThat(this.support.isUserLevelMethod(Person.class.getMethod("getName"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requirePropertyWithNonStringValueIsSuccessful() {
|
||||
when(support.requireProperty(anyString(), any())).thenCallRealMethod();
|
||||
|
||||
when(support.resolveProperty(anyString(), eq(Integer.class), eq(null))).thenReturn(1);
|
||||
|
||||
assertThat(support.requireProperty("key", Integer.class)).isEqualTo(1);
|
||||
@@ -60,7 +209,7 @@ public class AbstractAnnotationConfigSupportTests {
|
||||
|
||||
@Test
|
||||
public void requirePropertyWithStringValueIsSuccessful() {
|
||||
when(support.requireProperty(anyString(), any())).thenCallRealMethod();
|
||||
|
||||
when(support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn("test");
|
||||
|
||||
assertThat(support.requireProperty("key", String.class)).isEqualTo("test");
|
||||
@@ -71,13 +220,14 @@ public class AbstractAnnotationConfigSupportTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void requirePropertyWithEmptyStringThrowsIllegalArgumentException() {
|
||||
when(support.requireProperty(anyString(), any())).thenCallRealMethod();
|
||||
|
||||
when(support.resolveProperty(anyString(), eq(String.class), eq(null))).thenReturn(" ");
|
||||
|
||||
try {
|
||||
support.requireProperty("key", String.class);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("Property [key] is required");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
@@ -91,7 +241,7 @@ public class AbstractAnnotationConfigSupportTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void requirePropertyWithNullValueThrowsIllegalArgumentException() {
|
||||
when(support.requireProperty(anyString(), any())).thenCallRealMethod();
|
||||
|
||||
when(support.resolveProperty(anyString(), any(), eq(null))).thenReturn(null);
|
||||
|
||||
try {
|
||||
@@ -108,4 +258,288 @@ public class AbstractAnnotationConfigSupportTests {
|
||||
.resolveProperty(eq("key"), eq(Integer.class), eq(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationFromClass() {
|
||||
|
||||
Annotation region = this.support.resolveAnnotation(Person.class, Region.class);
|
||||
|
||||
assertThat(region).isNotNull();
|
||||
assertThat(AnnotationUtils.getAnnotationAttributes(region).get("value")).isEqualTo("People");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationFromMethod() throws NoSuchMethodException {
|
||||
|
||||
Method cacheableMethod = TestService.class.getMethod("cacheableMethod");
|
||||
|
||||
Annotation cacheable = this.support.resolveAnnotation(cacheableMethod, Cacheable.class);
|
||||
|
||||
assertThat(cacheable).isNotNull();
|
||||
assertThat(AnnotationUtils.getAnnotationAttributes(cacheable).get("cacheNames"))
|
||||
.isEqualTo(asArray("cacheOne", "cacheTwo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveAnnotationIsUnresolvable() throws NoSuchMethodException {
|
||||
|
||||
Method cacheableMethodFive = TestService.class.getMethod("nonCacheableMethod");
|
||||
|
||||
assertThat(this.support.resolveAnnotation(cacheableMethodFive, Cacheable.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveBeanClassFromBeanDefinition() throws ClassNotFoundException {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistryConfigurableBeanFactory.class);
|
||||
|
||||
ClassLoader mockClassLoader = mock(ClassLoader.class);
|
||||
|
||||
when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenReturn((Class) Object.class);
|
||||
when(((ConfigurableBeanFactory) mockBeanDefinitionRegistry).getBeanClassLoader()).thenReturn(mockClassLoader);
|
||||
|
||||
assertThat(this.support.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null))
|
||||
.isEqualTo(Object.class);
|
||||
|
||||
verify(mockBeanDefinition, never()).getBeanClassName();
|
||||
verify(mockBeanDefinition, never()).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).resolveBeanClass(eq(mockClassLoader));
|
||||
verify(((ConfigurableBeanFactory) mockBeanDefinitionRegistry), times(1))
|
||||
.getBeanClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassUsingRegistryClassLoaderAndBeanClassName() throws ClassNotFoundException {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistryConfigurableBeanFactory.class);
|
||||
|
||||
ClassLoader testClassLoader = AbstractAnnotationConfigSupport.class.getClassLoader();
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn("java.lang.Object");
|
||||
when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class))).thenThrow(new ClassNotFoundException("test"));
|
||||
when(((ConfigurableBeanFactory) mockBeanDefinitionRegistry).getBeanClassLoader()).thenReturn(testClassLoader);
|
||||
|
||||
assertThat(this.support.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry).orElse(null))
|
||||
.isEqualTo(Object.class);
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, never()).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).resolveBeanClass(eq(testClassLoader));
|
||||
verify((ConfigurableBeanFactory) mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassUsingThreadContextClassLoaderAndFactoryMethodReturnType()
|
||||
throws ClassNotFoundException {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistryConfigurableBeanFactory.class);
|
||||
|
||||
MethodMetadata mockFactoryMethodMetadata = mock(MethodMetadata.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockFactoryMethodMetadata);
|
||||
when(((ConfigurableBeanFactory) mockBeanDefinitionRegistry).getBeanClassLoader()).thenReturn(null);
|
||||
when(mockFactoryMethodMetadata.getReturnTypeName()).thenReturn("java.lang.Object");
|
||||
|
||||
assertThat(this.support.resolveBeanClass(mockBeanDefinition, mockBeanDefinitionRegistry)
|
||||
.orElse(null)).isEqualTo(Object.class);
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
verify((ConfigurableBeanFactory) mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanClassLoader();
|
||||
verify(mockFactoryMethodMetadata, times(1)).getReturnTypeName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassIsUnresolvable() throws ClassNotFoundException {
|
||||
|
||||
AbstractBeanDefinition mockBeanDefinition = mock(AbstractBeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn("non.existing.bean.Class");
|
||||
when(mockBeanDefinition.resolveBeanClass(any(ClassLoader.class)))
|
||||
.thenThrow(new ClassNotFoundException("Class [non.existing.bean.Class] not found"));
|
||||
|
||||
assertThat(this.support.resolveBeanClass(mockBeanDefinition, null).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, never()).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1))
|
||||
.resolveBeanClass(eq(Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderIsNullSafe() {
|
||||
assertThat(this.support.resolveBeanClassLoader(null))
|
||||
.isEqualTo(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderReturnsRegistryClassLoader() {
|
||||
|
||||
ClassLoader mockClassLoader = mock(ClassLoader.class);
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistryConfigurableBeanFactory.class);
|
||||
|
||||
when(((ConfigurableBeanFactory) mockBeanDefinitionRegistry).getBeanClassLoader()).thenReturn(mockClassLoader);
|
||||
|
||||
assertThat(this.support.resolveBeanClassLoader(mockBeanDefinitionRegistry)).isEqualTo(mockClassLoader);
|
||||
|
||||
verify((ConfigurableBeanFactory) mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderReturnsThreadContextClassLoaderWhenRegistryClassLoaderIsNull() {
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistryConfigurableBeanFactory.class);
|
||||
|
||||
when(((ConfigurableBeanFactory) mockBeanDefinitionRegistry).getBeanClassLoader()).thenReturn(null);
|
||||
|
||||
assertThat(this.support.resolveBeanClassLoader(mockBeanDefinitionRegistry))
|
||||
.isEqualTo(Thread.currentThread().getContextClassLoader());
|
||||
|
||||
verify((ConfigurableBeanFactory) mockBeanDefinitionRegistry, times(1))
|
||||
.getBeanClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassLoaderReturnsThreadContextClassLoaderWhenRegistryIsNotConfigurable() {
|
||||
|
||||
BeanDefinitionRegistry mockBeanDefinitionRegistry = mock(BeanDefinitionRegistry.class);
|
||||
|
||||
assertThat(this.support.resolveBeanClassLoader(mockBeanDefinitionRegistry))
|
||||
.isEqualTo(Thread.currentThread().getContextClassLoader());
|
||||
|
||||
verifyZeroInteractions(mockBeanDefinitionRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameIsNullSafe() {
|
||||
assertThat(this.support.resolveBeanClassName(null).orElse(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameReturnsBeanClassName() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mockBeanDefinition(Person.class);
|
||||
|
||||
assertThat(this.support.resolveBeanClassName(mockBeanDefinition).orElse(null))
|
||||
.isEqualTo(Person.class.getName());
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameReturnsFactoryMethodReturnTypeName() {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
MethodMetadata mockFactoryMethodMetadata = mock(MethodMetadata.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(mockFactoryMethodMetadata);
|
||||
when(mockFactoryMethodMetadata.getReturnTypeName()).thenReturn(Person.class.getName());
|
||||
|
||||
assertThat(this.support.resolveBeanClassName(mockBeanDefinition).orElse(null))
|
||||
.isEqualTo(Person.class.getName());
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
verify(mockFactoryMethodMetadata, times(1)).getReturnTypeName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNoFactoryMethodMetadataReturnsEmpty() {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(" ");
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn("testFactoryMethod");
|
||||
when(mockBeanDefinition.getFactoryMethodMetadata()).thenReturn(null);
|
||||
|
||||
assertThat(this.support.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodMetadata();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNoFactoryMethodNameReturnsEmpty() {
|
||||
|
||||
AnnotatedBeanDefinition mockBeanDefinition = mock(AnnotatedBeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn("");
|
||||
when(mockBeanDefinition.getFactoryMethodName()).thenReturn(" ");
|
||||
|
||||
assertThat(this.support.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, times(1)).getFactoryMethodName();
|
||||
verify(mockBeanDefinition, never()).getFactoryMethodMetadata();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveBeanClassNameWithNonAnnotatedBeanDefinitionReturnsEmpty() {
|
||||
|
||||
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
|
||||
|
||||
when(mockBeanDefinition.getBeanClassName()).thenReturn(null);
|
||||
|
||||
assertThat(this.support.resolveBeanClassName(mockBeanDefinition).orElse(null)).isNull();
|
||||
|
||||
verify(mockBeanDefinition, times(1)).getBeanClassName();
|
||||
verify(mockBeanDefinition, never()).getFactoryMethodName();
|
||||
verifyNoMoreInteractions(mockBeanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void safeResolveTypeReturnsType() {
|
||||
assertThat(this.support.safeResolveType(() -> Object.class)).isEqualTo(Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void safeResolveTypeThrowingClassNotFoundExceptionReturnsNull() {
|
||||
assertThat(this.support.safeResolveType(() -> { throw new ClassNotFoundException("TEST"); })).isNull();
|
||||
}
|
||||
|
||||
interface BeanDefinitionRegistryConfigurableBeanFactory extends BeanDefinitionRegistry, ConfigurableBeanFactory {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
interface TestService {
|
||||
|
||||
@Cacheable({ "cacheOne", "cacheTwo" })
|
||||
void cacheableMethod();
|
||||
|
||||
void nonCacheableMethod();
|
||||
|
||||
}
|
||||
|
||||
class TestAnnotationConfigSupport extends AbstractAnnotationConfigSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotationType() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user