diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 6bbd95a1de..2126c5c248 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -75,6 +75,11 @@ hazelcast true + + com.hazelcast + hazelcast-client + true + com.hazelcast hazelcast-spring diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfiguration.java index 1eb7d23b81..91a146db17 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfiguration.java @@ -16,21 +16,13 @@ package org.springframework.boot.autoconfigure.hazelcast; -import java.io.IOException; - -import com.hazelcast.config.Config; -import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.Resource; +import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for Hazelcast. Creates a @@ -38,59 +30,14 @@ import org.springframework.core.io.Resource; * configuration file is found in the environment. * * @author Stephane Nicoll - * @author Madhura Bhave + * @author Vedran Pavic * @since 1.3.0 * @see HazelcastConfigResourceCondition */ @Configuration @ConditionalOnClass(HazelcastInstance.class) -@ConditionalOnMissingBean(HazelcastInstance.class) @EnableConfigurationProperties(HazelcastProperties.class) +@Import({ HazelcastClientConfiguration.class, HazelcastServerConfiguration.class }) public class HazelcastAutoConfiguration { - @Configuration - @ConditionalOnMissingBean(Config.class) - @Conditional(ConfigAvailableCondition.class) - static class HazelcastConfigFileConfiguration { - - private final HazelcastProperties hazelcastProperties; - - HazelcastConfigFileConfiguration(HazelcastProperties hazelcastProperties) { - this.hazelcastProperties = hazelcastProperties; - } - - @Bean - public HazelcastInstance hazelcastInstance() throws IOException { - Resource config = this.hazelcastProperties.resolveConfigLocation(); - if (config != null) { - return new HazelcastInstanceFactory(config).getHazelcastInstance(); - } - return Hazelcast.newHazelcastInstance(); - } - - } - - @Configuration - @ConditionalOnSingleCandidate(Config.class) - static class HazelcastConfigConfiguration { - - @Bean - public HazelcastInstance hazelcastInstance(Config config) { - return new HazelcastInstanceFactory(config).getHazelcastInstance(); - } - - } - - /** - * {@link HazelcastConfigResourceCondition} that checks if the - * {@code spring.hazelcast.config} configuration key is defined. - */ - static class ConfigAvailableCondition extends HazelcastConfigResourceCondition { - - ConfigAvailableCondition() { - super("spring.hazelcast.config"); - } - - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientConfiguration.java new file mode 100644 index 0000000000..2a496c2365 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientConfiguration.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.hazelcast; + +import java.io.IOException; + +import com.hazelcast.client.HazelcastClient; +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.core.HazelcastInstance; + +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.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +/** + * Configuration for Hazelcast client. + * + * @author Vedran Pavic + * @author Stephane Nicoll + */ +@ConditionalOnClass(HazelcastClient.class) +@ConditionalOnMissingBean(HazelcastInstance.class) +class HazelcastClientConfiguration { + + static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.client.config"; + + @Configuration + @ConditionalOnMissingBean(ClientConfig.class) + @Conditional(ConfigAvailableCondition.class) + static class HazelcastClientConfigFileConfiguration { + + @Bean + public HazelcastInstance hazelcastInstance(HazelcastProperties properties) + throws IOException { + Resource config = properties.resolveConfigLocation(); + if (config != null) { + return new HazelcastClientFactory(config).getHazelcastInstance(); + } + return HazelcastClient.newHazelcastClient(); + } + + } + + @Configuration + @ConditionalOnSingleCandidate(ClientConfig.class) + static class HazelcastClientConfigConfiguration { + + @Bean + public HazelcastInstance hazelcastInstance(ClientConfig config) { + return new HazelcastClientFactory(config).getHazelcastInstance(); + } + + } + + /** + * {@link HazelcastConfigResourceCondition} that checks if the + * {@code spring.hazelcast.config} configuration key is defined. + */ + static class ConfigAvailableCondition extends HazelcastConfigResourceCondition { + + ConfigAvailableCondition() { + super(CONFIG_SYSTEM_PROPERTY, "file:./hazelcast-client.xml", + "classpath:/hazelcast-client.xml"); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientFactory.java new file mode 100644 index 0000000000..75efe28ed0 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastClientFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.hazelcast; + +import java.io.IOException; +import java.net.URL; + +import com.hazelcast.client.HazelcastClient; +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.client.config.XmlClientConfigBuilder; +import com.hazelcast.core.HazelcastInstance; + +import org.springframework.core.io.Resource; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Factory that can be used to create a client {@link HazelcastInstance}. + * + * @author Vedran Pavic + * @since 2.0.0 + */ +public class HazelcastClientFactory { + + private final ClientConfig clientConfig; + + /** + * Create a {@link HazelcastClientFactory} for the specified configuration location. + * @param clientConfigLocation the location of the configuration file + * @throws IOException if the configuration location could not be read + */ + public HazelcastClientFactory(Resource clientConfigLocation) throws IOException { + this.clientConfig = getClientConfig(clientConfigLocation); + } + + /** + * Create a {@link HazelcastClientFactory} for the specified configuration. + * @param clientConfig the configuration + */ + public HazelcastClientFactory(ClientConfig clientConfig) { + Assert.notNull(clientConfig, "ClientConfig must not be null"); + this.clientConfig = clientConfig; + } + + + private ClientConfig getClientConfig(Resource clientConfigLocation) + throws IOException { + URL configUrl = clientConfigLocation.getURL(); + return new XmlClientConfigBuilder(configUrl).build(); + } + + /** + * Get the {@link HazelcastInstance}. + * @return the {@link HazelcastInstance} + */ + public HazelcastInstance getHazelcastInstance() { + if (StringUtils.hasText(this.clientConfig.getInstanceName())) { + return HazelcastClient.getHazelcastClientByName( + this.clientConfig.getInstanceName()); + } + return HazelcastClient.newHazelcastClient(this.clientConfig); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigResourceCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigResourceCondition.java index 0bec54d50a..7de04e03c6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigResourceCondition.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastConfigResourceCondition.java @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ResourceCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * {@link SpringBootCondition} used to check if the Hazelcast configuration is available. @@ -29,22 +30,26 @@ import org.springframework.core.type.AnnotatedTypeMetadata; * * @author Stephane Nicoll * @author Madhura Bhave + * @author Vedran Pavic * @since 1.3.0 */ public abstract class HazelcastConfigResourceCondition extends ResourceCondition { - static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config"; + private final String configSystemProperty; - protected HazelcastConfigResourceCondition(String property) { - super("Hazelcast", property, "file:./hazelcast.xml", "classpath:/hazelcast.xml"); + protected HazelcastConfigResourceCondition(String configSystemProperty, + String... resourceLocations) { + super("Hazelcast", "spring.hazelcast.config", resourceLocations); + Assert.notNull(configSystemProperty, "ConfigSystemProperty must not be null"); + this.configSystemProperty = configSystemProperty; } @Override protected ConditionOutcome getResourceOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) { + if (System.getProperty(this.configSystemProperty) != null) { return ConditionOutcome.match(startConditionMessage() - .because("System property '" + CONFIG_SYSTEM_PROPERTY + "' is set.")); + .because("System property '" + this.configSystemProperty + "' is set.")); } return super.getResourceOutcome(context, metadata); } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastInstanceFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastInstanceFactory.java index fffd240055..a6014a4675 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastInstanceFactory.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -38,7 +38,7 @@ import org.springframework.util.StringUtils; */ public class HazelcastInstanceFactory { - private Config config; + private final Config config; /** * Create a {@link HazelcastInstanceFactory} for the specified configuration location. diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java new file mode 100644 index 0000000000..6d25ac6ac4 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastServerConfiguration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.hazelcast; + +import java.io.IOException; + +import com.hazelcast.config.Config; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +/** + * Configuration for Hazelcast server. + * + * @author Stephane Nicoll + * @author Vedran Pavic + */ +@ConditionalOnMissingBean(HazelcastInstance.class) +class HazelcastServerConfiguration { + + static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config"; + + @Configuration + @ConditionalOnMissingBean(Config.class) + @Conditional(ConfigAvailableCondition.class) + static class HazelcastServerConfigFileConfiguration { + + @Bean + public HazelcastInstance hazelcastInstance(HazelcastProperties properties) + throws IOException { + Resource config = properties.resolveConfigLocation(); + if (config != null) { + return new HazelcastInstanceFactory(config).getHazelcastInstance(); + } + return Hazelcast.newHazelcastInstance(); + } + + } + + @Configuration + @ConditionalOnSingleCandidate(Config.class) + static class HazelcastServerConfigConfiguration { + + @Bean + public HazelcastInstance hazelcastInstance(Config config) { + return new HazelcastInstanceFactory(config).getHazelcastInstance(); + } + + } + + /** + * {@link HazelcastConfigResourceCondition} that checks if the + * {@code spring.hazelcast.config} configuration key is defined. + */ + static class ConfigAvailableCondition extends HazelcastConfigResourceCondition { + + ConfigAvailableCondition() { + super(CONFIG_SYSTEM_PROPERTY, "file:./hazelcast.xml", + "classpath:/hazelcast.xml"); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java index 65258ec12c..7f40b3e1fe 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java @@ -49,12 +49,15 @@ import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; +import org.springframework.boot.junit.runner.classpath.ClassPathExclusions; +import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -91,6 +94,8 @@ import static org.mockito.Mockito.verify; * @author Stephane Nicoll * @author EddĂș MelĂ©ndez */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions("hazelcast-client-*.jar") public class CacheAutoConfigurationTests { @Rule diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationClientTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationClientTests.java new file mode 100644 index 0000000000..03d0886c81 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationClientTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.hazelcast; + +import java.io.IOException; + +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.client.impl.HazelcastClientProxy; +import com.hazelcast.config.Config; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HazelcastAutoConfiguration} specific to the client. + * + * @author Vedran Pavic + * @author Stephane Nicoll + */ +public class HazelcastAutoConfigurationClientTests { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context; + + /** + * Servers the test clients will connect to. + */ + private static HazelcastInstance hazelcastServer; + + @BeforeClass + public static void init() { + hazelcastServer = Hazelcast.newHazelcastInstance(); + } + + @AfterClass + public static void close() { + if (hazelcastServer != null) { + hazelcastServer.shutdown(); + } + } + + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void systemProperty() throws IOException { + System.setProperty(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY, + "classpath:org/springframework/boot/autoconfigure/hazelcast/" + + "hazelcast-client-specific.xml"); + try { + load(); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance).isInstanceOf(HazelcastClientProxy.class); + assertThat(hazelcastInstance.getName()).startsWith("hz.client_"); + } + finally { + System.clearProperty(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY); + } + } + + @Test + public void explicitConfigFile() throws IOException { + load("spring.hazelcast.config=org/springframework/boot/autoconfigure/" + + "hazelcast/hazelcast-client-specific.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance).isInstanceOf(HazelcastClientProxy.class); + assertThat(hazelcastInstance.getName()).startsWith("hz.client_"); + } + + @Test + public void explicitConfigUrl() throws IOException { + load("spring.hazelcast.config=hazelcast-client-default.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance).isInstanceOf(HazelcastClientProxy.class); + assertThat(hazelcastInstance.getName()).startsWith("hz.client_"); + } + + @Test + public void unknownConfigFile() { + this.thrown.expect(BeanCreationException.class); + this.thrown.expectMessage("foo/bar/unknown.xml"); + load("spring.hazelcast.config=foo/bar/unknown.xml"); + } + + @Test + public void clientConfigTakesPrecedence() { + load(HazelcastServerAndClientConfig.class, "spring.hazelcast.config=this-is-ignored.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance).isInstanceOf(HazelcastClientProxy.class); + } + + private void load(String... environment) { + load(null, environment); + } + + private void load(Class config, String... environment) { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(applicationContext, environment); + if (config != null) { + applicationContext.register(config); + } + applicationContext.register(HazelcastAutoConfiguration.class); + applicationContext.refresh(); + this.context = applicationContext; + } + + @Configuration + static class HazelcastServerAndClientConfig { + + @Bean + public Config config() { + return new Config(); + } + + @Bean + public ClientConfig clientConfig() { + return new ClientConfig(); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java new file mode 100644 index 0000000000..bb66916b4c --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationServerTests.java @@ -0,0 +1,184 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.hazelcast; + +import java.io.IOException; +import java.util.Map; + +import com.hazelcast.config.Config; +import com.hazelcast.config.QueueConfig; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.junit.runner.classpath.ClassPathExclusions; +import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HazelcastAutoConfiguration} when the client library is not present. + * + * @author Stephane Nicoll + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions("hazelcast-client-*.jar") +public class HazelcastAutoConfigurationServerTests { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context; + + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void defaultConfigFile() throws IOException { + load(); // hazelcast.xml present in root classpath + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance.getConfig().getConfigurationUrl()) + .isEqualTo(new ClassPathResource("hazelcast.xml").getURL()); + } + + @Test + public void systemProperty() throws IOException { + System.setProperty(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY, + "classpath:org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.xml"); + try { + load(); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + Map queueConfigs = hazelcastInstance.getConfig() + .getQueueConfigs(); + assertThat(queueConfigs).hasSize(1).containsKey("foobar"); + } + finally { + System.clearProperty(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY); + } + } + + @Test + public void explicitConfigFile() throws IOException { + load("spring.hazelcast.config=org/springframework/boot/autoconfigure/hazelcast/" + + "hazelcast-specific.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance.getConfig().getConfigurationFile()).isEqualTo( + new ClassPathResource("org/springframework/boot/autoconfigure/hazelcast" + + "/hazelcast-specific.xml").getFile()); + } + + @Test + public void explicitConfigUrl() throws IOException { + load("spring.hazelcast.config=hazelcast-default.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance.getConfig().getConfigurationUrl()) + .isEqualTo(new ClassPathResource("hazelcast-default.xml").getURL()); + } + + @Test + public void unknownConfigFile() { + this.thrown.expect(BeanCreationException.class); + this.thrown.expectMessage("foo/bar/unknown.xml"); + load("spring.hazelcast.config=foo/bar/unknown.xml"); + } + + @Test + public void configInstanceWithName() { + Config config = new Config("my-test-instance"); + HazelcastInstance existingHazelcastInstance = Hazelcast + .newHazelcastInstance(config); + try { + load(HazelcastConfigWithName.class, + "spring.hazelcast.config=this-is-ignored.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + assertThat(hazelcastInstance.getConfig().getInstanceName()) + .isEqualTo("my-test-instance"); + // Should reuse any existing instance by default. + assertThat(hazelcastInstance).isEqualTo(existingHazelcastInstance); + } + finally { + existingHazelcastInstance.shutdown(); + } + } + + @Test + public void configInstanceWithoutName() { + load(HazelcastConfigNoName.class, "spring.hazelcast.config=this-is-ignored.xml"); + HazelcastInstance hazelcastInstance = this.context + .getBean(HazelcastInstance.class); + Map queueConfigs = hazelcastInstance.getConfig() + .getQueueConfigs(); + assertThat(queueConfigs).hasSize(1).containsKey("another-queue"); + } + + private void load(String... environment) { + load(null, environment); + } + + private void load(Class config, String... environment) { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(applicationContext, environment); + if (config != null) { + applicationContext.register(config); + } + applicationContext.register(HazelcastAutoConfiguration.class); + applicationContext.refresh(); + this.context = applicationContext; + } + + @Configuration + static class HazelcastConfigWithName { + + @Bean + public Config myHazelcastConfig() { + return new Config("my-test-instance"); + } + + } + + @Configuration + static class HazelcastConfigNoName { + + @Bean + public Config anotherHazelcastConfig() { + Config config = new Config(); + config.addQueueConfig(new QueueConfig("another-queue")); + return config; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationTests.java index ef20564aed..54ced943aa 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -17,36 +17,24 @@ package org.springframework.boot.autoconfigure.hazelcast; import java.io.IOException; -import java.util.Map; -import com.hazelcast.config.Config; -import com.hazelcast.config.QueueConfig; -import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import org.junit.After; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link HazelcastAutoConfiguration}. + * Tests for {@link HazelcastAutoConfiguration} with full classpath. * * @author Stephane Nicoll */ public class HazelcastAutoConfigurationTests { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - private AnnotationConfigApplicationContext context; @After @@ -58,122 +46,19 @@ public class HazelcastAutoConfigurationTests { @Test public void defaultConfigFile() throws IOException { - load(); // hazelcast.xml present in root classpath + load(); // no hazelcast-client.xml and hazelcast.xml is present in root classpath HazelcastInstance hazelcastInstance = this.context .getBean(HazelcastInstance.class); assertThat(hazelcastInstance.getConfig().getConfigurationUrl()) .isEqualTo(new ClassPathResource("hazelcast.xml").getURL()); } - @Test - public void systemProperty() throws IOException { - System.setProperty(HazelcastConfigResourceCondition.CONFIG_SYSTEM_PROPERTY, - "classpath:org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.xml"); - try { - load(); - HazelcastInstance hazelcastInstance = this.context - .getBean(HazelcastInstance.class); - Map queueConfigs = hazelcastInstance.getConfig() - .getQueueConfigs(); - assertThat(queueConfigs).hasSize(1).containsKey("foobar"); - } - finally { - System.clearProperty(HazelcastConfigResourceCondition.CONFIG_SYSTEM_PROPERTY); - } - } - - @Test - public void explicitConfigFile() throws IOException { - load("spring.hazelcast.config=org/springframework/boot/autoconfigure/hazelcast/" - + "hazelcast-specific.xml"); - HazelcastInstance hazelcastInstance = this.context - .getBean(HazelcastInstance.class); - assertThat(hazelcastInstance.getConfig().getConfigurationFile()).isEqualTo( - new ClassPathResource("org/springframework/boot/autoconfigure/hazelcast" - + "/hazelcast-specific.xml").getFile()); - } - - @Test - public void explicitConfigUrl() throws IOException { - load("spring.hazelcast.config=hazelcast-default.xml"); - HazelcastInstance hazelcastInstance = this.context - .getBean(HazelcastInstance.class); - assertThat(hazelcastInstance.getConfig().getConfigurationUrl()) - .isEqualTo(new ClassPathResource("hazelcast-default.xml").getURL()); - } - - @Test - public void unknownConfigFile() { - this.thrown.expect(BeanCreationException.class); - this.thrown.expectMessage("foo/bar/unknown.xml"); - load("spring.hazelcast.config=foo/bar/unknown.xml"); - } - - @Test - public void configInstanceWithName() { - Config config = new Config("my-test-instance"); - HazelcastInstance existingHazelcastInstance = Hazelcast - .newHazelcastInstance(config); - try { - load(HazelcastConfigWithName.class, - "spring.hazelcast.config=this-is-ignored.xml"); - HazelcastInstance hazelcastInstance = this.context - .getBean(HazelcastInstance.class); - assertThat(hazelcastInstance.getConfig().getInstanceName()) - .isEqualTo("my-test-instance"); - // Should reuse any existing instance by default. - assertThat(hazelcastInstance).isEqualTo(existingHazelcastInstance); - } - finally { - existingHazelcastInstance.shutdown(); - } - } - - @Test - public void configInstanceWithoutName() { - load(HazelcastConfigNoName.class, "spring.hazelcast.config=this-is-ignored.xml"); - HazelcastInstance hazelcastInstance = this.context - .getBean(HazelcastInstance.class); - Map queueConfigs = hazelcastInstance.getConfig() - .getQueueConfigs(); - assertThat(queueConfigs).hasSize(1).containsKey("another-queue"); - } - private void load(String... environment) { - load(null, environment); - } - - private void load(Class config, String... environment) { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - EnvironmentTestUtils.addEnvironment(applicationContext, environment); - if (config != null) { - applicationContext.register(config); - } - applicationContext.register(HazelcastAutoConfiguration.class); - applicationContext.refresh(); - this.context = applicationContext; - } - - @Configuration - static class HazelcastConfigWithName { - - @Bean - public Config myHazelcastConfig() { - return new Config("my-test-instance"); - } - - } - - @Configuration - static class HazelcastConfigNoName { - - @Bean - public Config anotherHazelcastConfig() { - Config config = new Config(); - config.addQueueConfig(new QueueConfig("another-queue")); - return config; - } - + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(HazelcastAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; } } diff --git a/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/hazelcast/hazelcast-client-specific.xml b/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/hazelcast/hazelcast-client-specific.xml new file mode 100644 index 0000000000..e5596bf220 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/hazelcast/hazelcast-client-specific.xml @@ -0,0 +1,6 @@ + + + + diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index addc18b436..6ea3bce6da 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -5111,9 +5111,8 @@ provide good examples of how to write XA wrappers. [[boot-features-hazelcast]] == Hazelcast -If hazelcast is on the classpath, Spring Boot will auto-configure an `HazelcastInstance` -that you can inject in your application. The `HazelcastInstance` is only created if a -configuration is found. +If hazelcast is on the classpath and a suitable configuration is found, Spring Boot +will auto-configure an `HazelcastInstance` that you can inject in your application. You can define a `com.hazelcast.config.Config` bean and we'll use that. If your configuration defines an instance name, we'll try to locate an existing instance rather @@ -5132,6 +5131,14 @@ classpath. We also check if the `hazelcast.config` system property is set. Check http://docs.hazelcast.org/docs/latest/manual/html-single/[Hazelcast documentation] for more details. +If `hazelcast-client` is present on the classpath, Spring Boot will first attempt to +create a client with similar rules as above, that is: + +* The presence of a `com.hazelcast.client.config.ClientConfig` bean +* A configuration file defined by the `spring.hazelcast.config` property +* The presence of the `hazelcast.client.config` system property +* A `hazelcast-client.xml` in the working directory or at the root of the classpath + NOTE: Spring Boot also has an <>. The `HazelcastInstance` is automatically wrapped in a `CacheManager` implementation if