diff --git a/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/DistributedSystemIdConfiguration.java b/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/DistributedSystemIdConfiguration.java new file mode 100644 index 00000000..2c39b9a0 --- /dev/null +++ b/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/DistributedSystemIdConfiguration.java @@ -0,0 +1,131 @@ +/* + * 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.geode.config.annotation; + +import java.lang.annotation.Annotation; +import java.util.Optional; + +import org.apache.geode.cache.Cache; +import org.apache.shiro.util.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.type.AnnotationMetadata; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; +import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer; +import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport; + +/** + * The {@link DistributedSystemIdConfiguration} class is a Spring {@link Configuration} class used to configure + * the {@literal distributed-system-id} for a {@link Cache peer Cache member} in a cluster + * when using the P2P topology. + * + * @author John Blum + * @see org.apache.geode.cache.Cache + * @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.config.annotation.ClientCacheConfigurer + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer + * @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport + * @see org.springframework.geode.config.annotation.UseDistributedSystemId + * @since 1.0.0 + */ +@Configuration +@SuppressWarnings("unused") +public class DistributedSystemIdConfiguration extends AbstractAnnotationConfigSupport implements ImportAware { + + private static final String GEMFIRE_DISTRIBUTED_SYSTEM_ID_PROPERTY = "distributed-system-id"; + + private Integer distributedSystemId; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + protected Class getAnnotationType() { + return UseDistributedSystemId.class; + } + + @Override + @SuppressWarnings("all") + public void setImportMetadata(AnnotationMetadata importMetadata) { + + if (isAnnotationPresent(importMetadata)) { + + AnnotationAttributes distributedSystemIdAttributes = getAnnotationAttributes(importMetadata); + + setDistributedSystemId(distributedSystemIdAttributes.containsKey("value") + ? distributedSystemIdAttributes.getNumber("value") : null); + + setDistributedSystemId(distributedSystemIdAttributes.containsKey("id") + ? distributedSystemIdAttributes.getNumber("id") : null); + } + } + + protected void setDistributedSystemId(Integer distributedSystemId) { + + this.distributedSystemId = Optional.ofNullable(distributedSystemId) + .filter(id -> id > -1) + .orElse(this.distributedSystemId); + } + + protected Optional getDistributedSystemId() { + + return Optional.ofNullable(this.distributedSystemId) + .filter(id -> id > -1); + } + + protected Logger getLogger() { + return this.logger; + } + + private int validateDistributedSystemId(int distributedSystemId) { + + Assert.isTrue(distributedSystemId >= -1 && distributedSystemId < 256, + String.format("Distributed System ID [%d] must be between -1 and 255", distributedSystemId)); + + return distributedSystemId; + } + + @Bean + ClientCacheConfigurer clientCacheDistributedSystemIdConfigurer() { + + return (beanName, clientCacheFactoryBean) -> getDistributedSystemId().ifPresent(distributedSystemId -> { + + Logger logger = getLogger(); + + if (logger.isWarnEnabled()) { + logger.warn("Distributed System Id [{}] was set on the ClientCache instance, which will not have any effect", + distributedSystemId); + } + }); + } + + @Bean + PeerCacheConfigurer peerCacheDistributedSystemIdConfigurer() { + + return (beanName, cacheFactoryBean) -> + getDistributedSystemId().ifPresent(id -> cacheFactoryBean.getProperties() + .setProperty(GEMFIRE_DISTRIBUTED_SYSTEM_ID_PROPERTY, + String.valueOf(validateDistributedSystemId(id)))); + } +} diff --git a/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/UseDistributedSystemId.java b/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/UseDistributedSystemId.java new file mode 100644 index 00000000..2f1da693 --- /dev/null +++ b/geode-spring-boot/src/main/java/org/springframework/geode/config/annotation/UseDistributedSystemId.java @@ -0,0 +1,81 @@ +/* + * 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.geode.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.client.ClientCache; +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.AliasFor; + +/** + * The {@link UseDistributedSystemId} annotation configures the {@literal distributed-system-id} property + * of a {@link Cache peer Cache member} in an Apache Geode/Pivotal GemFire P2P topology. + * + * This configuration annotation is only applicable on {@link Cache peer Cache members} + * and has no effect on {@link ClientCache} instances. + * + * @author John Blum + * @see java.lang.annotation.Documented + * @see java.lang.annotation.Inherited + * @see java.lang.annotation.Retention + * @see java.lang.annotation.Target + * @see org.apache.geode.cache.Cache + * @see org.springframework.context.annotation.Import + * @see org.springframework.core.annotation.AliasFor + * @see org.springframework.geode.config.annotation.DistributedSystemIdConfiguration + * @since 1.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Import(DistributedSystemIdConfiguration.class) +@SuppressWarnings("unused") +public @interface UseDistributedSystemId { + + /** + * Configures the identifier used to distinguish messages from different distributed systems. + * + * This is required for Portable Data eXchange (PDX) data serialization. + * + * Set distributed-system-id to different values for different systems in a multi-site (WAN) configuration, + * and to different values for production vs. development environments. This setting must be the same + * for every member of a given distributed system and unique to each distributed system within a WAN installation. + * + * Valid values are integers in the range -1…255. -1 means no setting. + * + * Defaults to {@literal -1}. + */ + @AliasFor("id") + int value() default -1; + + /** + * Configures the identifier used to distinguish messages from different distributed systems. + * + * Alias for {@literal #value()}. + */ + @AliasFor("value") + int id() default -1; + +} diff --git a/geode-spring-boot/src/test/java/org/springframework/geode/config/annotation/DistributedSystemIdConfigurationIntegrationTests.java b/geode-spring-boot/src/test/java/org/springframework/geode/config/annotation/DistributedSystemIdConfigurationIntegrationTests.java new file mode 100644 index 00000000..c5b824a3 --- /dev/null +++ b/geode-spring-boot/src/test/java/org/springframework/geode/config/annotation/DistributedSystemIdConfigurationIntegrationTests.java @@ -0,0 +1,69 @@ +/* + * 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.geode.config.annotation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.geode.cache.GemFireCache; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.config.annotation.PeerCacheApplication; +import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport; +import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests for {@link UseDistributedSystemId} and {@link DistributedSystemIdConfiguration}. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache + * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication + * @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport + * @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects + * @see org.springframework.geode.config.annotation.DistributedSystemIdConfiguration + * @see org.springframework.geode.config.annotation.UseDistributedSystemId + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 1.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class DistributedSystemIdConfigurationIntegrationTests extends IntegrationTestsSupport { + + @Autowired + private GemFireCache gemfireCache; + + @Test + public void distributedSystemIdWasConfiguredCorrectly() { + + assertThat(this.gemfireCache).isNotNull(); + assertThat(this.gemfireCache.getDistributedSystem()).isNotNull(); + assertThat(this.gemfireCache.getDistributedSystem().getProperties()).isNotNull(); + assertThat(this.gemfireCache.getDistributedSystem().getProperties().getProperty("distributed-system-id")) + .isEqualTo("42"); + } + + @PeerCacheApplication + @EnableGemFireMockObjects + @UseDistributedSystemId(42) + static class TestConfiguration { } + +}