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 ded71685435..91a146db178 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,12 +16,10 @@ package org.springframework.boot.autoconfigure.hazelcast; -import com.hazelcast.client.HazelcastClient; 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.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -39,21 +37,7 @@ import org.springframework.context.annotation.Import; @Configuration @ConditionalOnClass(HazelcastInstance.class) @EnableConfigurationProperties(HazelcastProperties.class) +@Import({ HazelcastClientConfiguration.class, HazelcastServerConfiguration.class }) public class HazelcastAutoConfiguration { - @Configuration - @ConditionalOnMissingBean(HazelcastInstance.class) - @Import(HazelcastServerConfiguration.class) - static class ServerConfiguration { - - } - - @Configuration - @ConditionalOnClass(HazelcastClient.class) - @ConditionalOnMissingBean(HazelcastInstance.class) - @Import(HazelcastClientConfiguration.class) - static class ClientConfiguration { - - } - } 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 index ba3dc3b203e..2a496c23653 100644 --- 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 @@ -22,6 +22,7 @@ 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; @@ -33,8 +34,10 @@ import org.springframework.core.io.Resource; * Configuration for Hazelcast client. * * @author Vedran Pavic - * @since 2.0.0 + * @author Stephane Nicoll */ +@ConditionalOnClass(HazelcastClient.class) +@ConditionalOnMissingBean(HazelcastInstance.class) class HazelcastClientConfiguration { static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.client.config"; @@ -49,7 +52,7 @@ class HazelcastClientConfiguration { throws IOException { Resource config = properties.resolveConfigLocation(); if (config != null) { - return HazelcastInstanceFactory.createHazelcastClient(config); + return new HazelcastClientFactory(config).getHazelcastInstance(); } return HazelcastClient.newHazelcastClient(); } @@ -62,7 +65,7 @@ class HazelcastClientConfiguration { @Bean public HazelcastInstance hazelcastInstance(ClientConfig config) { - return HazelcastInstanceFactory.createHazelcastClient(config); + return new HazelcastClientFactory(config).getHazelcastInstance(); } } 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 00000000000..75efe28ed09 --- /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/HazelcastInstanceFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastInstanceFactory.java index 1369860f1a9..a6014a4675c 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 @@ -19,9 +19,6 @@ 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.config.Config; import com.hazelcast.config.XmlConfigBuilder; import com.hazelcast.core.Hazelcast; @@ -37,33 +34,32 @@ import org.springframework.util.StringUtils; * * @author Stephane Nicoll * @author Phillip Webb - * @author Vedran Pavic * @since 1.3.0 */ -public abstract class HazelcastInstanceFactory { +public class HazelcastInstanceFactory { + + private final Config config; /** - * Get the {@link HazelcastInstance}. - * @param config the configuration - * @return the {@link HazelcastInstance} + * Create a {@link HazelcastInstanceFactory} for the specified configuration location. + * @param configLocation the location of the configuration file + * @throws IOException if the configuration location could not be read */ - public static HazelcastInstance createHazelcastInstance(Config config) { - Assert.notNull(config, "Config must not be null"); - if (StringUtils.hasText(config.getInstanceName())) { - return Hazelcast.getOrCreateHazelcastInstance(config); - } - return Hazelcast.newHazelcastInstance(config); + public HazelcastInstanceFactory(Resource configLocation) throws IOException { + Assert.notNull(configLocation, "ConfigLocation must not be null"); + this.config = getConfig(configLocation); } /** - * Get the {@link HazelcastInstance}. - * @param configLocation the location of the configuration file - * @return the {@link HazelcastInstance} - * @throws IOException if the configuration location could not be read + * Create a {@link HazelcastInstanceFactory} for the specified configuration. + * @param config the configuration */ - public static HazelcastInstance createHazelcastInstance(Resource configLocation) - throws IOException { - Assert.notNull(configLocation, "ConfigLocation must not be null"); + public HazelcastInstanceFactory(Config config) { + Assert.notNull(config, "Config must not be null"); + this.config = config; + } + + private Config getConfig(Resource configLocation) throws IOException { URL configUrl = configLocation.getURL(); Config config = new XmlConfigBuilder(configUrl).build(); if (ResourceUtils.isFileURL(configUrl)) { @@ -72,34 +68,18 @@ public abstract class HazelcastInstanceFactory { else { config.setConfigurationUrl(configUrl); } - return createHazelcastInstance(config); + return config; } /** - * Get the client {@link HazelcastInstance}. - * @param config the client configuration - * @return the client {@link HazelcastInstance} + * Get the {@link HazelcastInstance}. + * @return the {@link HazelcastInstance} */ - public static HazelcastInstance createHazelcastClient(ClientConfig config) { - Assert.notNull(config, "Config must not be null"); - if (StringUtils.hasText(config.getInstanceName())) { - return HazelcastClient.getHazelcastClientByName(config.getInstanceName()); + public HazelcastInstance getHazelcastInstance() { + if (StringUtils.hasText(this.config.getInstanceName())) { + return Hazelcast.getOrCreateHazelcastInstance(this.config); } - return HazelcastClient.newHazelcastClient(config); - } - - /** - * Get the client {@link HazelcastInstance}. - * @param configLocation the location of the client configuration file - * @return the client {@link HazelcastInstance} - * @throws IOException if the configuration location could not be read - */ - public static HazelcastInstance createHazelcastClient(Resource configLocation) - throws IOException { - Assert.notNull(configLocation, "ConfigLocation must not be null"); - URL configUrl = configLocation.getURL(); - ClientConfig config = new XmlClientConfigBuilder(configUrl).build(); - return createHazelcastClient(config); + return Hazelcast.newHazelcastInstance(this.config); } } 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 index 8b84976cbc9..6d25ac6ac45 100644 --- 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 @@ -34,8 +34,8 @@ import org.springframework.core.io.Resource; * * @author Stephane Nicoll * @author Vedran Pavic - * @since 2.0.0 */ +@ConditionalOnMissingBean(HazelcastInstance.class) class HazelcastServerConfiguration { static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config"; @@ -50,7 +50,7 @@ class HazelcastServerConfiguration { throws IOException { Resource config = properties.resolveConfigLocation(); if (config != null) { - return HazelcastInstanceFactory.createHazelcastInstance(config); + return new HazelcastInstanceFactory(config).getHazelcastInstance(); } return Hazelcast.newHazelcastInstance(); } @@ -63,7 +63,7 @@ class HazelcastServerConfiguration { @Bean public HazelcastInstance hazelcastInstance(Config config) { - return HazelcastInstanceFactory.createHazelcastInstance(config); + return new HazelcastInstanceFactory(config).getHazelcastInstance(); } } 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 index 145a5452a97..03d0886c812 100644 --- 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 @@ -39,9 +39,10 @@ import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for client {@link HazelcastAutoConfiguration}. + * Tests for {@link HazelcastAutoConfiguration} specific to the client. * * @author Vedran Pavic + * @author Stephane Nicoll */ public class HazelcastAutoConfigurationClientTests { @@ -50,24 +51,27 @@ public class HazelcastAutoConfigurationClientTests { private AnnotationConfigApplicationContext context; - @After - public void closeContext() { - if (this.context != null) { - this.context.close(); - } - } - - private static HazelcastInstance hazelcastInstance; + /** + * Servers the test clients will connect to. + */ + private static HazelcastInstance hazelcastServer; @BeforeClass public static void init() { - hazelcastInstance = Hazelcast.newHazelcastInstance(); + hazelcastServer = Hazelcast.newHazelcastInstance(); } @AfterClass public static void close() { - if (hazelcastInstance != null) { - hazelcastInstance.shutdown(); + if (hazelcastServer != null) { + hazelcastServer.shutdown(); + } + } + + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); } } @@ -115,7 +119,7 @@ public class HazelcastAutoConfigurationClientTests { } @Test - public void clientConfigHasPriority() { + public void clientConfigTakesPrecedence() { load(HazelcastServerAndClientConfig.class, "spring.hazelcast.config=this-is-ignored.xml"); HazelcastInstance hazelcastInstance = this.context .getBean(HazelcastInstance.class); 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 index ba25620d046..bb66916b4c5 100644 --- 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 @@ -41,7 +41,7 @@ import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link HazelcastAutoConfiguration}. + * Tests for {@link HazelcastAutoConfiguration} when the client library is not present. * * @author Stephane Nicoll */ 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 new file mode 100644 index 00000000000..54ced943aa6 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastAutoConfigurationTests.java @@ -0,0 +1,64 @@ +/* + * 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.core.HazelcastInstance; +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HazelcastAutoConfiguration} with full classpath. + * + * @author Stephane Nicoll + */ +public class HazelcastAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void defaultConfigFile() throws IOException { + 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()); + } + + private void load(String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(HazelcastAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; + } + +} 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 addc18b4369..6ea3bce6daa 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