diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableGemFireProperties.java b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableGemFireProperties.java index 2abefeae..bc562ae3 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/EnableGemFireProperties.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/EnableGemFireProperties.java @@ -115,14 +115,14 @@ public @interface EnableGemFireProperties { * * Defaults to unset. */ - String bindAddress() default ""; + String bindAddress() default GemFirePropertiesConfiguration.DEFAULT_BIND_ADDRESS; /** * Declarative initialization file for the member’s cache. * * Defaults to unset. */ - String cacheXmlFile() default ""; + String cacheXmlFile() default GemFirePropertiesConfiguration.DEFAULT_CACHE_XML_FILE; /** * This property specifies the directory in which the cluster configuration related disk-store and artifacts @@ -131,7 +131,8 @@ public @interface EnableGemFireProperties { * * Defaults to unset. */ - String clusterConfigurationDirectory() default ""; + String clusterConfigurationDirectory() + default GemFirePropertiesConfiguration.DEFAULT_CLUSTER_CONFIGURATION_DIRECTORY; /** * Used only by clients in a client/server installation. This is a client-side property @@ -204,7 +205,8 @@ public @interface EnableGemFireProperties { * * Defaults to {@literal false}. */ - boolean enableNetworkPartitionDetection() default GemFirePropertiesConfiguration.DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION; + boolean enableNetworkPartitionDetection() + default GemFirePropertiesConfiguration.DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION; /** * Whether partitioned regions will put redundant copies of the same data in different members @@ -232,7 +234,8 @@ public @interface EnableGemFireProperties { * * Defaults to {@literal false}. */ - boolean loadClusterConfigurationFromDirectory() default GemFirePropertiesConfiguration.DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY; + boolean loadClusterConfigurationFromDirectory() + default GemFirePropertiesConfiguration.DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY; /** * The number of seconds that a member should wait for a Locator to start if a Locator is not available @@ -302,6 +305,14 @@ public @interface EnableGemFireProperties { */ String membershipPortRange() default GemFirePropertiesConfiguration.DEFAULT_MEMBERSHIP_PORT_RANGE; + /** + * Configures the {@link String name} of this member in the Apache Geode/Pivotal GemFire distributed system + * whether the member is a client in the client/server topology or a peer in the cluster. + * + * Defaults to unset. + */ + String name() default GemFirePropertiesConfiguration.DEFAULT_NAME; + /** * Defines this member’s redundancy zone. Used to separate member’s into different groups for satisfying * Partitioned Region redundancy. If this property is set, GemFire will not put redundant copies of data @@ -311,7 +322,7 @@ public @interface EnableGemFireProperties { * * @see Configure High Availability for a Partitioned Region */ - String redundancyZone() default ""; + String redundancyZone() default GemFirePropertiesConfiguration.DEFAULT_REDUNDANCY_ZONE; /** * Used to configure the Locators that a cluster will use in order to connect to a remote site in a multi-site @@ -338,7 +349,7 @@ public @interface EnableGemFireProperties { * * Defaults to unset. */ - String remoteLocators() default ""; + String remoteLocators() default GemFirePropertiesConfiguration.DEFAULT_REMOTE_LOCATORS; /** * When this property is set to {@literal true}, the primary server drops unresponsive clients @@ -350,6 +361,17 @@ public @interface EnableGemFireProperties { */ boolean removeUnresponsiveClient() default GemFirePropertiesConfiguration.DEFAULT_REMOVE_UNRESPONSIVE_CLIENT; + /** + * A semicolon-separated list of items that become full class names of objects that the system will serialize + * when the property {@link #validateSerializableObjects()} is set to {@literal true}. + * + * The list is expanded using the patterns specified in the createFilter method at + * Class ObjectInputFilter.Config. + * + * Defaults to unset. + */ + String serializableObjectFilter() default GemFirePropertiesConfiguration.DEFAULT_SERIALIZABLE_OBJECT_FILTER; + /** * Receive buffer sizes in bytes of the TCP/IP connections used for data transmission. To minimize the buffer size * allocation needed for distributing large, serializable messages, the messages are sent in chunks. This setting @@ -433,6 +455,19 @@ public @interface EnableGemFireProperties { * * Defaults to unset. */ - String userCommandPackages() default ""; + String userCommandPackages() default GemFirePropertiesConfiguration.DEFAULT_USER_COMMAND_PACKAGES; + + /** + * A boolean that defaults to {@literal false}. When {@literal true}, instances of classes that are not internal + * to Apache Geode/Pivotal GemFire and whose class name is not allowed by the list defined in + * the {@link #serializableObjectFilter()} property will not be permitted to be deserialized. + * + * An {@literal IncompatibleClassException} is thrown for objects not listed. JDK 8 build 121 or a later build + * must be installed to use this property. Servers and clients that do not meet this requirement + * will throw an exception upon startup. + * + * Defaults to {@literal false}. + */ + boolean validateSerializableObjects() default GemFirePropertiesConfiguration.DEFAULT_VALIDATE_SERIALIZABLE_OBJECTS; } diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/GemFirePropertiesConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/GemFirePropertiesConfiguration.java index eaf234f6..c18e03db 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/GemFirePropertiesConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/GemFirePropertiesConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.data.gemfire.config.annotation; +import java.lang.annotation.Annotation; import java.util.Map; import java.util.Properties; @@ -44,6 +45,7 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration public static final boolean DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY = false; public static final boolean DEFAULT_LOCK_MEMORY = false; public static final boolean DEFAULT_REMOVE_UNRESPONSIVE_CLIENT = false; + public static final boolean DEFAULT_VALIDATE_SERIALIZABLE_OBJECTS = false; public static final int DEFAULT_ACK_SEVERE_ALERT_THRESHOLD = 0; public static final int DEFAULT_ACK_WAIT_THRESHOLD = 15; @@ -63,15 +65,26 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration public static final long DEFAULT_MEMBER_TIMEOUT = 5000L; public static final long DEFAULT_SOCKET_LEASE_TIME = 60000L; + public static final String DEFAULT_BIND_ADDRESS = ""; + public static final String DEFAULT_CACHE_XML_FILE = ""; + public static final String DEFAULT_CLUSTER_CONFIGURATION_DIRECTORY = ""; public static final String DEFAULT_CONFLATE_EVENTS = "server"; public static final String DEFAULT_DEPLOY_WORKING_DIRECTORY = "."; public static final String DEFAULT_MEMBERSHIP_PORT_RANGE = "1024-65535"; + public static final String DEFAULT_NAME = ""; + public static final String DEFAULT_REDUNDANCY_ZONE = ""; + public static final String DEFAULT_REMOTE_LOCATORS = ""; + public static final String DEFAULT_SERIALIZABLE_OBJECT_FILTER = ""; + public static final String DEFAULT_USER_COMMAND_PACKAGES = ""; + + @SuppressWarnings("unused") + public static final String[] DEFAULT_GROUPS = {}; /** * {@inheritDoc} */ @Override - protected Class getAnnotationType() { + protected Class getAnnotationType() { return EnableGemFireProperties.class; } @@ -80,6 +93,7 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration */ @Override protected Properties toGemFireProperties(Map annotationAttributes) { + PropertiesBuilder gemfireProperties = new PropertiesBuilder(); gemfireProperties.setPropertyIfNotDefault("ack-severe-alert-threshold", @@ -149,6 +163,8 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration gemfireProperties.setPropertyIfNotDefault("membership-port-range", annotationAttributes.get("membershipPortRange"), DEFAULT_MEMBERSHIP_PORT_RANGE); + gemfireProperties.setProperty("name", annotationAttributes.get("name")); + gemfireProperties.setProperty("redundancy-zone", annotationAttributes.get("redundancyZone")); gemfireProperties.setProperty("remote-locators", annotationAttributes.get("remoteLocators")); @@ -156,13 +172,17 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration gemfireProperties.setPropertyIfNotDefault("remove-unresponsive-client", annotationAttributes.get("removeUnresponsiveClient"), DEFAULT_REMOVE_UNRESPONSIVE_CLIENT); + gemfireProperties.setProperty("serializable-object-filter", + annotationAttributes.get("serializableObjectFilter")); + gemfireProperties.setPropertyIfNotDefault("socket-buffer-size", annotationAttributes.get("socketBufferSize"), DEFAULT_SOCKET_BUFFER_SIZE); gemfireProperties.setPropertyIfNotDefault("socket-lease-time", annotationAttributes.get("socketLeaseTime"), DEFAULT_SOCKET_LEASE_TIME); - gemfireProperties.setPropertyIfNotDefault("tcp-port", annotationAttributes.get("tcpPort"), DEFAULT_TCP_PORT); + gemfireProperties.setPropertyIfNotDefault("tcp-port", + annotationAttributes.get("tcpPort"), DEFAULT_TCP_PORT); gemfireProperties.setPropertyIfNotDefault("tombstone-gc-threshold", annotationAttributes.get("tombstoneGcThreshold"), DEFAULT_TOMBSTONE_THRESHOLD); @@ -178,6 +198,9 @@ public class GemFirePropertiesConfiguration extends EmbeddedServiceConfiguration gemfireProperties.setProperty("user-command-packages", annotationAttributes.get("userCommandPackages")); + gemfireProperties.setPropertyIfNotDefault("validate-serializable-objects", + annotationAttributes.get("validateSerializableObjects"), DEFAULT_VALIDATE_SERIALIZABLE_OBJECTS); + return gemfireProperties.build(); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java index 6e5120a9..1593eb59 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/EmbeddedServiceConfigurationSupport.java @@ -97,13 +97,11 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota } } - /* (non-Javadoc) */ @SuppressWarnings("unused") protected void registerBeanDefinitions(AnnotationMetadata importingClassMetaData, Map annotationAttributes, BeanDefinitionRegistry registry) { } - /* (non-Javadoc) */ protected void setGemFireProperties(AnnotationMetadata importingClassMetadata, Map annotationAttributes, BeanDefinitionRegistry registry) { @@ -119,20 +117,16 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota } } - /* (non-Javadoc) */ protected abstract Properties toGemFireProperties(Map annotationAttributes); - /* (non-Javadoc) */ protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) { return importingClassMetadata.hasAnnotation(getAnnotationTypeName()); } - /* (non-Javadoc) */ protected boolean hasProperties(Properties properties) { return !CollectionUtils.isEmpty(properties); } - /* (non-Javadoc) */ protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry, Properties customGemFireProperties) { @@ -144,22 +138,18 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota BeanDefinitionReaderUtils.registerBeanDefinition(newBeanDefinitionHolder(builder), registry); } - /* (non-Javadoc) */ protected BeanDefinitionHolder newBeanDefinitionHolder(BeanDefinitionBuilder builder) { return new BeanDefinitionHolder(builder.getBeanDefinition(), generateBeanName()); } - /* (non-Javadoc) */ protected String generateBeanName() { return generateBeanName(getAnnotationType()); } - /* (non-Javadoc) */ protected String generateBeanName(Class typeQualifier) { return generateBeanName(typeQualifier.getSimpleName()); } - /* (non-Javadoc) */ protected String generateBeanName(String nameQualifier) { return String.format("%1$s.%2$s", getClass().getName(), nameQualifier); } @@ -199,22 +189,18 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota } } - /* (non-Javadoc) */ protected String resolveHost(String hostname) { return resolveHost(hostname, DEFAULT_HOST); } - /* (non-Javadoc) */ protected String resolveHost(String hostname, String defaultHostname) { return Optional.ofNullable(hostname).filter(StringUtils::hasText).orElse(defaultHostname); } - /* (non-Javadoc) */ protected Integer resolvePort(Integer port) { return resolvePort(port, DEFAULT_PORT); } - /* (non-Javadoc) */ protected Integer resolvePort(Integer port, Integer defaultPort) { return Optional.ofNullable(port).orElse(defaultPort); } @@ -240,7 +226,9 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota * @see java.util.Properties */ protected GemFirePropertiesBeanPostProcessor(Properties gemfireProperties) { + Assert.notEmpty(gemfireProperties, "GemFire Properties are required"); + this.gemfireProperties = gemfireProperties; } @@ -251,7 +239,9 @@ public abstract class EmbeddedServiceConfigurationSupport extends AbstractAnnota public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Properties && GEMFIRE_PROPERTIES_BEAN_NAME.equals(beanName)) { + Properties gemfirePropertiesBean = (Properties) bean; + gemfirePropertiesBean.putAll(gemfireProperties); } diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableGemFirePropertiesIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableGemFirePropertiesIntegrationTests.java index 6bf619fa..9fe69e95 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableGemFirePropertiesIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableGemFirePropertiesIntegrationTests.java @@ -57,14 +57,21 @@ public class EnableGemFirePropertiesIntegrationTests { Optional.ofNullable(this.applicationContext).ifPresent(ConfigurableApplicationContext::close); } + private ConfigurableApplicationContext newApplicationContext(Class... annotatedClasses) { + return newApplicationContext(null, annotatedClasses); + } + private ConfigurableApplicationContext newApplicationContext(PropertySource testPropertySource, Class... annotatedClasses) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources(); + Optional.ofNullable(testPropertySource).ifPresent(it -> { - propertySources.addFirst(testPropertySource); + MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources(); + + propertySources.addFirst(testPropertySource); + }); applicationContext.registerShutdownHook(); applicationContext.register(annotatedClasses); @@ -259,6 +266,36 @@ public class EnableGemFirePropertiesIntegrationTests { assertThat(gemfireProperties.getProperty("memcached-protocol")).isEqualTo("BINARY"); } + @Test + public void nameAndGroupGemFirePropertiesAnnotationConfiguration() { + + this.applicationContext = newApplicationContext(TestNameAndGroupGemFirePropertiesAnnotationConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + assertThat(this.applicationContext.containsBean("gemfireCache")).isTrue(); + assertThat(this.applicationContext.containsBean("gemfireProperties")).isTrue(); + + Properties gemfireProperties = this.applicationContext.getBean("gemfireProperties", Properties.class); + + + // TODO: uncomment when Spring Test for Apache Geode/Pivotal GemFire project replaces + // the test infrastructure classes in SDG. + /* + GemFireCache gemfireCache = this.applicationContext.getBean("gemfireCache", GemFireCache.class); + + assertThat(gemfireCache).isNotNull(); + assertThat(gemfireCache.getDistributedSystem()).isNotNull(); + + Properties gemfireProperties = gemfireCache.getDistributedSystem().getProperties(); + */ + + assertThat(gemfireProperties).isNotNull(); + assertThat(gemfireProperties.containsKey("name")).isTrue(); + assertThat(gemfireProperties.getProperty("name")).isEqualTo("TestName"); + assertThat(gemfireProperties.containsKey("groups")).isTrue(); + assertThat(gemfireProperties.getProperty("groups")).isEqualTo("TestGroupOne,TestGroupTwo"); + } + @Test public void offHeapGemFirePropertiesConfiguration() { @@ -422,64 +459,60 @@ public class EnableGemFirePropertiesIntegrationTests { @PeerCacheApplication @EnableAuth @EnableGemFireProperties - static class TestAuthGemFirePropertiesConfiguration { - } + static class TestAuthGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableHttpService - static class TestHttpGemFirePropertiesConfiguration { - } + static class TestHttpGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableLocator - static class TestLocatorGemFirePropertiesConfiguration { - } + static class TestLocatorGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableLogging - static class TestLoggingGemFirePropertiesConfiguration { - } + static class TestLoggingGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableManager - static class TestManagerGemFirePropertiesConfiguration { - } + static class TestManagerGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableMemcachedServer - static class TestMemcachedServerGemFirePropertiesConfiguration { - } + static class TestMemcachedServerGemFirePropertiesConfiguration { } + + @EnableGemFireMockObjects + @PeerCacheApplication + @EnableGemFireProperties(name = "TestName", groups = { "TestGroupOne", "TestGroupTwo" }) + static class TestNameAndGroupGemFirePropertiesAnnotationConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableOffHeap(memorySize = "64g") - static class TestOffHeapGemFirePropertiesConfiguration { - } + static class TestOffHeapGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableRedisServer - static class TestRedisServerGemFirePropertiesConfiguration { - } + static class TestRedisServerGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableSecurity - static class TestSecurityGemFirePropertiesConfiguration { - } + static class TestSecurityGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @@ -491,13 +524,12 @@ public class EnableGemFirePropertiesIntegrationTests { @EnableSsl.ComponentAlias(component = EnableSsl.Component.GATEWAY, alias = "WanCert"), @EnableSsl.ComponentAlias(component = EnableSsl.Component.WEB, alias = "HttpCert") }, defaultCertificateAlias = "MockCert", protocols = "HTTP") - static class TestSslGemFirePropertiesConfiguration { - } + static class TestSslGemFirePropertiesConfiguration { } @EnableGemFireMockObjects @PeerCacheApplication @EnableGemFireProperties @EnableStatistics - static class TestStatisticsGemFirePropertiesConfiguration { - } + static class TestStatisticsGemFirePropertiesConfiguration { } + }