SGF-757 - Move configuration of PDX to a PdxConfiguration class imported by the EnablePdx annotation.

This commit is contained in:
John Blum
2018-06-09 17:38:10 -07:00
parent bca6ca8a7a
commit cc8ec39a2e
11 changed files with 908 additions and 405 deletions

View File

@@ -33,17 +33,12 @@ import org.apache.geode.cache.TransactionWriter;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.util.GatewayConflictResolver;
import org.apache.geode.pdx.PdxSerializer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
@@ -51,9 +46,6 @@ import org.springframework.data.gemfire.config.annotation.support.AbstractAnnota
import org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor;
import org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener;
import org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor;
import org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.data.gemfire.util.PropertiesBuilder;
import org.springframework.util.StringUtils;
@@ -72,7 +64,6 @@ import org.springframework.util.StringUtils;
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.server.CacheServer
* @see org.apache.geode.pdx.PdxSerializer
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
@@ -80,7 +71,7 @@ import org.springframework.util.StringUtils;
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.core.convert.ConversionService
* @see org.springframework.core.annotation.AnnotationAttributes
* @see org.springframework.core.io.Resource
* @see org.springframework.core.type.AnnotationMetadata
* @see org.springframework.data.gemfire.CacheFactoryBean
@@ -89,19 +80,20 @@ import org.springframework.util.StringUtils;
* @see org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor
* @see org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener
* @see org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor
* @see org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
* @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
* @since 1.9.0
*/
@Configuration
@SuppressWarnings("unused")
public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
private static final AtomicBoolean CUSTOM_EDITORS_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean CUSTOM_EDITORS_BEAN_FACTORY_POST_PROCESSOR_REGISTERED =
new AtomicBoolean(false);
private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED =
new AtomicBoolean(false);
private static final AtomicBoolean DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED =
new AtomicBoolean(false);
protected static final boolean DEFAULT_CLOSE = true;
protected static final boolean DEFAULT_COPY_ON_READ = false;
@@ -117,13 +109,9 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
private boolean copyOnRead = DEFAULT_COPY_ON_READ;
private boolean useBeanFactoryLocator = DEFAULT_USE_BEAN_FACTORY_LOCATOR;
private Boolean pdxIgnoreUnreadFields;
private Boolean pdxPersistent;
private Boolean pdxReadSerialized;
private DynamicRegionSupport dynamicRegionSupport;
private Integer mcastPort = 0;
private Integer mcastPort = DEFAULT_MCAST_PORT;
private Float criticalHeapPercentage;
private Float criticalOffHeapPercentage;
@@ -132,14 +120,9 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
private GatewayConflictResolver gatewayConflictResolver;
@Autowired(required = false)
private GemfireMappingContext mappingContext;
private List<JndiDataSource> jndiDataSources;
private List<TransactionListener> transactionListeners;
private PdxSerializer pdxSerializer;
private PropertiesBuilder customGemFireProperties = PropertiesBuilder.create();
private Resource cacheXml;
@@ -147,7 +130,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
private String locators = DEFAULT_LOCATORS;
private String logLevel = DEFAULT_LOG_LEVEL;
private String name;
private String pdxDiskStoreName;
private String startLocator;
private TransactionWriter transactionWriter;
@@ -197,8 +179,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
configureInfrastructure(importMetadata);
configureCache(importMetadata);
configurePdx(importMetadata);
configureTheRest(importMetadata);
configureOptional(importMetadata);
}
/**
@@ -216,16 +197,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
registerDiskStoreDirectoryBeanPostProcessor(importMetadata);
}
/* (non-Javadoc) */
private void registerCustomEditorBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
if (CUSTOM_EDITORS_REGISTERED.compareAndSet(false, true)) {
if (CUSTOM_EDITORS_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
register(BeanDefinitionBuilder.rootBeanDefinition(CustomEditorBeanFactoryPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
}
}
/* (non-Javadoc) */
private void registerDefinedIndexesApplicationListener(AnnotationMetadata importMetadata) {
if (DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED.compareAndSet(false, true)) {
@@ -234,7 +213,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
}
}
/* (non-Javadoc) */
private void registerDiskStoreDirectoryBeanPostProcessor(AnnotationMetadata importMetadata) {
if (DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
@@ -303,117 +281,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
}
/**
* Configures Pivotal GemFire/Apache Geode cache PDX Serialization.
*
* @param importMetadata {@link AnnotationMetadata} containing PDX meta-data used to configure the cache
* with PDX de/serialization capabilities.
* @see org.springframework.core.type.AnnotationMetadata
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/data_serialization/gemfire_pdx_serialization.html">Pivotal GemFire PDX Serialization</a>
*/
protected void configurePdx(AnnotationMetadata importMetadata) {
String enablePdxTypeName = EnablePdx.class.getName();
if (importMetadata.hasAnnotation(enablePdxTypeName)) {
AnnotationAttributes enablePdxAttributes = getAnnotationAttributes(importMetadata, enablePdxTypeName);
setPdxDiskStoreName(resolveProperty(pdxProperty("disk-store-name"),
(String) enablePdxAttributes.get("diskStoreName")));
setPdxIgnoreUnreadFields(resolveProperty(pdxProperty("ignore-unread-fields"),
Boolean.TRUE.equals(enablePdxAttributes.get("ignoreUnreadFields"))));
setPdxPersistent(resolveProperty(pdxProperty("persistent"),
Boolean.TRUE.equals(enablePdxAttributes.get("persistent"))));
setPdxReadSerialized(resolveProperty(pdxProperty("read-serialized"),
Boolean.TRUE.equals(enablePdxAttributes.get("readSerialized"))));
setPdxSerializer(resolvePdxSerializer((String) enablePdxAttributes.get("serializerBeanName")));
registerPdxDiskStoreAwareBeanFactoryPostProcessor(importMetadata);
}
}
/**
* Resolves the {@link PdxSerializer} used to configure the cache for PDX De/Serialization.
*
* @param pdxSerializerBeanName {@link String} containing the name of a Spring bean
* implementing the {@link PdxSerializer} interface.
* @return the resolved {@link PdxSerializer} from configuration.
* @see org.apache.geode.pdx.PdxSerializer
* @see #newPdxSerializer(BeanFactory)
* @see #getBeanFactory()
*/
protected PdxSerializer resolvePdxSerializer(String pdxSerializerBeanName) {
BeanFactory beanFactory = getBeanFactory();
return Optional.ofNullable(pdxSerializerBeanName)
.filter(beanFactory::containsBean)
.map(beanName -> beanFactory.getBean(beanName, PdxSerializer.class))
.orElseGet(() -> Optional.ofNullable(getPdxSerializer()).orElseGet(() -> newPdxSerializer(beanFactory)));
}
/**
* Constructs a new instance of {@link PdxSerializer}.
*
* @param <T> {@link Class} type of the {@link PdxSerializer}.
* @return a new instance of {@link PdxSerializer}.
* @see org.apache.geode.pdx.PdxSerializer
* @see #newPdxSerializer(BeanFactory)
*/
@SuppressWarnings("unchecked")
protected <T extends PdxSerializer> T newPdxSerializer() {
return newPdxSerializer(getBeanFactory());
}
/**
* Constructs a new instance of {@link MappingPdxSerializer}.
*
* @param <T> {@link Class} type of the {@link PdxSerializer}; this method returns a {@link MappingPdxSerializer}.
* @param beanFactory {@link BeanFactory} used to get an instance of {@link ConversionService}
* used by the {@link MappingPdxSerializer}.
* @return a new instance of {@link MappingPdxSerializer}.
* @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
* @see org.springframework.beans.factory.BeanFactory
* @see org.apache.geode.pdx.PdxSerializer
*/
@SuppressWarnings("unchecked")
protected <T extends PdxSerializer> T newPdxSerializer(BeanFactory beanFactory) {
Optional<ConversionService> conversionService = Optional.ofNullable(beanFactory)
.filter(it -> it instanceof ConfigurableBeanFactory)
.map(it -> ((ConfigurableBeanFactory) it).getConversionService());
return (T) MappingPdxSerializer.create(this.mappingContext, conversionService.orElse(null));
}
/* (non-Javadoc) */
private void registerPdxDiskStoreAwareBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
Optional.ofNullable(getPdxDiskStoreName())
.filter(StringUtils::hasText)
.ifPresent(pdxDiskStoreName -> {
if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addConstructorArgValue(pdxDiskStoreName)
.getBeanDefinition());
}
});
}
/**
=======
>>>>>>> 09d8cc7... DATAGEODE-117 - Move configuration of PDX to a PdxConfiguration class imported by the EnablePdx annotation.
* Callback method allowing developers to configure other cache or application specific configuration settings.
*
* @param importMetadata {@link AnnotationMetadata} containing meta-data used to configure the cache or application.
* @see org.springframework.core.type.AnnotationMetadata
*/
protected void configureTheRest(AnnotationMetadata importMetadata) {
}
protected void configureOptional(AnnotationMetadata importMetadata) { }
/**
* Constructs a new, initialized instance of {@link CacheFactoryBean} based on the Spring application's
@@ -477,11 +352,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
gemfireCache.setGatewayConflictResolver(getGatewayConflictResolver());
gemfireCache.setJndiDataSources(getJndiDataSources());
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setPdxDiskStoreName(getPdxDiskStoreName());
gemfireCache.setPdxIgnoreUnreadFields(getPdxIgnoreUnreadFields());
gemfireCache.setPdxPersistent(getPdxPersistent());
gemfireCache.setPdxReadSerialized(getPdxReadSerialized());
gemfireCache.setPdxSerializer(getPdxSerializer());
gemfireCache.setTransactionListeners(getTransactionListeners());
gemfireCache.setTransactionWriter(getTransactionWriter());
@@ -551,7 +421,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
protected boolean isTypedCacheApplication(Class<? extends Annotation> annotationType,
AnnotationMetadata importMetadata) {
return (annotationType.equals(getAnnotationType()) && importMetadata.hasAnnotation(getAnnotationTypeName()));
return annotationType.equals(getAnnotationType()) && importMetadata.hasAnnotation(getAnnotationTypeName());
}
/**
@@ -687,14 +557,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
return Optional.ofNullable(this.logLevel).orElse(DEFAULT_LOG_LEVEL);
}
void setMappingContext(GemfireMappingContext mappingContext) {
this.mappingContext = mappingContext;
}
protected GemfireMappingContext mappingContext() {
return this.mappingContext;
}
void setMcastPort(Integer mcastPort) {
this.mcastPort = mcastPort;
this.locators = DEFAULT_LOCATORS;
@@ -712,46 +574,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi
return Optional.ofNullable(this.name).filter(StringUtils::hasText).orElseGet(this::toString);
}
void setPdxDiskStoreName(String pdxDiskStoreName) {
this.pdxDiskStoreName = pdxDiskStoreName;
}
protected String getPdxDiskStoreName() {
return this.pdxDiskStoreName;
}
void setPdxIgnoreUnreadFields(Boolean pdxIgnoreUnreadFields) {
this.pdxIgnoreUnreadFields = pdxIgnoreUnreadFields;
}
protected Boolean getPdxIgnoreUnreadFields() {
return this.pdxIgnoreUnreadFields;
}
void setPdxPersistent(Boolean pdxPersistent) {
this.pdxPersistent = pdxPersistent;
}
protected Boolean getPdxPersistent() {
return this.pdxPersistent;
}
void setPdxReadSerialized(Boolean pdxReadSerialized) {
this.pdxReadSerialized = pdxReadSerialized;
}
protected Boolean getPdxReadSerialized() {
return this.pdxReadSerialized;
}
void setPdxSerializer(PdxSerializer pdxSerializer) {
this.pdxSerializer = pdxSerializer;
}
protected PdxSerializer getPdxSerializer() {
return this.pdxSerializer;
}
void setStartLocator(String startLocator) {
this.startLocator = startLocator;
}

View File

@@ -172,7 +172,7 @@ public class CacheServerConfiguration extends PeerCacheConfiguration {
* @see org.apache.geode.cache.server.CacheServer
*/
@Override
protected void configureTheRest(AnnotationMetadata importMetadata) {
protected void configureOptional(AnnotationMetadata importMetadata) {
super.configureCache(importMetadata);

View File

@@ -154,7 +154,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration {
return gemfireCache;
}
/* (non-Javadoc) */
@SuppressWarnings("all")
private List<ClientCacheConfigurer> resolveClientCacheConfigurers() {

View File

@@ -25,6 +25,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotationMetadata;
/**
@@ -34,13 +35,16 @@ import org.springframework.core.type.AnnotationMetadata;
*
* @author John Blum
* @see java.lang.annotation.Annotation
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration#configurePdx(AnnotationMetadata)
* @see org.springframework.data.gemfire.config.annotation.PdxConfiguration
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(PdxConfiguration.class)
@SuppressWarnings("unused")
public @interface EnablePdx {
@@ -49,7 +53,7 @@ public @interface EnablePdx {
*
* Use the {@literal spring.data.gemfire.pdx.disk-store-name} property in {@literal application.properties}.
*/
String diskStoreName() default "";
String diskStoreName() default PdxConfiguration.DEFAULT_PDX_DISK_STORE_NAME;
/**
* Configures whether pdx ignores fields that were unread during deserialization.
@@ -58,7 +62,7 @@ public @interface EnablePdx {
*
* Use the {@literal spring.data.gemfire.pdx.ignore-unread-fields} property in {@literal application.properties}.
*/
boolean ignoreUnreadFields() default false;
boolean ignoreUnreadFields() default PdxConfiguration.DEFAULT_IGNORE_UNREAD_FIELDS;
/**
* Configures whether the type metadata for PDX objects is persisted to disk.
@@ -67,7 +71,7 @@ public @interface EnablePdx {
*
* Use the {@literal spring.data.gemfire.pdx.persistent} property in {@literal application.properties}.
*/
boolean persistent() default false;
boolean persistent() default PdxConfiguration.DEFAULT_PERSISTENT;
/**
* Configures the object preference to {@link org.apache.geode.pdx.PdxInstance} type or {@link Object}.
@@ -76,11 +80,13 @@ public @interface EnablePdx {
*
* Use the {@literal spring.data.gemfire.pdx.read-serialized} property in {@literal application.properties}.
*/
boolean readSerialized() default false;
boolean readSerialized() default PdxConfiguration.DEFAULT_READ_SERIALIZED;
/**
* Configures the PDX serializer to be used by the cache to serialize object data.
*
* Use the {@literal spring.data.gemfire.pdx.serializer-bean-name} property in {@literal application.properties}.
*/
String serializerBeanName() default "";
String serializerBeanName() default PdxConfiguration.DEFAULT_PDX_SERIALIZER_BEAN_NAME;
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import java.lang.annotation.Annotation;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.pdx.PdxSerializer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.data.gemfire.support.NoOpBeanFactoryPostProcessor;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
/**
* The {@link PdxConfiguration} class is a Spring {@link Configuration} class that configures PDX
* on a {@link GemFireCache} instance.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.pdx.PdxSerializer
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.core.annotation.AnnotationAttributes
* @see org.springframework.core.type.AnnotationMetadata
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor
* @see org.springframework.data.gemfire.mapping.MappingPdxSerializer
* @see org.springframework.data.gemfire.support.NoOpBeanFactoryPostProcessor
* @since 2.1.0
*/
@SuppressWarnings("unused")
@Configuration("PdxConfiguration")
public class PdxConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
public static final boolean DEFAULT_IGNORE_UNREAD_FIELDS = false;
public static final boolean DEFAULT_PERSISTENT = false;
public static final boolean DEFAULT_READ_SERIALIZED = false;
public static final String DEFAULT_PDX_DISK_STORE_NAME = "";
public static final String DEFAULT_PDX_SERIALIZER_BEAN_NAME = "";
private Boolean ignoreUnreadFields;
private Boolean persistent;
private Boolean readSerialized;
private String diskStoreName;
private String serializerBeanName;
@Override
protected Class<? extends Annotation> getAnnotationType() {
return EnablePdx.class;
}
@Override
protected BeanFactory getBeanFactory() {
return super.getBeanFactory();
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
if (isAnnotationPresent(importMetadata)) {
AnnotationAttributes enablePdxAttributes = getAnnotationAttributes(importMetadata);
setDiskStoreName(resolveProperty(pdxProperty("disk-store-name"),
enablePdxAttributes.containsKey("diskStoreName")
? enablePdxAttributes.getString("diskStoreName")
: null));
setIgnoreUnreadFields(resolveProperty(pdxProperty("ignore-unread-fields"),
enablePdxAttributes.containsKey("ignoreUnreadFields")
? enablePdxAttributes.getBoolean("ignoreUnreadFields")
: DEFAULT_IGNORE_UNREAD_FIELDS));
setPersistent(resolveProperty(pdxProperty("persistent"),
enablePdxAttributes.containsKey("persistent")
? enablePdxAttributes.getBoolean("persistent")
: null));
setReadSerialized(resolveProperty(pdxProperty("read-serialized"),
enablePdxAttributes.containsKey("readSerialized")
? enablePdxAttributes.getBoolean("readSerialized")
: null));
setSerializerBeanName(resolveProperty(pdxProperty("serializer-bean-name"),
enablePdxAttributes.containsKey("serializerBeanName")
? enablePdxAttributes.getString("serializerBeanName")
: null));
}
}
void setDiskStoreName(String diskStoreName) {
this.diskStoreName = diskStoreName;
}
protected Optional<String> getDiskStoreName() {
return Optional.ofNullable(this.diskStoreName).filter(StringUtils::hasText);
}
void setIgnoreUnreadFields(Boolean ignoreUnreadFields) {
this.ignoreUnreadFields = ignoreUnreadFields;
}
protected boolean isIgnoreUnreadFields() {
return Boolean.TRUE.equals(this.ignoreUnreadFields);
}
void setPersistent(Boolean persistent) {
this.persistent = persistent;
}
protected boolean isPersistent() {
return Boolean.TRUE.equals(this.persistent);
}
void setReadSerialized(Boolean readSerialized) {
this.readSerialized = readSerialized;
}
protected boolean isReadSerialized() {
return Boolean.TRUE.equals(this.readSerialized);
}
void setSerializerBeanName(String serializerBeanName) {
this.serializerBeanName = serializerBeanName;
}
protected Optional<String> getSerializerBeanName() {
return Optional.ofNullable(this.serializerBeanName).filter(StringUtils::hasText);
}
@Bean
BeanFactoryPostProcessor pdxDiskStoreAwareBeanFactoryPostProcessor() {
return getDiskStoreName()
.<BeanFactoryPostProcessor>map(PdxDiskStoreAwareBeanFactoryPostProcessor::new)
.orElse(NoOpBeanFactoryPostProcessor.INSTANCE);
}
@Bean
ClientCacheConfigurer clientCachePdxConfigurer() {
return (beanName, clientCacheFactoryBean) -> configurePdx(clientCacheFactoryBean);
}
@Bean
PeerCacheConfigurer peerCachePdxConfigurer() {
return (beanName, cacheFactoryBean) -> configurePdx(cacheFactoryBean);
}
/**
* Configures Pivotal GemFire/Apache Geode cache PDX Serialization.
*
* @param cacheFactoryBean {@link CacheFactoryBean} instance on which to configure PDX.
* with PDX de/serialization capabilities.
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/data_serialization/gemfire_pdx_serialization.html">GemFire PDX Serialization</a>
*/
protected void configurePdx(CacheFactoryBean cacheFactoryBean) {
getDiskStoreName().ifPresent(cacheFactoryBean::setPdxDiskStoreName);
cacheFactoryBean.setPdxIgnoreUnreadFields(isIgnoreUnreadFields());
cacheFactoryBean.setPdxPersistent(isPersistent());
cacheFactoryBean.setPdxReadSerialized(isReadSerialized());
cacheFactoryBean.setPdxSerializer(resolvePdxSerializer());
}
/**
* Resolves the configured {@link ConversionService} from the {@link BeanFactory}.
*
* @return an {@link Optional optionally} configured {@link ConversionService}.
* @see org.springframework.core.convert.ConversionService
* @see java.util.Optional
* @see #getBeanFactory()
*/
protected Optional<ConversionService> resolveConversionService() {
return Optional.of(getBeanFactory())
.filter(it -> it instanceof ConfigurableBeanFactory)
.map(it -> ((ConfigurableBeanFactory) it).getConversionService());
}
/**
* Returns any configured {@link GemfireMappingContext} registered in the {@link BeanFactory}.
*
* @return an {@link Optional optionally} configured {@link GemfireMappingContext}.
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
* @see java.util.Optional
* @see #getBeanFactory()
*/
protected Optional<GemfireMappingContext> resolveMappingContext() {
try {
return Optional.of(getBeanFactory().getBean(GemfireMappingContext.class));
}
catch (Throwable ignore) {
return Optional.empty();
}
}
/**
* Resolves the {@link PdxSerializer} used to configure the cache for PDX Serialization.
*
* implementing the {@link PdxSerializer} interface.
* @return the resolved {@link PdxSerializer} from configuration.
* @see org.apache.geode.pdx.PdxSerializer
* @see #getBeanFactory()
*/
@NonNull
@SuppressWarnings("unchecked")
protected PdxSerializer resolvePdxSerializer() {
BeanFactory beanFactory = getBeanFactory();
return getSerializerBeanName()
.filter(beanFactory::containsBean)
.map(beanName -> beanFactory.getBean(beanName, PdxSerializer.class))
.orElseGet(this::newPdxSerializer);
}
/**
* Constructs a new instance of {@link PdxSerializer}.
*
* @param <T> {@link Class} type of the {@link PdxSerializer}.
* @return a new instance of {@link PdxSerializer}.
* @see org.apache.geode.pdx.PdxSerializer
*/
@NonNull
@SuppressWarnings("unchecked")
protected <T extends PdxSerializer> T newPdxSerializer() {
return (T) MappingPdxSerializer.create(resolveMappingContext().orElse(null),
resolveConversionService().orElse(null));
}
}

View File

@@ -61,7 +61,9 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos
* @throws IllegalArgumentException if the Pivotal GemFire PDX {@link DiskStore} name is unspecified.
*/
public PdxDiskStoreAwareBeanFactoryPostProcessor(String pdxDiskStoreName) {
Assert.hasText(pdxDiskStoreName, "PDX DiskStore name is required");
this.pdxDiskStoreName = pdxDiskStoreName;
}
@@ -104,7 +106,7 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// NOTE for simplicity sake, we add a bean dependency to any bean definition for a bean
// (Async Event Queue, Region, Disk Store or otherwise) that may potentially require
// (Async Event Queue, Disk Store, Region, or otherwise) that may potentially require
// the PDX Disk Store to exist.
// NOTE this logic could be optimized to include Disk Store, persistent Async Event Queues
// and persistent Regions (either by way of 'persistent' attribute, Data Policy
@@ -124,7 +126,10 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos
* @see org.springframework.beans.factory.config.BeanDefinition#setDependsOn(String[])
*/
private void addPdxDiskStoreDependency(BeanDefinition beanDefinition) {
String[] newDependsOn = (String[]) ArrayUtils.insert(getDependsOn(beanDefinition), 0, getPdxDiskStoreName());
String[] newDependsOn =
(String[]) ArrayUtils.insert(getDependsOn(beanDefinition), 0, getPdxDiskStoreName());
beanDefinition.setDependsOn(newDependsOn);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.support;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* The {@link NoOpBeanFactoryPostProcessor} class is a Spring {@link BeanFactoryPostProcessor} implementation
* that performs no operation.
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @since 1.0.0
*/
@SuppressWarnings("unused")
public final class NoOpBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public static final NoOpBeanFactoryPostProcessor INSTANCE = new NoOpBeanFactoryPostProcessor();
private NoOpBeanFactoryPostProcessor() { }
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { }
}

View File

@@ -18,37 +18,14 @@
package org.springframework.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
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.config.annotation.AbstractCacheConfiguration.DEFAULT_MCAST_PORT;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.geode.pdx.PdxSerializer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.util.MethodInvoker;
/**
* Unit tests for {@link AbstractCacheConfiguration}.
@@ -66,183 +43,26 @@ import org.springframework.util.MethodInvoker;
@RunWith(MockitoJUnitRunner.class)
public class AbstractCacheConfigurationUnitTests {
@Mock
private BeanFactory mockBeanFactory;
private TestCacheConfiguration cacheConfiguration;
@Before
public void setup() {
cacheConfiguration = new TestCacheConfiguration();
}
@SuppressWarnings("unchecked")
private <T> T invokeMethod(Object obj, String methodName) throws Exception {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(obj);
methodInvoker.setTargetMethod(methodName);
methodInvoker.prepare();
return (T) methodInvoker.invoke();
}
@Spy
private AbstractCacheConfiguration cacheConfiguration;
@Test
public void configurePdxWhenEnablePdxIsConfigured() {
public void gemfirePropertiesContainsEssentialProperties() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
PdxSerializer mockPdxSerializer = mock(PdxSerializer.class);
this.cacheConfiguration.setName("TestName");
this.cacheConfiguration.setMcastPort(-1);
this.cacheConfiguration.setLogLevel("DEBUG");
this.cacheConfiguration.setLocators("skullbox[11235]");
this.cacheConfiguration.setStartLocator("boombox[12480]");
Map<String, Object> annotationAttributes = new HashMap<>(5);
Properties gemfireProperties = this.cacheConfiguration.gemfireProperties();
annotationAttributes.put("diskStoreName", "BlockDiskStore");
annotationAttributes.put("ignoreUnreadFields", Boolean.FALSE);
annotationAttributes.put("persistent", Boolean.TRUE);
annotationAttributes.put("readSerialized", Boolean.TRUE);
annotationAttributes.put("serializerBeanName", "MockPdxSerializer");
when(mockAnnotationMetadata.hasAnnotation(eq(EnablePdx.class.getName()))).thenReturn(true);
when(mockAnnotationMetadata.getAnnotationAttributes(eq(EnablePdx.class.getName()))).thenReturn(annotationAttributes);
when(mockBeanFactory.containsBean(eq("MockPdxSerializer"))).thenReturn(true);
when(mockBeanFactory.getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class))).thenReturn(mockPdxSerializer);
cacheConfiguration.setBeanFactory(mockBeanFactory);
cacheConfiguration.configurePdx(mockAnnotationMetadata);
assertThat(cacheConfiguration.getPdxDiskStoreName()).isEqualTo("BlockDiskStore");
assertThat(cacheConfiguration.getPdxIgnoreUnreadFields()).isFalse();
assertThat(cacheConfiguration.getPdxPersistent()).isTrue();
assertThat(cacheConfiguration.getPdxReadSerialized()).isTrue();
assertThat(cacheConfiguration.getPdxSerializer()).isEqualTo(mockPdxSerializer);
verify(mockAnnotationMetadata, times(1)).hasAnnotation(eq(EnablePdx.class.getName()));
verify(mockAnnotationMetadata, times(1)).getAnnotationAttributes(eq(EnablePdx.class.getName()));
verify(mockBeanFactory, times(1)).containsBean(eq("MockPdxSerializer"));
verify(mockBeanFactory, times(1)).getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class));
}
@Test
public void configurePdxWhenEnablePdxIsNotConfigured() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
when(mockAnnotationMetadata.hasAnnotation(anyString())).thenReturn(false);
cacheConfiguration.configurePdx(mockAnnotationMetadata);
assertThat(cacheConfiguration.getPdxDiskStoreName()).isNull();
assertThat(cacheConfiguration.getPdxIgnoreUnreadFields()).isNull();
assertThat(cacheConfiguration.getPdxPersistent()).isNull();
assertThat(cacheConfiguration.getPdxReadSerialized()).isNull();
assertThat(cacheConfiguration.getPdxSerializer()).isNull();
verify(mockAnnotationMetadata, times(1)).hasAnnotation(eq(EnablePdx.class.getName()));
verifyNoMoreInteractions(mockAnnotationMetadata);
}
@Test
public void resolvePdxSerializerUsesPdxSerializerBean() {
PdxSerializer mockPdxSerializer = mock(PdxSerializer.class);
when(mockBeanFactory.containsBean(anyString())).thenReturn(true);
when(mockBeanFactory.getBean(anyString(), eq(PdxSerializer.class))).thenReturn(mockPdxSerializer);
cacheConfiguration.setBeanFactory(mockBeanFactory);
PdxSerializer actualPdxSerializer = cacheConfiguration.resolvePdxSerializer("MockPdxSerializer");
assertThat(actualPdxSerializer).isEqualTo(mockPdxSerializer);
verify(mockBeanFactory, times(1)).containsBean(eq("MockPdxSerializer"));
verify(mockBeanFactory, times(1)).getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class));
}
@Test
public void resolvePdxSerializerUsesConfiguredPdxSerializer() {
PdxSerializer mockPdxSerializer = mock(PdxSerializer.class);
when(mockBeanFactory.containsBean(anyString())).thenReturn(false);
cacheConfiguration.setBeanFactory(mockBeanFactory);
cacheConfiguration.setPdxSerializer(mockPdxSerializer);
PdxSerializer actualPdxSerializer = cacheConfiguration.resolvePdxSerializer("TestPdxSerializer");
assertThat(actualPdxSerializer).isEqualTo(mockPdxSerializer);
verify(mockBeanFactory, times(1)).containsBean(eq("TestPdxSerializer"));
verify(mockBeanFactory, never()).getBean(anyString(), eq(PdxSerializer.class));
verifyZeroInteractions(mockPdxSerializer);
}
@Test
public void resolvePdxSerializerCallsNewMappingPdxSerializer() {
AbstractCacheConfiguration cacheConfigurationSpy = spy(this.cacheConfiguration);
MappingPdxSerializer mockPdxSerializer = mock(MappingPdxSerializer.class);
when(mockBeanFactory.containsBean(anyString())).thenReturn(false);
doReturn(mockPdxSerializer).when(cacheConfigurationSpy).newPdxSerializer(any(BeanFactory.class));
cacheConfigurationSpy.setBeanFactory(mockBeanFactory);
PdxSerializer actualPdxSerializer = cacheConfigurationSpy.resolvePdxSerializer("TestPdxSerializer");
assertThat(actualPdxSerializer).isEqualTo(mockPdxSerializer);
verify(mockBeanFactory, times(1)).containsBean(eq("TestPdxSerializer"));
verify(mockBeanFactory, never()).getBean(anyString(), eq(PdxSerializer.class));
verify(cacheConfigurationSpy, times(1)).newPdxSerializer(eq(mockBeanFactory));
}
@Test
public void newPdxSerializerUsesConfiguredConversionServiceAndMappingContext() throws Exception {
ConfigurableBeanFactory mockBeanFactory = mock(ConfigurableBeanFactory.class);
ConversionService mockConversionService = mock(ConversionService.class);
GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class);
when(mockBeanFactory.getConversionService()).thenReturn(mockConversionService);
cacheConfiguration.setBeanFactory(mockBeanFactory);
cacheConfiguration.setMappingContext(mockMappingContext);
MappingPdxSerializer pdxSerializer = cacheConfiguration.newPdxSerializer();
assertThat(pdxSerializer).isNotNull();
assertThat((Object) invokeMethod(pdxSerializer, "getConversionService")).isEqualTo(mockConversionService);
assertThat((Object) invokeMethod(pdxSerializer, "getMappingContext")).isEqualTo(mockMappingContext);
verify(mockBeanFactory, times(2)).getConversionService();
verifyZeroInteractions(mockConversionService);
verifyZeroInteractions(mockMappingContext);
}
@Test
public void newPdxSerializerDefaultsConversionServiceAndMappingContextWhenNotConfigured() throws Exception {
cacheConfiguration.setBeanFactory(mockBeanFactory);
MappingPdxSerializer pdxSerializer = cacheConfiguration.newPdxSerializer();
assertThat(pdxSerializer).isNotNull();
assertThat((Object) invokeMethod(pdxSerializer, "getConversionService")).isInstanceOf(ConversionService.class);
assertThat((Object) invokeMethod(pdxSerializer, "getMappingContext")).isInstanceOf(GemfireMappingContext.class);
}
protected static class TestCacheConfiguration extends AbstractCacheConfiguration {
@Override
protected Class<? extends Annotation> getAnnotationType() {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
protected <T extends CacheFactoryBean> T newCacheFactoryBean() {
throw new UnsupportedOperationException("Not Implemented");
}
assertThat(gemfireProperties).isNotNull();
assertThat(gemfireProperties).hasSize(5);
assertThat(gemfireProperties.getProperty("name")).isEqualTo("TestName");
assertThat(gemfireProperties.getProperty("mcast-port")).isEqualTo(String.valueOf(DEFAULT_MCAST_PORT));
assertThat(gemfireProperties.getProperty("log-level")).isEqualTo("DEBUG");
assertThat(gemfireProperties.getProperty("locators")).isEqualTo("skullbox[11235]");
assertThat(gemfireProperties.getProperty("start-locator")).isEqualTo("boombox[12480]");
}
}

View File

@@ -17,17 +17,21 @@
package org.springframework.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.pdx.PdxSerializer;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.mock.env.MockPropertySource;
@@ -267,9 +271,10 @@ public class EnableGemFirePropertiesIntegrationTests {
}
@Test
public void nameAndGroupGemFirePropertiesAnnotationConfiguration() {
public void nameAndGroupsAnnotationBasedGemFirePropertiesConfiguration() {
this.applicationContext = newApplicationContext(TestNameAndGroupGemFirePropertiesAnnotationConfiguration.class);
this.applicationContext =
newApplicationContext(TestNameAndGroupsAnnotationBasedGemFirePropertiesConfiguration.class);
assertThat(this.applicationContext).isNotNull();
assertThat(this.applicationContext.containsBean("gemfireCache")).isTrue();
@@ -319,6 +324,37 @@ public class EnableGemFirePropertiesIntegrationTests {
assertThat(gemfireProperties.getProperty("off-heap-memory-size")).isEqualTo("1024g");
}
@Test
public void pdxGemFirePropertiesConfiguration() {
PropertySource testPropertySource = new MockPropertySource("TestPropertySource")
.withProperty("spring.data.gemfire.pdx.disk-store-name", "TestDiskStore")
.withProperty("spring.data.gemfire.pdx.ignore-unread-fields", "true")
.withProperty("spring.data.gemfire.pdx.persistent", "true")
.withProperty("spring.data.gemfire.pdx.read-serialized", "true")
.withProperty("spring.data.gemfire.pdx.serializer-bean-name", "mockPdxSerializer");
this.applicationContext =
newApplicationContext(testPropertySource, TestPdxGemFirePropertiesConfiguration.class);
assertThat(this.applicationContext).isNotNull();
assertThat(this.applicationContext.containsBean("gemfireCache")).isTrue();
assertThat(this.applicationContext.containsBean("mockPdxSerializer")).isTrue();
CacheFactoryBean gemfireCache = this.applicationContext.getBean("&gemfireCache", CacheFactoryBean.class);
assertThat(gemfireCache).isNotNull();
assertThat(gemfireCache.getPdxDiskStoreName()).isEqualTo("TestDiskStore");
assertThat(gemfireCache.getPdxIgnoreUnreadFields()).isTrue();
assertThat(gemfireCache.getPdxPersistent()).isTrue();
assertThat(gemfireCache.getPdxReadSerialized()).isTrue();
PdxSerializer mockPdxSerializer = this.applicationContext.getBean("mockPdxSerializer", PdxSerializer.class);
assertThat(mockPdxSerializer).isNotNull();
assertThat(gemfireCache.getPdxSerializer()).isEqualTo(mockPdxSerializer);
}
@Test
public void redisServerGemFirePropertiesConfiguration() {
@@ -515,7 +551,7 @@ public class EnableGemFirePropertiesIntegrationTests {
@EnableGemFireMockObjects
@PeerCacheApplication
@EnableGemFireProperties(name = "TestName", groups = { "TestGroupOne", "TestGroupTwo" })
static class TestNameAndGroupGemFirePropertiesAnnotationConfiguration { }
static class TestNameAndGroupsAnnotationBasedGemFirePropertiesConfiguration { }
@EnableGemFireMockObjects
@PeerCacheApplication
@@ -523,6 +559,18 @@ public class EnableGemFirePropertiesIntegrationTests {
@EnableOffHeap(memorySize = "64g")
static class TestOffHeapGemFirePropertiesConfiguration { }
@EnableGemFireMockObjects
@PeerCacheApplication
@EnablePdx
@SuppressWarnings("unused")
static class TestPdxGemFirePropertiesConfiguration {
@Bean
PdxSerializer mockPdxSerializer() {
return mock(PdxSerializer.class);
}
}
@EnableGemFireMockObjects
@PeerCacheApplication
@EnableGemFireProperties

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.pdx.PdxSerializer;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.DiskStoreFactoryBean;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
/**
* The EnablePdxConfigurationIntegrationTests class...
*
* @author John Blum
* @since 1.0.0
*/
public class EnablePdxConfigurationIntegrationTests {
@Autowired
private ConfigurableApplicationContext applicationContext;
@After
public void tearDown() {
Optional.ofNullable(this.applicationContext).ifPresent(ConfigurableApplicationContext::close);
}
private ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
return new AnnotationConfigApplicationContext(annotatedClasses);
}
@Test
public void regionBeanDefinitionDependsOnPdxDiskStoreBean() {
this.applicationContext = newApplicationContext(TestEnablePdxWithDiskStoreConfiguration.class);
assertThat(this.applicationContext).isNotNull();
assertThat(this.applicationContext.containsBean("gemfireCache")).isTrue();
assertThat(this.applicationContext.containsBean("MockPdxSerializer")).isTrue();
assertThat(this.applicationContext.containsBean("TestDiskStore")).isTrue();
assertThat(this.applicationContext.containsBean("TestRegion")).isTrue();
CacheFactoryBean gemfireCache = this.applicationContext.getBean("&gemfireCache", CacheFactoryBean.class);
assertThat(gemfireCache).isNotNull();
assertThat(gemfireCache.getPdxSerializer())
.isEqualTo(this.applicationContext.getBean("MockPdxSerializer", PdxSerializer.class));
BeanDefinition testDiskStoreBeanDefinition =
this.applicationContext.getBeanFactory().getBeanDefinition("TestDiskStore");
assertThat(testDiskStoreBeanDefinition).isNotNull();
assertThat(testDiskStoreBeanDefinition.getDependsOn()).isNullOrEmpty();
BeanDefinition testRegionBeanDefinition =
this.applicationContext.getBeanFactory().getBeanDefinition("TestRegion");
assertThat(testRegionBeanDefinition).isNotNull();
assertThat(testRegionBeanDefinition.getDependsOn()).containsExactly("TestDiskStore");
}
@Test
public void regionBeanDefinitionHasNoDependencies() {
this.applicationContext = newApplicationContext(TestEnablePdxConfigurationWithNoDiskStoreConfiguration.class);
assertThat(this.applicationContext).isNotNull();
assertThat(this.applicationContext.containsBean("gemfireCache")).isTrue();
assertThat(this.applicationContext.containsBean("TestDiskStore")).isTrue();
assertThat(this.applicationContext.containsBean("TestRegion")).isTrue();
CacheFactoryBean gemfireCache = this.applicationContext.getBean("&gemfireCache", CacheFactoryBean.class);
assertThat(gemfireCache).isNotNull();
assertThat(gemfireCache.getPdxSerializer()).isInstanceOf(MappingPdxSerializer.class);
BeanDefinition testDiskStoreBeanDefinition =
this.applicationContext.getBeanFactory().getBeanDefinition("TestDiskStore");
assertThat(testDiskStoreBeanDefinition).isNotNull();
assertThat(testDiskStoreBeanDefinition.getDependsOn()).isNullOrEmpty();
BeanDefinition testRegionBeanDefinition =
this.applicationContext.getBeanFactory().getBeanDefinition("TestRegion");
assertThat(testRegionBeanDefinition).isNotNull();
assertThat(testRegionBeanDefinition.getDependsOn()).isNullOrEmpty();
}
@EnableGemFireMockObjects
@PeerCacheApplication
@EnablePdx(diskStoreName = "TestDiskStore", serializerBeanName = "MockPdxSerializer")
@SuppressWarnings("unused")
static class TestEnablePdxWithDiskStoreConfiguration {
@Bean("TestDiskStore")
DiskStoreFactoryBean testPdxDiskStore(GemFireCache gemfireCache) {
DiskStoreFactoryBean testDiskStore = new DiskStoreFactoryBean();
testDiskStore.setCache(gemfireCache);
return testDiskStore;
}
@Bean("TestRegion")
LocalRegionFactoryBean<Object, Object> testRegion(GemFireCache gemfireCache) {
LocalRegionFactoryBean<Object, Object> testRegion = new LocalRegionFactoryBean<>();
testRegion.setCache(gemfireCache);
testRegion.setClose(false);
testRegion.setPersistent(false);
return testRegion;
}
@Bean("MockPdxSerializer")
PdxSerializer mockPdxSerializer() {
return mock(PdxSerializer.class);
}
}
@EnableGemFireMockObjects
@ClientCacheApplication
@EnablePdx
@SuppressWarnings("unused")
static class TestEnablePdxConfigurationWithNoDiskStoreConfiguration {
@Bean("TestDiskStore")
DiskStoreFactoryBean testPdxDiskStore(GemFireCache gemfireCache) {
DiskStoreFactoryBean testDiskStore = new DiskStoreFactoryBean();
testDiskStore.setCache(gemfireCache);
return testDiskStore;
}
@Bean("TestRegion")
public ClientRegionFactoryBean<Object, Object> testRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> testRegion = new ClientRegionFactoryBean<>();
testRegion.setCache(gemfireCache);
testRegion.setClose(false);
testRegion.setShortcut(ClientRegionShortcut.LOCAL);
return testRegion;
}
}
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
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.atLeastOnce;
import static org.mockito.Mockito.doReturn;
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 java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.apache.geode.pdx.PdxSerializer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.MappingPdxSerializer;
import org.springframework.util.MethodInvoker;
/**
* Unit tests for {@link EnablePdx} and {@link PdxConfiguration}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Spy
* @see org.apache.geode.pdx.PdxSerializer
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnablePdx
* @see org.springframework.data.gemfire.config.annotation.PdxConfiguration
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class EnablePdxConfigurationUnitTests {
@Spy
private PdxConfiguration pdxConfiguration;
@SuppressWarnings("unchecked")
private <T> T invokeMethod(Object obj, String methodName) throws Exception {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(obj);
methodInvoker.setTargetMethod(methodName);
methodInvoker.prepare();
return (T) methodInvoker.invoke();
}
@Test
public void setImportMetadataWhenEnablePdxIsConfigured() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
BeanFactory mockBeanFactory = mock(BeanFactory.class);
Map<String, Object> annotationAttributes = new HashMap<>(5);
annotationAttributes.put("diskStoreName", "MockDiskStore");
annotationAttributes.put("ignoreUnreadFields", Boolean.TRUE);
annotationAttributes.put("persistent", Boolean.TRUE);
annotationAttributes.put("readSerialized", Boolean.TRUE);
annotationAttributes.put("serializerBeanName", "MockPdxSerializer");
when(mockAnnotationMetadata.hasAnnotation(eq(EnablePdx.class.getName()))).thenReturn(true);
when(mockAnnotationMetadata.getAnnotationAttributes(eq(EnablePdx.class.getName())))
.thenReturn(annotationAttributes);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
this.pdxConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.getDiskStoreName().orElse(null)).isEqualTo("MockDiskStore");
assertThat(this.pdxConfiguration.isIgnoreUnreadFields()).isTrue();
assertThat(this.pdxConfiguration.isPersistent()).isTrue();
assertThat(this.pdxConfiguration.isReadSerialized()).isTrue();
assertThat(this.pdxConfiguration.getSerializerBeanName().orElse(null)).isEqualTo("MockPdxSerializer");
verify(mockAnnotationMetadata, times(1)).hasAnnotation(eq(EnablePdx.class.getName()));
verify(mockAnnotationMetadata, times(1))
.getAnnotationAttributes(eq(EnablePdx.class.getName()));
}
@Test
public void setImportMetadataWhenEnablePdxIsNotConfigured() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
when(mockAnnotationMetadata.hasAnnotation(anyString())).thenReturn(false);
this.pdxConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(this.pdxConfiguration.getDiskStoreName().isPresent()).isFalse();
assertThat(this.pdxConfiguration.isIgnoreUnreadFields()).isFalse();
assertThat(this.pdxConfiguration.isPersistent()).isFalse();
assertThat(this.pdxConfiguration.isReadSerialized()).isFalse();
assertThat(this.pdxConfiguration.getSerializerBeanName().isPresent()).isFalse();
verify(mockAnnotationMetadata, times(1)).hasAnnotation(eq(EnablePdx.class.getName()));
verifyNoMoreInteractions(mockAnnotationMetadata);
}
@Test
public void configuresPdxForCacheFactoryBean() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
PdxSerializer mockPdxSerializer = mock(PdxSerializer.class);
when(mockBeanFactory.containsBean(eq("MockPdxSerializer"))).thenReturn(true);
when(mockBeanFactory.getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class)))
.thenReturn(mockPdxSerializer);
doReturn(Optional.of("MockPdxDiskStore")).when(this.pdxConfiguration).getDiskStoreName();
doReturn(true).when(this.pdxConfiguration).isIgnoreUnreadFields();
doReturn(true).when(this.pdxConfiguration).isPersistent();
doReturn(true).when(this.pdxConfiguration).isReadSerialized();
doReturn(Optional.of("MockPdxSerializer")).when(this.pdxConfiguration).getSerializerBeanName();
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
this.pdxConfiguration.configurePdx(cacheFactoryBean);
assertThat(cacheFactoryBean.getPdxDiskStoreName()).isEqualTo("MockPdxDiskStore");
assertThat(cacheFactoryBean.getPdxIgnoreUnreadFields()).isTrue();
assertThat(cacheFactoryBean.getPdxPersistent()).isTrue();
assertThat(cacheFactoryBean.getPdxReadSerialized()).isTrue();
assertThat(cacheFactoryBean.getPdxSerializer()).isEqualTo(mockPdxSerializer);
verify(mockBeanFactory, times(1)).containsBean(eq("MockPdxSerializer"));
verify(mockBeanFactory, times(1))
.getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class));
verify(this.pdxConfiguration, times(1)).getDiskStoreName();
verify(this.pdxConfiguration, times(1)).isIgnoreUnreadFields();
verify(this.pdxConfiguration, times(1)).isPersistent();
verify(this.pdxConfiguration, times(1)).isReadSerialized();
verify(this.pdxConfiguration, times(1)).getSerializerBeanName();
}
@Test
public void resolveConversionServiceWithNonConfigurableBeanFactoryReturnsNull() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.resolveConversionService().isPresent()).isFalse();
verifyZeroInteractions(mockBeanFactory);
}
@Test
public void resolveConversionServiceReturnsConfiguredConversionService() {
ConfigurableBeanFactory mockBeanFactory = mock(ConfigurableBeanFactory.class);
ConversionService mockConversionService = mock(ConversionService.class);
when(mockBeanFactory.getConversionService()).thenReturn(mockConversionService);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.resolveConversionService().orElse(null))
.isEqualTo(mockConversionService);
verify(mockBeanFactory, atLeastOnce()).getConversionService();
}
@Test
@SuppressWarnings("unchecked")
public void resolveMappingContextWhenBeanFactoryDoesNotContainMappingContextBeanReturnsNull() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
when(mockBeanFactory.getBean(any(Class.class))).thenThrow(new NoSuchBeanDefinitionException("test"));
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.resolveMappingContext().isPresent()).isFalse();
verify(mockBeanFactory, times(1)).getBean(eq(GemfireMappingContext.class));
}
@Test
public void resolveMappingContextReturnsConfiguredMappingContext() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class);
when(mockBeanFactory.getBean(eq(GemfireMappingContext.class))).thenReturn(mockMappingContext);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.resolveMappingContext().orElse(null)).isEqualTo(mockMappingContext);
verify(mockBeanFactory, times(1)).getBean(eq(GemfireMappingContext.class));
}
@Test
public void resolvePdxSerializerWhenBeanFactoryDoesNotContainPdxSerializerBeanReturnsMappingPdxSerializer() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
when(mockBeanFactory.containsBean(anyString())).thenReturn(false);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
this.pdxConfiguration.setSerializerBeanName("MockPdxSerializer");
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.getSerializerBeanName().orElse(null)).isEqualTo("MockPdxSerializer");
assertThat(this.pdxConfiguration.resolvePdxSerializer()).isInstanceOf(MappingPdxSerializer.class);
verify(mockBeanFactory, times(1)).containsBean(eq("MockPdxSerializer"));
verify(mockBeanFactory, never()).getBean(anyString(), any(PdxSerializer.class));
}
@Test
public void resolvePdxSerializerReturnsConfiguredPdxSerializer() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
PdxSerializer mockPdxSerializer = mock(PdxSerializer.class);
when(mockBeanFactory.containsBean(eq("MockPdxSerializer"))).thenReturn(true);
when(mockBeanFactory.getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class)))
.thenReturn(mockPdxSerializer);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
this.pdxConfiguration.setSerializerBeanName("MockPdxSerializer");
assertThat(this.pdxConfiguration.getBeanFactory()).isEqualTo(mockBeanFactory);
assertThat(this.pdxConfiguration.getSerializerBeanName().orElse(null)).isEqualTo("MockPdxSerializer");
assertThat(this.pdxConfiguration.resolvePdxSerializer()).isEqualTo(mockPdxSerializer);
verify(mockBeanFactory, times(1)).containsBean(eq("MockPdxSerializer"));
verify(mockBeanFactory, times(1))
.getBean(eq("MockPdxSerializer"), eq(PdxSerializer.class));
}
@Test
public void newPdxSerializerReturnsMappingPdxSerializerConfiguredWithResolvedConversionServiceAndMappingContext()
throws Exception {
ConfigurableBeanFactory mockBeanFactory = mock(ConfigurableBeanFactory.class);
ConversionService mockConversionService = mock(ConversionService.class);
GemfireMappingContext mockMappingContext = mock(GemfireMappingContext.class);
when(mockBeanFactory.getBean(eq(GemfireMappingContext.class))).thenReturn(mockMappingContext);
when(mockBeanFactory.getConversionService()).thenReturn(mockConversionService);
this.pdxConfiguration.setBeanFactory(mockBeanFactory);
PdxSerializer pdxSerializer = this.pdxConfiguration.newPdxSerializer();
assertThat(pdxSerializer).isInstanceOf(MappingPdxSerializer.class);
assertThat(this.<Object>invokeMethod(pdxSerializer, "getConversionService"))
.isEqualTo(mockConversionService);
assertThat(this.<Object>invokeMethod(pdxSerializer, "getMappingContext"))
.isEqualTo(mockMappingContext);
verify(mockBeanFactory, times(1)).getBean(eq(GemfireMappingContext.class));
verify(mockBeanFactory, atLeastOnce()).getConversionService();
}
}