diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigCustomizer.java new file mode 100644 index 00000000000..f6cebb79e4f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigCustomizer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.boot.autoconfigure.hazelcast; + +import com.hazelcast.config.Config; + +/** + * Callback interface that can be implemented by beans wishing to customize the Hazelcast + * server {@link Config configuration}. + * + * @author Jaromir Hamala + * @author Stephane Nicoll + * @since 2.7.0 + */ +@FunctionalInterface +public interface HazelcastConfigCustomizer { + + /** + * Customize the configuration. + * @param config the {@link Config} to customize + */ + void customize(Config config); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java index 63fe94612e7..b72cd2471d3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -24,12 +24,17 @@ import com.hazelcast.config.XmlConfigBuilder; import com.hazelcast.config.YamlConfigBuilder; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.spring.context.SpringManagedContext; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.ResourceUtils; @@ -60,11 +65,12 @@ class HazelcastServerConfiguration { static class HazelcastServerConfigFileConfiguration { @Bean - HazelcastInstance hazelcastInstance(HazelcastProperties properties, ResourceLoader resourceLoader) - throws IOException { + HazelcastInstance hazelcastInstance(HazelcastProperties properties, ResourceLoader resourceLoader, + ObjectProvider hazelcastConfigCustomizers) throws IOException { Resource configLocation = properties.resolveConfigLocation(); Config config = (configLocation != null) ? loadConfig(configLocation) : Config.load(); config.setClassLoader(resourceLoader.getClassLoader()); + hazelcastConfigCustomizers.orderedStream().forEach((customizer) -> customizer.customize(config)); return getHazelcastInstance(config); } @@ -101,6 +107,18 @@ class HazelcastServerConfiguration { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(SpringManagedContext.class) + static class SpringManagedContextHazelcastConfigCustomizerConfiguration { + + @Bean + @Order(0) + HazelcastConfigCustomizer springManagedContextHazelcastConfigCustomizer(ApplicationContext applicationContext) { + return (config) -> config.setManagedContext(new SpringManagedContext(applicationContext)); + } + + } + /** * {@link HazelcastConfigResourceCondition} that checks if the * {@code spring.hazelcast.config} configuration key is defined. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java index 3856dc99416..a6ea4451b13 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -22,16 +22,24 @@ import com.hazelcast.config.Config; import com.hazelcast.config.QueueConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.map.EntryProcessor; +import com.hazelcast.map.IMap; +import com.hazelcast.spring.context.SpringAware; +import com.hazelcast.spring.context.SpringManagedContext; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; @@ -164,6 +172,39 @@ class HazelcastAutoConfigurationServerTests { }); } + @Test + void autoConfiguredConfigUsesSpringManagedContext() { + this.contextRunner.run((context) -> { + Config config = context.getBean(HazelcastInstance.class).getConfig(); + assertThat(config.getManagedContext()).isInstanceOf(SpringManagedContext.class); + }); + } + + @Test + void autoConfiguredConfigCanUseSpringAwareComponent() { + this.contextRunner.withPropertyValues("test.hazelcast.key=42").run((context) -> { + HazelcastInstance hz = context.getBean(HazelcastInstance.class); + IMap map = hz.getMap("test"); + assertThat(map.executeOnKey("test.hazelcast.key", new SpringAwareEntryProcessor<>())).isEqualTo("42"); + }); + } + + @Test + void autoConfiguredConfigWithoutHazelcastSpringDoesNotUseSpringManagedContext() { + this.contextRunner.withClassLoader(new FilteredClassLoader(SpringManagedContext.class)).run((context) -> { + Config config = context.getBean(HazelcastInstance.class).getConfig(); + assertThat(config.getManagedContext()).isNull(); + }); + } + + @Test + void autoConfiguredContextCanOverrideManagementContextUsingCustomizer() { + this.contextRunner.withBean(TestHazelcastConfigCustomizer.class).run((context) -> { + Config config = context.getBean(HazelcastInstance.class).getConfig(); + assertThat(config.getManagedContext()).isNull(); + }); + } + @Configuration(proxyBeanMethods = false) static class HazelcastConfigWithName { @@ -186,4 +227,27 @@ class HazelcastAutoConfigurationServerTests { } + @SpringAware + static class SpringAwareEntryProcessor implements EntryProcessor { + + @Autowired + private Environment environment; + + @Override + public String process(Map.Entry entry) { + return this.environment.getProperty(entry.getKey()); + } + + } + + @Order(1) + static class TestHazelcastConfigCustomizer implements HazelcastConfigCustomizer { + + @Override + public void customize(Config config) { + config.setManagedContext(null); + } + + } + } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/hazelcast.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/hazelcast.adoc index 3969f9cdd3b..8df03a5862c 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/hazelcast.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/hazelcast.adoc @@ -30,5 +30,8 @@ Otherwise, Spring Boot tries to find the Hazelcast configuration from the defaul We also check if the `hazelcast.config` system property is set. See the https://docs.hazelcast.org/docs/latest/manual/html-single/[Hazelcast documentation] for more details. +TIP: By default, `@SpringAware` on Hazelcast components is supported. +The `ManagementContext` can be overridden by declaring a `HazelcastConfigCustomizer` bean with an `@Order` higher than zero. + NOTE: Spring Boot also has <>. If caching is enabled, the `HazelcastInstance` is automatically wrapped in a `CacheManager` implementation.