diff --git a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/CacheNameAutoConfiguration.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/CacheNameAutoConfiguration.java new file mode 100644 index 00000000..3adb6bf1 --- /dev/null +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/CacheNameAutoConfiguration.java @@ -0,0 +1,110 @@ +/* + * 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.boot.autoconfigure; + +import java.util.Optional; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.client.ClientCache; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; +import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer; +import org.springframework.geode.boot.autoconfigure.condition.ConditionalOnMissingProperty; +import org.springframework.util.StringUtils; + +/** + * Spring Boot {@link EnableAutoConfiguration auto-configuration} class used to configure the Apache Geode + * / Pivotal GemFire {@link ClientCache} application or peer {@link Cache} member node name + * (i.e. {@literal gemfire.name}) with the Spring Boot {@literal spring.application.name} property. + * + * @author John Blum + * @see org.apache.geode.cache.Cache + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.client.ClientCache + * @see org.springframework.boot.autoconfigure.EnableAutoConfiguration + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.core.env.Environment + * @see org.springframework.data.gemfire.CacheFactoryBean + * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer + * @since 1.0.0 + */ +@Configuration +@ConditionalOnClass({ CacheFactoryBean.class, GemFireCache.class }) +@SuppressWarnings("unused") +public class CacheNameAutoConfiguration { + + private static final String GEMFIRE_NAME_PROPERTY = "name"; + private static final String SPRING_APPLICATION_NAME_PROPERTY = "spring.application.name"; + private static final String SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY = "spring.data.gemfire.cache.name"; + private static final String SPRING_DATA_GEMFIRE_NAME_PROPERTY = "spring.data.gemfire.name"; + private static final String SPRING_DATA_GEODE_CACHE_NAME_PROPERTY = "spring.data.geode.cache.name"; + private static final String SPRING_DATA_GEODE_NAME_PROPERTY = "spring.data.geode.name"; + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE + 1) // apply next (e.g. after @UseMemberName) + @ConditionalOnMissingProperty({ + SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY, + SPRING_DATA_GEMFIRE_NAME_PROPERTY, + SPRING_DATA_GEODE_CACHE_NAME_PROPERTY, + SPRING_DATA_GEODE_NAME_PROPERTY, + }) + ClientCacheConfigurer clientCacheNameConfigurer(Environment environment) { + return (beanName, clientCacheFactoryBean) -> configureCacheName(environment, clientCacheFactoryBean); + } + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE + 1) // apply next (e.g. after @UseMemberName) + @ConditionalOnMissingProperty({ + SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY, + SPRING_DATA_GEMFIRE_NAME_PROPERTY, + SPRING_DATA_GEODE_CACHE_NAME_PROPERTY, + SPRING_DATA_GEODE_NAME_PROPERTY, + }) + PeerCacheConfigurer peerCacheNameConfigurer(Environment environment) { + return (beanName, peerCacheFactoryBean) -> configureCacheName(environment, peerCacheFactoryBean); + } + + private void configureCacheName(Environment environment, CacheFactoryBean cacheFactoryBean) { + + Optional.of(environment) + .map(this::resolveSpringApplicationName) + .ifPresent(springApplicationName -> setGemFireName(cacheFactoryBean, springApplicationName)); + } + + private String resolveSpringApplicationName(Environment environment) { + + return Optional.ofNullable(environment) + .filter(it -> it.containsProperty(SPRING_APPLICATION_NAME_PROPERTY)) + .map(it -> it.getProperty(SPRING_APPLICATION_NAME_PROPERTY)) + .filter(StringUtils::hasText) + .orElse(null); + } + + private void setGemFireName(CacheFactoryBean cacheFactoryBean, String gemfireName) { + cacheFactoryBean.getProperties().setProperty(GEMFIRE_NAME_PROPERTY, gemfireName); + } +} diff --git a/spring-geode-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-geode-autoconfigure/src/main/resources/META-INF/spring.factories index cc9510eb..17173c6b 100644 --- a/spring-geode-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-geode-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,5 +1,6 @@ # Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.geode.boot.autoconfigure.CacheNameAutoConfiguration,\ org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration,\ org.springframework.geode.boot.autoconfigure.CachingProviderAutoConfiguration,\ org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration,\ diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cache/CacheNameAutoConfigurationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cache/CacheNameAutoConfigurationIntegrationTests.java new file mode 100644 index 00000000..9d1cac67 --- /dev/null +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cache/CacheNameAutoConfigurationIntegrationTests.java @@ -0,0 +1,122 @@ +/* + * 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.boot.autoconfigure.cache; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.function.Function; + +import org.apache.geode.cache.GemFireCache; +import org.junit.Test; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.gemfire.config.annotation.PeerCacheApplication; +import org.springframework.data.gemfire.tests.integration.SpringBootApplicationIntegrationTestsSupport; +import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects; +import org.springframework.geode.boot.autoconfigure.CacheNameAutoConfiguration; +import org.springframework.geode.config.annotation.UseMemberName; + +/** + * Integration tests for {@link CacheNameAutoConfiguration}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.boot.autoconfigure.SpringBootApplication + * @see org.springframework.boot.builder.SpringApplicationBuilder + * @see org.springframework.data.gemfire.tests.integration.SpringBootApplicationIntegrationTestsSupport + * @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects + * @see org.springframework.geode.boot.autoconfigure.CacheNameAutoConfiguration + * @since 1.0.0 + */ +public class CacheNameAutoConfigurationIntegrationTests extends SpringBootApplicationIntegrationTestsSupport { + + private Function springApplicationBuilderFunction = + Function.identity(); + + private Function springApplicationNamePropertyFunction = + builder -> { + builder.properties(Collections.singletonMap("spring.application.name", "SpringApplicationNameTest")); + return builder; + }; + + private Function springDataGemFireNamePropertyFunction = + builder -> { + builder.properties(Collections.singletonMap("spring.data.gemfire.name", "SpringDataGemFireNameTest")); + return builder; + }; + + @Override + protected SpringApplicationBuilder processBeforeBuild(SpringApplicationBuilder springApplicationBuilder) { + return this.springApplicationBuilderFunction.apply(springApplicationBuilder); + } + + private void assertGemFireCache(GemFireCache gemfireCache, String expectedName) { + + assertThat(gemfireCache).isNotNull(); + assertThat(gemfireCache.getDistributedSystem()).isNotNull(); + assertThat(gemfireCache.getDistributedSystem().getProperties()).isNotNull(); + assertThat(gemfireCache.getDistributedSystem().getProperties().getProperty("name")).isEqualTo(expectedName); + } + + @Test + public void cacheNameUsesAnnotationNameAttribute() { + assertGemFireCache(newApplicationContext(AnnotationNameAttributeTestConfiguration.class) + .getBean(GemFireCache.class), "AnnotationNameTest"); + } + + @Test + public void cacheNameUsesMemberNameAttribute() { + assertGemFireCache(newApplicationContext(MemberNameAttributeTestConfiguration.class) + .getBean(GemFireCache.class), "MemberNameTest"); + } + + @Test + public void cacheNameUsesSpringApplicationNameProperty() { + + this.springApplicationBuilderFunction = this.springApplicationNamePropertyFunction; + + assertGemFireCache(newApplicationContext(AnnotationNameAttributeTestConfiguration.class) + .getBean(GemFireCache.class), "SpringApplicationNameTest"); + } + + @Test + public void cacheNameUsesSpringDataGemFireNameProperty() { + + this.springApplicationBuilderFunction = this.springApplicationNamePropertyFunction + .andThen(this.springDataGemFireNamePropertyFunction); + + assertGemFireCache(newApplicationContext(MemberNameAttributeTestConfiguration.class) + .getBean(GemFireCache.class), "SpringDataGemFireNameTest"); + } + + @SpringBootApplication + @EnableGemFireMockObjects + @PeerCacheApplication(name = "AnnotationNameTest") + @ComponentScan(excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MemberNameAttributeTestConfiguration.class) + }) + static class AnnotationNameAttributeTestConfiguration { } + + @SpringBootApplication + @EnableGemFireMockObjects + @UseMemberName("MemberNameTest") + static class MemberNameAttributeTestConfiguration { } + +}