Commit 721b5a23 authored by Stephane Nicoll's avatar Stephane Nicoll

Hazelcast auto-configuration

Provide a general purpose Hazelcast integration (i.e. not tied to caching).

Auto-configure a `HazelcastInstance` either based on the presence of a
`Config` bean or a configuration file. Said configuration file can be
specified explicitly or automatically found from default locations.

The cache integration already supports Hazelcast so it has been reworked
to automatically reuse an existing `HazelcastInstance` if available.

Closes gh-2942
parent 35b2bca6
...@@ -20,6 +20,7 @@ import net.sf.ehcache.Cache; ...@@ -20,6 +20,7 @@ import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager; import net.sf.ehcache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ResourceCondition;
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.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheCacheManager;
...@@ -68,10 +69,10 @@ class EhCacheCacheConfiguration { ...@@ -68,10 +69,10 @@ class EhCacheCacheConfiguration {
* default configuration has been found or if property referring to the file to use * default configuration has been found or if property referring to the file to use
* has been set. * has been set.
*/ */
static class ConfigAvailableCondition extends CacheConfigFileCondition { static class ConfigAvailableCondition extends ResourceCondition {
public ConfigAvailableCondition() { public ConfigAvailableCondition() {
super("EhCache", "spring.cache.ehcache", "classpath:/ehcache.xml"); super("EhCache", "spring.cache.ehcache", "config", "classpath:/ehcache.xml");
} }
} }
......
...@@ -16,85 +16,118 @@ ...@@ -16,85 +16,118 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastConfigResourceCondition;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import com.hazelcast.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
/** /**
* Hazelcast cache configuration. Only kick in if a configuration file location is set or * Hazelcast cache configuration. Can either reuse the {@link HazelcastInstance} that
* if a default configuration file exists (either placed in the default location or set * has been configured by the general {@link HazelcastAutoConfiguration} or create
* via the {@value #CONFIG_SYSTEM_PROPERTY} system property). * a separate one if the {@code spring.cache.hazelcast.config} property has been set.
* <p>
* If the {@link HazelcastAutoConfiguration} has been disabled, an attempt to configure
* a default {@link HazelcastInstance} is still made, using the same defaults.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
* @see HazelcastConfigResourceCondition
*/ */
@Configuration @Configuration
@ConditionalOnClass({ HazelcastInstance.class, HazelcastCacheManager.class }) @ConditionalOnClass({HazelcastInstance.class, HazelcastCacheManager.class})
@ConditionalOnMissingBean(CacheManager.class) @ConditionalOnMissingBean(CacheManager.class)
@Conditional({ CacheCondition.class, @Conditional(CacheCondition.class)
HazelcastCacheConfiguration.ConfigAvailableCondition.class }) @AutoConfigureAfter(HazelcastAutoConfiguration.class)
class HazelcastCacheConfiguration { class HazelcastCacheConfiguration {
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config"; @Configuration
@ConditionalOnSingleCandidate(HazelcastInstance.class)
static class ExistingHazelcastInstanceConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Autowired @Bean
private CacheProperties cacheProperties; public HazelcastCacheManager cacheManager(HazelcastInstance existingHazelcastInstance)
throws IOException {
Resource location = this.cacheProperties
.resolveConfigLocation(this.cacheProperties.getHazelcast().getConfig());
if (location != null) {
HazelcastInstance cacheHazelcastInstance =
HazelcastAutoConfiguration.createHazelcastInstance(location);
return new CloseableHazelcastCacheManager(cacheHazelcastInstance);
}
else {
return new HazelcastCacheManager(existingHazelcastInstance);
}
@Bean }
public HazelcastCacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
} }
@Bean @Configuration
@ConditionalOnMissingBean @ConditionalOnMissingBean(HazelcastInstance.class)
public HazelcastInstance hazelcastInstance() throws IOException { @Conditional(ConfigAvailableCondition.class)
Resource location = this.cacheProperties static class DefaultHazelcastInstanceConfiguration {
.resolveConfigLocation(this.cacheProperties.getHazelcast().getConfig());
if (location != null) { @Autowired
Config cfg = new XmlConfigBuilder(location.getURL()).build(); private CacheProperties cacheProperties;
return Hazelcast.newHazelcastInstance(cfg);
@Bean
public HazelcastInstance hazelcastInstance() throws IOException {
Resource location = this.cacheProperties
.resolveConfigLocation(this.cacheProperties.getHazelcast().getConfig());
if (location != null) {
HazelcastAutoConfiguration.createHazelcastInstance(location);
}
return Hazelcast.newHazelcastInstance();
}
@Bean
public HazelcastCacheManager cacheManager() throws IOException {
return new HazelcastCacheManager(hazelcastInstance());
} }
return Hazelcast.newHazelcastInstance();
} }
/** /**
* Determines if the Hazelcast configuration is available. This either kicks in if a * {@link HazelcastConfigResourceCondition} that checks if the
* default configuration has been found or if property referring to the file to use * {@code spring.cache.hazelcast.config} configuration key is defined.
* has been set.
*/ */
static class ConfigAvailableCondition extends CacheConfigFileCondition { static class ConfigAvailableCondition extends HazelcastConfigResourceCondition {
public ConfigAvailableCondition() { public ConfigAvailableCondition() {
super("Hazelcast", "spring.cache.hazelcast", "file:./hazelcast.xml", super("spring.cache.hazelcast", "config");
"classpath:/hazelcast.xml");
} }
@Override }
protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { private static class CloseableHazelcastCacheManager extends HazelcastCacheManager implements Closeable {
if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) { private final HazelcastInstance hazelcastInstance;
return ConditionOutcome.match("System property '"
+ CONFIG_SYSTEM_PROPERTY + "' is set."); public CloseableHazelcastCacheManager(HazelcastInstance hazelcastInstance) {
} super(hazelcastInstance);
return super.getResourceOutcome(context, metadata); this.hazelcastInstance = hazelcastInstance;
} }
@Override
public void close() throws IOException {
this.hazelcastInstance.shutdown();
}
} }
} }
...@@ -14,33 +14,44 @@ ...@@ -14,33 +14,44 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.condition;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
/** /**
* {@link SpringBootCondition} used to check if a cache configuration file can be found. * {@link SpringBootCondition} used to check if a resource can be found using a
* configurable property and optional default location(s).
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Phillip Webb * @author Phillip Webb
* @since 1.3.0
*/ */
abstract class CacheConfigFileCondition extends SpringBootCondition { public abstract class ResourceCondition extends SpringBootCondition {
private final String name; private final String name;
private final String prefix; private final String prefix;
private final String propertyName;
private final String[] resourceLocations; private final String[] resourceLocations;
public CacheConfigFileCondition(String name, String prefix, /**
* Create a new condition.
* @param name the name of the component
* @param prefix the prefix of the configuration key
* @param propertyName the name of the configuration key
* @param resourceLocations default location(s) where the configuration file can be
* found if the configuration key is not specified
*/
protected ResourceCondition(String name, String prefix, String propertyName,
String... resourceLocations) { String... resourceLocations) {
this.name = name; this.name = name;
this.prefix = (prefix.endsWith(".") ? prefix : prefix + "."); this.prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
this.propertyName = propertyName;
this.resourceLocations = resourceLocations; this.resourceLocations = resourceLocations;
} }
...@@ -49,13 +60,19 @@ abstract class CacheConfigFileCondition extends SpringBootCondition { ...@@ -49,13 +60,19 @@ abstract class CacheConfigFileCondition extends SpringBootCondition {
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), this.prefix); context.getEnvironment(), this.prefix);
if (resolver.containsProperty("config")) { if (resolver.containsProperty(propertyName)) {
return ConditionOutcome.match("A '" + this.prefix + ".config' " return ConditionOutcome.match("A '" + this.prefix + propertyName +"' "
+ "property is specified"); + "property is specified");
} }
return getResourceOutcome(context, metadata); return getResourceOutcome(context, metadata);
} }
/**
* Check if one of the default resource locations actually exists.
* @param context the condition context
* @param metadata the annotation metadata
* @return the condition outcome
*/
protected ConditionOutcome getResourceOutcome(ConditionContext context, protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
for (String location : this.resourceLocations) { for (String location : this.resourceLocations) {
......
/*
* Copyright 2012-2015 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.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.annotation.Autowired;
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.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Hazelcast. Creates a
* {@link HazelcastInstance} based on explicit configuration or when a default
* configuration file is found in the environment.
*
* @author Stephane Nicoll
* @since 1.3.0
* @see HazelcastConfigResourceCondition
*/
@Configuration
@ConditionalOnClass(HazelcastInstance.class)
@ConditionalOnMissingBean(HazelcastInstance.class)
@EnableConfigurationProperties(HazelcastProperties.class)
public class HazelcastAutoConfiguration {
/**
* Create a {@link HazelcastInstance} based on the specified configuration location.
* @param location the location of the configuration file
* @return a {@link HazelcastInstance} for the specified configuration
* @throws IOException the configuration file could not be read
*/
public static HazelcastInstance createHazelcastInstance(Resource location)
throws IOException {
Assert.notNull(location, "Config must not be null");
URL configUrl = location.getURL();
Config config = new XmlConfigBuilder(configUrl).build();
if (ResourceUtils.isFileURL(configUrl)) {
config.setConfigurationFile(location.getFile());
}
else {
config.setConfigurationUrl(configUrl);
}
return createHazelcastInstance(config);
}
private static HazelcastInstance createHazelcastInstance(Config config) {
if (StringUtils.hasText(config.getInstanceName())) {
return Hazelcast.getOrCreateHazelcastInstance(config);
}
return Hazelcast.newHazelcastInstance(config);
}
@Configuration
@ConditionalOnMissingBean({HazelcastInstance.class, Config.class})
@Conditional(ConfigAvailableCondition.class)
static class HazelcastConfigFileConfiguration {
@Autowired
private HazelcastProperties hazelcastProperties;
@Bean
@ConditionalOnMissingBean
public HazelcastInstance hazelcastInstance() throws IOException {
Resource config = this.hazelcastProperties.resolveConfigLocation();
if (config != null) {
return createHazelcastInstance(config);
}
return Hazelcast.newHazelcastInstance();
}
}
@Configuration
@ConditionalOnMissingBean(HazelcastInstance.class)
@ConditionalOnSingleCandidate(Config.class)
static class HazelcastConfigConfiguration {
@Bean
public HazelcastInstance hazelcastInstance(Config config) {
return createHazelcastInstance(config);
}
}
/**
* {@link HazelcastConfigResourceCondition} that checks if the
* {@code spring.hazelcast.config} configuration key is defined.
*/
static class ConfigAvailableCondition extends HazelcastConfigResourceCondition {
public ConfigAvailableCondition() {
super("spring.hazelcast", "config");
}
}
}
/*
* Copyright 2012-2015 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 org.springframework.boot.autoconfigure.condition.ConditionOutcome;
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;
/**
* {@link SpringBootCondition} used to check if the Hazelcast configuration is
* available. This either kicks in if a default configuration has been found or
* if configurable property referring to the resource to use has been set.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public abstract class HazelcastConfigResourceCondition extends ResourceCondition {
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config";
protected HazelcastConfigResourceCondition(String prefix, String propertyName) {
super("Hazelcast", prefix, propertyName, "file:./hazelcast.xml",
"classpath:/hazelcast.xml");
}
@Override
protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) {
return ConditionOutcome.match("System property '"
+ CONFIG_SYSTEM_PROPERTY + "' is set.");
}
return super.getResourceOutcome(context, metadata);
}
}
/*
* Copyright 2012-2015 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 org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* Configuration properties for the hazelcast integration.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@ConfigurationProperties("spring.hazelcast")
public class HazelcastProperties {
/**
* The location of the configuration file to use to initialize Hazelcast.
*/
private Resource config;
public Resource getConfig() {
return this.config;
}
public void setConfig(Resource config) {
this.config = config;
}
/**
* Resolve the config location if set.
* @return the location or {@code null} if it is not set
* @throws IllegalArgumentException if the config attribute is set to an unknown
* location
*/
public Resource resolveConfigLocation() {
if (this.config != null) {
Assert.isTrue(this.config.exists(), "Hazelcast configuration does not exist '"
+ this.config.getDescription() + "'");
return this.config;
}
return null;
}
}
/*
* Copyright 2012-2015 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.
*/
/**
* Auto-configuration for Hazelcast.
*/
package org.springframework.boot.autoconfigure.hazelcast;
\ No newline at end of file
...@@ -24,6 +24,7 @@ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ ...@@ -24,6 +24,7 @@ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -38,6 +39,7 @@ import org.springframework.beans.DirectFieldAccessor; ...@@ -38,6 +39,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider; import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
...@@ -74,6 +76,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; ...@@ -74,6 +76,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is; import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
...@@ -367,6 +370,51 @@ public class CacheAutoConfigurationTests { ...@@ -367,6 +370,51 @@ public class CacheAutoConfigurationTests {
is(this.context.getBean("customHazelcastInstance"))); is(this.context.getBean("customHazelcastInstance")));
} }
@Test
public void hazelcastCacheWithMainHazelcastAutoConfiguration() throws IOException {
Collection<Class<?>> configs = new ArrayList<Class<?>>();
configs.add(DefaultCacheConfiguration.class);
configs.add(HazelcastAutoConfiguration.class);
String mainConfig =
"org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.xml";
doLoad(configs, "spring.cache.type=hazelcast",
"spring.hazelcast.config=" + mainConfig);
HazelcastCacheManager cacheManager = validateCacheManager(HazelcastCacheManager.class);
HazelcastInstance hazelcastInstance = this.context.getBean(HazelcastInstance.class);
assertThat(
new DirectFieldAccessor(cacheManager)
.getPropertyValue("hazelcastInstance"),
is((Object) hazelcastInstance));
assertThat(hazelcastInstance.getConfig().getConfigurationFile(),
is(new ClassPathResource(mainConfig).getFile()));
}
@Test
public void hazelcastCacheWithMainHazelcastAutoConfigurationAndSeparateCacheConfig()
throws IOException {
Collection<Class<?>> configs = new ArrayList<Class<?>>();
configs.add(DefaultCacheConfiguration.class);
configs.add(HazelcastAutoConfiguration.class);
String mainConfig = "org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.xml";
String cacheConfig = "org/springframework/boot/autoconfigure/cache/hazelcast-specific.xml";
doLoad(configs, "spring.cache.type=hazelcast",
"spring.cache.hazelcast.config=" + cacheConfig,
"spring.hazelcast.config=" + mainConfig);
HazelcastInstance hazelcastInstance = this.context.getBean(HazelcastInstance.class);
HazelcastCacheManager cacheManager = validateCacheManager(HazelcastCacheManager.class);
HazelcastInstance cacheHazelcastInstance = (HazelcastInstance)
new DirectFieldAccessor(cacheManager).getPropertyValue("hazelcastInstance");
assertThat(
cacheHazelcastInstance,
is(not(hazelcastInstance))); // Our custom cache instance
assertThat(hazelcastInstance.getConfig().getConfigurationFile(),
is(new ClassPathResource(mainConfig).getFile()));
assertThat(cacheHazelcastInstance.getConfig().getConfigurationFile(),
is(new ClassPathResource(cacheConfig).getFile()));
}
@Test @Test
public void hazelcastAsJCacheWithCaches() { public void hazelcastAsJCacheWithCaches() {
String cachingProviderFqn = HazelcastCachingProvider.class.getName(); String cachingProviderFqn = HazelcastCachingProvider.class.getName();
...@@ -499,17 +547,21 @@ public class CacheAutoConfigurationTests { ...@@ -499,17 +547,21 @@ public class CacheAutoConfigurationTests {
} }
private void load(Class<?> config, String... environment) { private void load(Class<?> config, String... environment) {
this.context = doLoad(config, environment); Collection<Class<?>> configs = new ArrayList<Class<?>>();
configs.add(config);
doLoad(configs, environment);
} }
private AnnotationConfigApplicationContext doLoad(Class<?> config, private void doLoad(Collection<Class<?>> configs,
String... environment) { String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(applicationContext, environment); EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.register(config); for (Class<?> config : configs) {
applicationContext.register(config);
}
applicationContext.register(CacheAutoConfiguration.class); applicationContext.register(CacheAutoConfiguration.class);
applicationContext.refresh(); applicationContext.refresh();
return applicationContext; this.context = applicationContext;
} }
@Configuration @Configuration
......
...@@ -14,10 +14,11 @@ ...@@ -14,10 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.condition;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
...@@ -29,11 +30,11 @@ import static org.junit.Assert.assertFalse; ...@@ -29,11 +30,11 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
* Test for {@link CacheConfigFileCondition}. * Test for {@link ResourceCondition}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class CacheConfigFileConditionTests { public class ResourceConditionTests {
private ConfigurableApplicationContext context; private ConfigurableApplicationContext context;
...@@ -45,20 +46,21 @@ public class CacheConfigFileConditionTests { ...@@ -45,20 +46,21 @@ public class CacheConfigFileConditionTests {
} }
@Test @Test
public void defaultFileAndNoExplicitKey() { public void defaultResourceAndNoExplicitKey() {
load(DefaultFileConfiguration.class); load(DefaultLocationConfiguration.class);
assertTrue(this.context.containsBean("foo")); assertTrue(this.context.containsBean("foo"));
} }
@Test @Test
public void noDefaultFileAndNoExplicitKey() { public void unknownDefaultLocationAndNoExplicitKey() {
load(NoDefaultFileConfiguration.class); load(UnknownDefaultLocationConfiguration.class);
assertFalse(this.context.containsBean("foo")); assertFalse(this.context.containsBean("foo"));
} }
@Test @Test
public void noDefaultFileAndExplicitKeyToResource() { public void unknownDefaultLocationAndExplicitKeyToResource() {
load(NoDefaultFileConfiguration.class, "spring.cache.test.config=ehcache.xml"); load(UnknownDefaultLocationConfiguration.class,
"spring.foo.test.config=logging.properties");
assertTrue(this.context.containsBean("foo")); assertTrue(this.context.containsBean("foo"));
} }
...@@ -71,8 +73,8 @@ public class CacheConfigFileConditionTests { ...@@ -71,8 +73,8 @@ public class CacheConfigFileConditionTests {
} }
@Configuration @Configuration
@Conditional(CacheConfigFileDefaultFileCondition.class) @Conditional(DefaultLocationResourceCondition.class)
static class DefaultFileConfiguration { static class DefaultLocationConfiguration {
@Bean @Bean
public String foo() { public String foo() {
...@@ -81,8 +83,8 @@ public class CacheConfigFileConditionTests { ...@@ -81,8 +83,8 @@ public class CacheConfigFileConditionTests {
} }
@Configuration @Configuration
@Conditional(CacheConfigFileNoDefaultFileCondition.class) @Conditional(UnknownDefaultLocationResourceCondition.class)
static class NoDefaultFileConfiguration { static class UnknownDefaultLocationConfiguration {
@Bean @Bean
public String foo() { public String foo() {
...@@ -90,19 +92,19 @@ public class CacheConfigFileConditionTests { ...@@ -90,19 +92,19 @@ public class CacheConfigFileConditionTests {
} }
} }
private static class CacheConfigFileDefaultFileCondition extends private static class DefaultLocationResourceCondition extends
CacheConfigFileCondition { ResourceCondition {
public CacheConfigFileDefaultFileCondition() { public DefaultLocationResourceCondition() {
super("test", "spring.cache.test.", "classpath:/ehcache.xml"); super("test", "spring.foo.test.", "config", "classpath:/logging.properties");
} }
} }
private static class CacheConfigFileNoDefaultFileCondition extends private static class UnknownDefaultLocationResourceCondition extends
CacheConfigFileCondition { ResourceCondition {
public CacheConfigFileNoDefaultFileCondition() { public UnknownDefaultLocationResourceCondition() {
super("test", "spring.cache.test", super("test", "spring.foo.test", "config",
"classpath:/this-cache-file-does-not-exist.xml"); "classpath:/this-file-does-not-exist.xml");
} }
} }
......
/*
* Copyright 2012-2015 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.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.test.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.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsMapContaining.hasKey;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link HazelcastAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class HazelcastAutoConfigurationTests {
@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(),
is(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<String, QueueConfig> queueConfigs = hazelcastInstance.getConfig().getQueueConfigs();
assertThat(queueConfigs.values(), hasSize(1));
assertThat(queueConfigs, hasKey("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(),
is(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(),
is(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(), is("my-test-instance"));
// Should reuse any existing instance by default.
assertThat(hazelcastInstance, is(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<String, QueueConfig> queueConfigs = hazelcastInstance.getConfig().getQueueConfigs();
assertThat(queueConfigs.values(), hasSize(1));
assertThat(queueConfigs, hasKey("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;
}
}
}
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.5.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<queue name="foobar"/>
</hazelcast>
\ No newline at end of file
...@@ -585,6 +585,9 @@ content into your application; rather pick only the properties that you need. ...@@ -585,6 +585,9 @@ content into your application; rather pick only the properties that you need.
spring.cache.jcache.provider= # fully qualified name of the CachingProvider implementation to use spring.cache.jcache.provider= # fully qualified name of the CachingProvider implementation to use
spring.cache.guava.spec= # link:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilderSpec.html[guava specs] spring.cache.guava.spec= # link:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilderSpec.html[guava specs]
# HAZELCAST ({sc-spring-boot-autoconfigure}/hazelcast/HazelcastProperties.{sc-ext}[HazelcastProperties])
spring.hazelcast.config= # location of the hazelcast configuration
# AOP # AOP
spring.aop.auto= spring.aop.auto=
spring.aop.proxy-target-class= spring.aop.proxy-target-class=
......
...@@ -2900,16 +2900,23 @@ manager. An alternate configuration file can be provide a well using: ...@@ -2900,16 +2900,23 @@ manager. An alternate configuration file can be provide a well using:
[[boot-features-caching-provider-hazelcast]] [[boot-features-caching-provider-hazelcast]]
==== Hazelcast ==== Hazelcast
Hazelcast is used if a `hazelcast.xml` file can be found in the current working
directory, at the root of the classpath or a location specified via the `hazelcast.config` Spring Boot has a <<boot-features-hazelcast,general support for Hazelcast>>. If
system property. Spring Boot detects all of these and also allows for explicit location a `HazelcastInstance` has been auto-configured, it is automatically wrapped in a
using: `CacheManager`.
If for some reason you need a different `HazelcastInstance` for caching, you can
request Spring Boot to create a separate one that will only used by the
`CacheManager`:
[source,properties,indent=0] [source,properties,indent=0]
---- ----
spring.cache.hazelcast.config=classpath:config/my-hazelcast.xml spring.cache.hazelcast.config=classpath:config/my-cache-hazelcast.xml
---- ----
TIP: If a separate `HazelcastInstance` is created that way, it is not registered
in the application context.
[[boot-features-caching-provider-infinispan]] [[boot-features-caching-provider-infinispan]]
...@@ -3391,6 +3398,36 @@ provide good examples of how to write XA wrappers. ...@@ -3391,6 +3398,36 @@ 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.
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
than creating a new one.
You could also specify the `hazelcast.xml` configuration file to use via configuration:
[source,properties,indent=0]
----
spring.hazelcast.config=classpath:config/my-hazelcast.xml
----
Otherwise, Spring Boot tries to find the Hazelcast configuration from the default
locations, that is `hazelcast.xml` in the working directory or at the root of the
classpath. We also check if the `hazelcast.config` system property is set. Check the
Hazelcast documentation for more details.
NOTE: Spring Boot also has an
<<boot-features-caching-provider-hazelcast,explicit caching support for Hazelcast>>. The
`HazelcastInstance` is automatically wrapped in a `CacheManager` implementation if
caching is enabled.
[[boot-features-integration]] [[boot-features-integration]]
== Spring Integration == Spring Integration
Spring Integration provides abstractions over messaging and also other transports such as Spring Integration provides abstractions over messaging and also other transports such as
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment