Polish "Add auto-configuration support for Hazelcast client"

Closes gh-7469
pull/2934/merge
Stephane Nicoll 8 years ago
parent 3fbf1a2ea6
commit 0aded58884

@ -16,12 +16,10 @@
package org.springframework.boot.autoconfigure.hazelcast; package org.springframework.boot.autoconfigure.hazelcast;
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.HazelcastInstance;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -39,21 +37,7 @@ import org.springframework.context.annotation.Import;
@Configuration @Configuration
@ConditionalOnClass(HazelcastInstance.class) @ConditionalOnClass(HazelcastInstance.class)
@EnableConfigurationProperties(HazelcastProperties.class) @EnableConfigurationProperties(HazelcastProperties.class)
@Import({ HazelcastClientConfiguration.class, HazelcastServerConfiguration.class })
public class HazelcastAutoConfiguration { 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 {
}
} }

@ -22,6 +22,7 @@ import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig; import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.core.HazelcastInstance; 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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -33,8 +34,10 @@ import org.springframework.core.io.Resource;
* Configuration for Hazelcast client. * Configuration for Hazelcast client.
* *
* @author Vedran Pavic * @author Vedran Pavic
* @since 2.0.0 * @author Stephane Nicoll
*/ */
@ConditionalOnClass(HazelcastClient.class)
@ConditionalOnMissingBean(HazelcastInstance.class)
class HazelcastClientConfiguration { class HazelcastClientConfiguration {
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.client.config"; static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.client.config";
@ -49,7 +52,7 @@ class HazelcastClientConfiguration {
throws IOException { throws IOException {
Resource config = properties.resolveConfigLocation(); Resource config = properties.resolveConfigLocation();
if (config != null) { if (config != null) {
return HazelcastInstanceFactory.createHazelcastClient(config); return new HazelcastClientFactory(config).getHazelcastInstance();
} }
return HazelcastClient.newHazelcastClient(); return HazelcastClient.newHazelcastClient();
} }
@ -62,7 +65,7 @@ class HazelcastClientConfiguration {
@Bean @Bean
public HazelcastInstance hazelcastInstance(ClientConfig config) { public HazelcastInstance hazelcastInstance(ClientConfig config) {
return HazelcastInstanceFactory.createHazelcastClient(config); return new HazelcastClientFactory(config).getHazelcastInstance();
} }
} }

@ -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);
}
}

@ -19,9 +19,6 @@ package org.springframework.boot.autoconfigure.hazelcast;
import java.io.IOException; import java.io.IOException;
import java.net.URL; 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.Config;
import com.hazelcast.config.XmlConfigBuilder; import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast; import com.hazelcast.core.Hazelcast;
@ -37,33 +34,32 @@ import org.springframework.util.StringUtils;
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Phillip Webb * @author Phillip Webb
* @author Vedran Pavic
* @since 1.3.0 * @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}
*/
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);
}
/** /**
* Get the {@link HazelcastInstance}. * Create a {@link HazelcastInstanceFactory} for the specified configuration location.
* @param configLocation the location of the configuration file * @param configLocation the location of the configuration file
* @return the {@link HazelcastInstance}
* @throws IOException if the configuration location could not be read * @throws IOException if the configuration location could not be read
*/ */
public static HazelcastInstance createHazelcastInstance(Resource configLocation) public HazelcastInstanceFactory(Resource configLocation) throws IOException {
throws IOException {
Assert.notNull(configLocation, "ConfigLocation must not be null"); Assert.notNull(configLocation, "ConfigLocation must not be null");
this.config = getConfig(configLocation);
}
/**
* Create a {@link HazelcastInstanceFactory} for the specified configuration.
* @param config the configuration
*/
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(); URL configUrl = configLocation.getURL();
Config config = new XmlConfigBuilder(configUrl).build(); Config config = new XmlConfigBuilder(configUrl).build();
if (ResourceUtils.isFileURL(configUrl)) { if (ResourceUtils.isFileURL(configUrl)) {
@ -72,34 +68,18 @@ public abstract class HazelcastInstanceFactory {
else { else {
config.setConfigurationUrl(configUrl); config.setConfigurationUrl(configUrl);
} }
return createHazelcastInstance(config); return config;
} }
/** /**
* Get the client {@link HazelcastInstance}. * Get the {@link HazelcastInstance}.
* @param config the client configuration * @return the {@link HazelcastInstance}
* @return the client {@link HazelcastInstance}
*/ */
public static HazelcastInstance createHazelcastClient(ClientConfig config) { public HazelcastInstance getHazelcastInstance() {
Assert.notNull(config, "Config must not be null"); if (StringUtils.hasText(this.config.getInstanceName())) {
if (StringUtils.hasText(config.getInstanceName())) { return Hazelcast.getOrCreateHazelcastInstance(this.config);
return HazelcastClient.getHazelcastClientByName(config.getInstanceName());
} }
return HazelcastClient.newHazelcastClient(config); return Hazelcast.newHazelcastInstance(this.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);
} }
} }

@ -34,8 +34,8 @@ import org.springframework.core.io.Resource;
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Vedran Pavic * @author Vedran Pavic
* @since 2.0.0
*/ */
@ConditionalOnMissingBean(HazelcastInstance.class)
class HazelcastServerConfiguration { class HazelcastServerConfiguration {
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config"; static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config";
@ -50,7 +50,7 @@ class HazelcastServerConfiguration {
throws IOException { throws IOException {
Resource config = properties.resolveConfigLocation(); Resource config = properties.resolveConfigLocation();
if (config != null) { if (config != null) {
return HazelcastInstanceFactory.createHazelcastInstance(config); return new HazelcastInstanceFactory(config).getHazelcastInstance();
} }
return Hazelcast.newHazelcastInstance(); return Hazelcast.newHazelcastInstance();
} }
@ -63,7 +63,7 @@ class HazelcastServerConfiguration {
@Bean @Bean
public HazelcastInstance hazelcastInstance(Config config) { public HazelcastInstance hazelcastInstance(Config config) {
return HazelcastInstanceFactory.createHazelcastInstance(config); return new HazelcastInstanceFactory(config).getHazelcastInstance();
} }
} }

@ -39,9 +39,10 @@ import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat; 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 Vedran Pavic
* @author Stephane Nicoll
*/ */
public class HazelcastAutoConfigurationClientTests { public class HazelcastAutoConfigurationClientTests {
@ -50,24 +51,27 @@ public class HazelcastAutoConfigurationClientTests {
private AnnotationConfigApplicationContext context; private AnnotationConfigApplicationContext context;
@After /**
public void closeContext() { * Servers the test clients will connect to.
if (this.context != null) { */
this.context.close(); private static HazelcastInstance hazelcastServer;
}
}
private static HazelcastInstance hazelcastInstance;
@BeforeClass @BeforeClass
public static void init() { public static void init() {
hazelcastInstance = Hazelcast.newHazelcastInstance(); hazelcastServer = Hazelcast.newHazelcastInstance();
} }
@AfterClass @AfterClass
public static void close() { public static void close() {
if (hazelcastInstance != null) { if (hazelcastServer != null) {
hazelcastInstance.shutdown(); hazelcastServer.shutdown();
}
}
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
} }
} }
@ -115,7 +119,7 @@ public class HazelcastAutoConfigurationClientTests {
} }
@Test @Test
public void clientConfigHasPriority() { public void clientConfigTakesPrecedence() {
load(HazelcastServerAndClientConfig.class, "spring.hazelcast.config=this-is-ignored.xml"); load(HazelcastServerAndClientConfig.class, "spring.hazelcast.config=this-is-ignored.xml");
HazelcastInstance hazelcastInstance = this.context HazelcastInstance hazelcastInstance = this.context
.getBean(HazelcastInstance.class); .getBean(HazelcastInstance.class);

@ -41,7 +41,7 @@ import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat; 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 * @author Stephane Nicoll
*/ */

@ -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;
}
}

@ -5111,9 +5111,8 @@ provide good examples of how to write XA wrappers.
[[boot-features-hazelcast]] [[boot-features-hazelcast]]
== Hazelcast == Hazelcast
If hazelcast is on the classpath, Spring Boot will auto-configure an `HazelcastInstance` If hazelcast is on the classpath and a suitable configuration is found, Spring Boot
that you can inject in your application. The `HazelcastInstance` is only created if a will auto-configure an `HazelcastInstance` that you can inject in your application.
configuration is found.
You can define a `com.hazelcast.config.Config` bean and we'll use that. If your 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 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 http://docs.hazelcast.org/docs/latest/manual/html-single/[Hazelcast documentation] for
more details. 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 NOTE: Spring Boot also has an
<<boot-features-caching-provider-hazelcast,explicit caching support for Hazelcast>>. The <<boot-features-caching-provider-hazelcast,explicit caching support for Hazelcast>>. The
`HazelcastInstance` is automatically wrapped in a `CacheManager` implementation if `HazelcastInstance` is automatically wrapped in a `CacheManager` implementation if

Loading…
Cancel
Save