Create spring-boot-hazelcast module
This commit is contained in:
committed by
Phillip Webb
parent
cf7d8332e2
commit
8d5c834df9
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Hazelcast IMDG. Creates a
|
||||
* {@link HazelcastInstance} based on explicit configuration or when a default
|
||||
* configuration file is found in the environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Vedran Pavic
|
||||
* @since 4.0.0
|
||||
* @see HazelcastConfigResourceCondition
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(HazelcastInstance.class)
|
||||
@EnableConfigurationProperties(HazelcastProperties.class)
|
||||
@Import({ HazelcastClientConfiguration.class, HazelcastServerConfiguration.class })
|
||||
public class HazelcastAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import com.hazelcast.client.config.ClientConfigRecognizer;
|
||||
import com.hazelcast.config.ConfigStream;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link HazelcastConfigResourceCondition} that checks if the
|
||||
* {@code spring.hazelcast.config} configuration key is defined.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastClientConfigAvailableCondition extends HazelcastConfigResourceCondition {
|
||||
|
||||
HazelcastClientConfigAvailableCondition() {
|
||||
super(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY, "file:./hazelcast-client.xml",
|
||||
"classpath:/hazelcast-client.xml", "file:./hazelcast-client.yaml", "classpath:/hazelcast-client.yaml",
|
||||
"file:./hazelcast-client.yml", "classpath:/hazelcast-client.yml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
if (context.getEnvironment().containsProperty(HAZELCAST_CONFIG_PROPERTY)) {
|
||||
ConditionOutcome configValidationOutcome = HazelcastClientValidation.clientConfigOutcome(context,
|
||||
HAZELCAST_CONFIG_PROPERTY, startConditionMessage());
|
||||
return (configValidationOutcome != null) ? configValidationOutcome : ConditionOutcome
|
||||
.match(startConditionMessage().foundExactly("property " + HAZELCAST_CONFIG_PROPERTY));
|
||||
}
|
||||
return getResourceOutcome(context, metadata);
|
||||
}
|
||||
|
||||
static class HazelcastClientValidation {
|
||||
|
||||
static ConditionOutcome clientConfigOutcome(ConditionContext context, String propertyName, Builder builder) {
|
||||
String resourcePath = context.getEnvironment().getProperty(propertyName);
|
||||
Resource resource = context.getResourceLoader().getResource(resourcePath);
|
||||
if (!resource.exists()) {
|
||||
return ConditionOutcome.noMatch(builder.because("Hazelcast configuration does not exist"));
|
||||
}
|
||||
try (InputStream in = resource.getInputStream()) {
|
||||
boolean clientConfig = new ClientConfigRecognizer().isRecognized(new ConfigStream(in));
|
||||
return new ConditionOutcome(clientConfig, existingConfigurationOutcome(resource, clientConfig));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String existingConfigurationOutcome(Resource resource, boolean client) throws IOException {
|
||||
URL location = resource.getURL();
|
||||
return client ? "Hazelcast client configuration detected at '" + location + "'"
|
||||
: "Hazelcast server configuration detected at '" + location + "'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.client.HazelcastClient;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Configuration for Hazelcast client.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(HazelcastClient.class)
|
||||
@ConditionalOnMissingBean(HazelcastInstance.class)
|
||||
@Import({ HazelcastConnectionDetailsConfiguration.class, HazelcastClientInstanceConfiguration.class })
|
||||
class HazelcastClientConfiguration {
|
||||
|
||||
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.client.config";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.client.HazelcastClient;
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration for Hazelcast client instance.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(HazelcastConnectionDetails.class)
|
||||
class HazelcastClientInstanceConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastInstance hazelcastInstance(HazelcastConnectionDetails hazelcastConnectionDetails) {
|
||||
ClientConfig config = hazelcastConnectionDetails.getClientConfig();
|
||||
return (!StringUtils.hasText(config.getInstanceName())) ? HazelcastClient.newHazelcastClient(config)
|
||||
: HazelcastClient.getOrCreateHazelcastClient(config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the Hazelcast
|
||||
* server {@link Config configuration}.
|
||||
*
|
||||
* @author Jaromir Hamala
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HazelcastConfigCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the configuration.
|
||||
* @param config the {@link Config} to customize
|
||||
*/
|
||||
void customize(Config config);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
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;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@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
|
||||
* @author Madhura Bhave
|
||||
* @author Vedran Pavic
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public abstract class HazelcastConfigResourceCondition extends ResourceCondition {
|
||||
|
||||
protected static final String HAZELCAST_CONFIG_PROPERTY = "spring.hazelcast.config";
|
||||
|
||||
private final String configSystemProperty;
|
||||
|
||||
protected HazelcastConfigResourceCondition(String configSystemProperty, String... resourceLocations) {
|
||||
super("Hazelcast", HAZELCAST_CONFIG_PROPERTY, resourceLocations);
|
||||
Assert.notNull(configSystemProperty, "'configSystemProperty' must not be null");
|
||||
this.configSystemProperty = configSystemProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionOutcome getResourceOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
if (System.getProperty(this.configSystemProperty) != null) {
|
||||
return ConditionOutcome
|
||||
.match(startConditionMessage().because("System property '" + this.configSystemProperty + "' is set."));
|
||||
}
|
||||
return super.getResourceOutcome(context, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a client connection to a Hazelcast instance.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface HazelcastConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* The {@link ClientConfig} for Hazelcast client.
|
||||
* @return the client config
|
||||
*/
|
||||
ClientConfig getClientConfig();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
|
||||
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.ResourceLoader;
|
||||
|
||||
/**
|
||||
* {@link Configuration} for providing {@link HazelcastConnectionDetails}.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(HazelcastConnectionDetails.class)
|
||||
class HazelcastConnectionDetailsConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ClientConfig.class)
|
||||
@Conditional(HazelcastClientConfigAvailableCondition.class)
|
||||
static class HazelcastClientConfigFileConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastConnectionDetails hazelcastConnectionDetails(HazelcastProperties properties,
|
||||
ResourceLoader resourceLoader) {
|
||||
return new PropertiesHazelcastConnectionDetails(properties, resourceLoader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnSingleCandidate(ClientConfig.class)
|
||||
static class HazelcastClientConfigConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastConnectionDetails hazelcastConnectionDetails(ClientConfig config) {
|
||||
return () -> config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.hazelcast.autoconfigure.HazelcastJpaDependencyAutoConfiguration.HazelcastInstanceEntityManagerFactoryDependsOnConfiguration;
|
||||
import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryDependsOnPostProcessor;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
||||
/**
|
||||
* Additional configuration to ensure that {@link EntityManagerFactory} beans depend on
|
||||
* the {@code hazelcastInstance} bean.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = HazelcastAutoConfiguration.class,
|
||||
afterName = "org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfiguration")
|
||||
@ConditionalOnClass({ HazelcastInstance.class, LocalContainerEntityManagerFactoryBean.class })
|
||||
@Import(HazelcastInstanceEntityManagerFactoryDependsOnConfiguration.class)
|
||||
public class HazelcastJpaDependencyAutoConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(EntityManagerFactoryDependsOnPostProcessor.class)
|
||||
@Conditional(OnHazelcastAndJpaCondition.class)
|
||||
@Import(HazelcastInstanceEntityManagerFactoryDependsOnPostProcessor.class)
|
||||
static class HazelcastInstanceEntityManagerFactoryDependsOnConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class HazelcastInstanceEntityManagerFactoryDependsOnPostProcessor
|
||||
extends EntityManagerFactoryDependsOnPostProcessor {
|
||||
|
||||
HazelcastInstanceEntityManagerFactoryDependsOnPostProcessor() {
|
||||
super("hazelcastInstance");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OnHazelcastAndJpaCondition extends AllNestedConditions {
|
||||
|
||||
OnHazelcastAndJpaCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(name = "hazelcastInstance")
|
||||
static class HasHazelcastInstance {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
|
||||
static class HasJpa {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
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 4.0.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) {
|
||||
return null;
|
||||
}
|
||||
Assert.state(this.config.exists(),
|
||||
() -> "Hazelcast configuration does not exist '" + this.config.getDescription() + "'");
|
||||
return this.config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.spring.context.SpringManagedContext;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration for Hazelcast server.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(HazelcastInstance.class)
|
||||
class HazelcastServerConfiguration {
|
||||
|
||||
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config";
|
||||
|
||||
static final String HAZELCAST_LOGGING_TYPE = "hazelcast.logging.type";
|
||||
|
||||
private static HazelcastInstance getHazelcastInstance(Config config) {
|
||||
if (StringUtils.hasText(config.getInstanceName())) {
|
||||
return Hazelcast.getOrCreateHazelcastInstance(config);
|
||||
}
|
||||
return Hazelcast.newHazelcastInstance(config);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(Config.class)
|
||||
@Conditional(ConfigAvailableCondition.class)
|
||||
static class HazelcastServerConfigFileConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastInstance hazelcastInstance(HazelcastProperties properties, ResourceLoader resourceLoader,
|
||||
ObjectProvider<HazelcastConfigCustomizer> hazelcastConfigCustomizers) throws IOException {
|
||||
Resource configLocation = properties.resolveConfigLocation();
|
||||
Config config = (configLocation != null) ? loadConfig(configLocation) : Config.load();
|
||||
config.setClassLoader(resourceLoader.getClassLoader());
|
||||
hazelcastConfigCustomizers.orderedStream().forEach((customizer) -> customizer.customize(config));
|
||||
return getHazelcastInstance(config);
|
||||
}
|
||||
|
||||
private Config loadConfig(Resource configLocation) throws IOException {
|
||||
URL configUrl = configLocation.getURL();
|
||||
Config config = loadConfig(configUrl);
|
||||
if (ResourceUtils.isFileURL(configUrl)) {
|
||||
config.setConfigurationFile(configLocation.getFile());
|
||||
}
|
||||
else {
|
||||
config.setConfigurationUrl(configUrl);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private Config loadConfig(URL configUrl) throws IOException {
|
||||
try (InputStream stream = configUrl.openStream()) {
|
||||
return Config.loadFromStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnSingleCandidate(Config.class)
|
||||
static class HazelcastServerConfigConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastInstance hazelcastInstance(Config config) {
|
||||
return getHazelcastInstance(config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SpringManagedContext.class)
|
||||
static class SpringManagedContextHazelcastConfigCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
HazelcastConfigCustomizer springManagedContextHazelcastConfigCustomizer(ApplicationContext applicationContext) {
|
||||
return (config) -> {
|
||||
SpringManagedContext managementContext = new SpringManagedContext();
|
||||
managementContext.setApplicationContext(applicationContext);
|
||||
config.setManagedContext(managementContext);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(org.slf4j.Logger.class)
|
||||
static class HazelcastLoggingConfigCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
HazelcastConfigCustomizer loggingHazelcastConfigCustomizer() {
|
||||
return (config) -> {
|
||||
if (!config.getProperties().containsKey(HAZELCAST_LOGGING_TYPE)) {
|
||||
config.setProperty(HAZELCAST_LOGGING_TYPE, "slf4j");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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", "file:./hazelcast.yaml",
|
||||
"classpath:/hazelcast.yaml", "file:./hazelcast.yml", "classpath:/hazelcast.yml");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
import com.hazelcast.client.config.XmlClientConfigBuilder;
|
||||
import com.hazelcast.client.config.YamlClientConfigBuilder;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Adapts {@link HazelcastProperties} to {@link HazelcastConnectionDetails}.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class PropertiesHazelcastConnectionDetails implements HazelcastConnectionDetails {
|
||||
|
||||
private final HazelcastProperties properties;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
PropertiesHazelcastConnectionDetails(HazelcastProperties properties, ResourceLoader resourceLoader) {
|
||||
this.properties = properties;
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientConfig getClientConfig() {
|
||||
Resource configLocation = this.properties.resolveConfigLocation();
|
||||
ClientConfig config = (configLocation != null) ? loadClientConfig(configLocation) : ClientConfig.load();
|
||||
config.setClassLoader(this.resourceLoader.getClassLoader());
|
||||
return config;
|
||||
}
|
||||
|
||||
private ClientConfig loadClientConfig(Resource configLocation) {
|
||||
try {
|
||||
URL configUrl = configLocation.getURL();
|
||||
String configFileName = configUrl.getPath().toLowerCase(Locale.ROOT);
|
||||
return (!isYaml(configFileName)) ? new XmlClientConfigBuilder(configUrl).build()
|
||||
: new YamlClientConfigBuilder(configUrl).build();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to load Hazelcast config", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isYaml(String configFileName) {
|
||||
return configFileName.endsWith(".yaml") || configFileName.endsWith(".yml");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Hazelcast.
|
||||
*/
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.hazelcast.autoconfigure.HazelcastAutoConfiguration
|
||||
org.springframework.boot.hazelcast.autoconfigure.HazelcastJpaDependencyAutoConfiguration
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Set;
|
||||
|
||||
import com.hazelcast.client.HazelcastClient;
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
import com.hazelcast.client.impl.clientside.HazelcastClientProxy;
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.config.NetworkConfig;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastAutoConfiguration} specific to the client.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastAutoConfigurationClientTests {
|
||||
|
||||
/**
|
||||
* Servers the test clients will connect to.
|
||||
*/
|
||||
private static HazelcastInstance hazelcastServer;
|
||||
|
||||
private static String endpointAddress;
|
||||
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
Config config = Config.load();
|
||||
NetworkConfig networkConfig = config.getNetworkConfig();
|
||||
networkConfig.setPort(0);
|
||||
networkConfig.setPublicAddress("localhost");
|
||||
hazelcastServer = Hazelcast.newHazelcastInstance(config);
|
||||
InetSocketAddress inetSocketAddress = (InetSocketAddress) hazelcastServer.getLocalEndpoint().getSocketAddress();
|
||||
endpointAddress = inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void close() {
|
||||
if (hazelcastServer != null) {
|
||||
hazelcastServer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void systemPropertyWithXml() {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.xml");
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY + "=" + config.getAbsolutePath())
|
||||
.run(assertSpecificHazelcastClient("explicit-xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void systemPropertyWithYaml() {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.yaml");
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY + "=" + config.getAbsolutePath())
|
||||
.run(assertSpecificHazelcastClient("explicit-yaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void systemPropertyWithYml() {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.yml");
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastClientConfiguration.CONFIG_SYSTEM_PROPERTY + "=" + config.getAbsolutePath())
|
||||
.run(assertSpecificHazelcastClient("explicit-yml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithXml() throws MalformedURLException {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.xml");
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=" + config.toURI().toURL())
|
||||
.run(assertSpecificHazelcastClient("explicit-xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithYaml() throws MalformedURLException {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.yaml");
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=" + config.toURI().toURL())
|
||||
.run(assertSpecificHazelcastClient("explicit-yaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithYml() throws MalformedURLException {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.yml");
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=" + config.toURI().toURL())
|
||||
.run(assertSpecificHazelcastClient("explicit-yml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownConfigFile() {
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=foo/bar/unknown.xml")
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("foo/bar/unknown.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientConfigTakesPrecedence() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastServerAndClientConfig.class)
|
||||
.withPropertyValues("spring.hazelcast.config=this-is-ignored.xml")
|
||||
.run((context) -> assertThat(context).getBean(HazelcastInstance.class)
|
||||
.isInstanceOf(HazelcastClientProxy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsTakesPrecedenceOverConfigFile() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastConnectionDetailsConfig.class)
|
||||
.withPropertyValues("spring.hazelcast.config=this-is-ignored.xml")
|
||||
.run(assertSpecificHazelcastClient("connection-details"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsTakesPrecedenceOverUserDefinedClientConfig() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(HazelcastConnectionDetailsConfig.class, HazelcastServerAndClientConfig.class)
|
||||
.withPropertyValues("spring.hazelcast.config=this-is-ignored.xml")
|
||||
.run(assertSpecificHazelcastClient("connection-details"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientConfigWithInstanceNameCreatesClientIfNecessary() throws MalformedURLException {
|
||||
assertThat(HazelcastClient.getHazelcastClientByName("spring-boot")).isNull();
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-instance.xml");
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=" + config.toURI().toURL())
|
||||
.run((context) -> assertThat(context).getBean(HazelcastInstance.class)
|
||||
.extracting(HazelcastInstance::getName)
|
||||
.isEqualTo("spring-boot"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguredClientConfigUsesApplicationClassLoader() throws MalformedURLException {
|
||||
File config = prepareConfiguration("src/test/resources/org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-client-specific.xml");
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=" + config.toURI().toURL()).run((context) -> {
|
||||
HazelcastInstance hazelcast = context.getBean(HazelcastInstance.class);
|
||||
assertThat(hazelcast).isInstanceOf(HazelcastClientProxy.class);
|
||||
ClientConfig clientConfig = ((HazelcastClientProxy) hazelcast).getClientConfig();
|
||||
assertThat(clientConfig.getClassLoader()).isSameAs(context.getSourceApplicationContext().getClassLoader());
|
||||
});
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> assertSpecificHazelcastClient(String label) {
|
||||
return (context) -> assertThat(context).getBean(HazelcastInstance.class)
|
||||
.isInstanceOf(HazelcastInstance.class)
|
||||
.has(labelEqualTo(label));
|
||||
}
|
||||
|
||||
private static Condition<HazelcastInstance> labelEqualTo(String label) {
|
||||
return new Condition<>((o) -> ((HazelcastClientProxy) o).getClientConfig()
|
||||
.getLabels()
|
||||
.stream()
|
||||
.anyMatch((e) -> e.equals(label)), "Label equals to " + label);
|
||||
}
|
||||
|
||||
private File prepareConfiguration(String input) {
|
||||
File configFile = new File(input);
|
||||
try {
|
||||
String config = FileCopyUtils.copyToString(new FileReader(configFile));
|
||||
config = config.replace("${address}", endpointAddress);
|
||||
System.out.println(config);
|
||||
File outputFile = new File(Files.createTempDirectory(getClass().getSimpleName()).toFile(),
|
||||
configFile.getName());
|
||||
FileCopyUtils.copy(config, new FileWriter(outputFile));
|
||||
return outputFile;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastConnectionDetailsConfig {
|
||||
|
||||
@Bean
|
||||
HazelcastConnectionDetails hazelcastConnectionDetails() {
|
||||
ClientConfig config = new ClientConfig();
|
||||
config.setLabels(Set.of("connection-details"));
|
||||
config.getConnectionStrategyConfig().getConnectionRetryConfig().setClusterConnectTimeoutMillis(60000);
|
||||
config.getNetworkConfig().getAddresses().add(endpointAddress);
|
||||
return () -> config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastServerAndClientConfig {
|
||||
|
||||
@Bean
|
||||
Config config() {
|
||||
return new Config();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ClientConfig clientConfig() {
|
||||
ClientConfig config = new ClientConfig();
|
||||
config.getConnectionStrategyConfig().getConnectionRetryConfig().setClusterConnectTimeoutMillis(60000);
|
||||
config.getNetworkConfig().getAddresses().add(endpointAddress);
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.config.JoinConfig;
|
||||
import com.hazelcast.config.QueueConfig;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.map.EntryProcessor;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.hazelcast.spring.context.SpringAware;
|
||||
import com.hazelcast.spring.context.SpringManagedContext;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithResource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastAutoConfiguration} when the client library is not present.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastAutoConfigurationServerTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void defaultConfigFile() {
|
||||
// hazelcast.xml present in root classpath
|
||||
this.contextRunner.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getConfigurationUrl()).isEqualTo(new ClassPathResource("hazelcast.xml").getURL());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void systemPropertyWithXml() {
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY
|
||||
+ "=classpath:org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.xml")
|
||||
.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getMapConfigs().keySet()).containsOnly("foobar");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void systemPropertyWithYaml() {
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY
|
||||
+ "=classpath:org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yaml")
|
||||
.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getMapConfigs().keySet()).containsOnly("foobar");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void systemPropertyWithYml() {
|
||||
this.contextRunner
|
||||
.withSystemProperties(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY
|
||||
+ "=classpath:org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yml")
|
||||
.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getMapConfigs().keySet()).containsOnly("foobar");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigFileWithXml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=org/springframework/boot/hazelcast/autoconfigure/"
|
||||
+ "hazelcast-specific.xml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigFileWithYaml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=org/springframework/boot/hazelcast/autoconfigure/"
|
||||
+ "hazelcast-specific.yaml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigFileWithYml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=org/springframework/boot/hazelcast/autoconfigure/"
|
||||
+ "hazelcast-specific.yml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithXml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=classpath:org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-specific.xml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithYaml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=classpath:org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-specific.yaml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigUrlWithYml() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.hazelcast.config=classpath:org/springframework/"
|
||||
+ "boot/hazelcast/autoconfigure/hazelcast-specific.yml")
|
||||
.run(assertSpecificHazelcastServer(
|
||||
"org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.yml"));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> assertSpecificHazelcastServer(String location) {
|
||||
return (context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
String configurationLocation = (config.getConfigurationUrl() != null)
|
||||
? config.getConfigurationUrl().toString()
|
||||
: config.getConfigurationFile().toURI().toURL().toString();
|
||||
assertThat(configurationLocation).endsWith(location);
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownConfigFile() {
|
||||
this.contextRunner.withPropertyValues("spring.hazelcast.config=foo/bar/unknown.xml")
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("foo/bar/unknown.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void configInstanceWithName() {
|
||||
Config config = createTestConfig("my-test-instance");
|
||||
HazelcastInstance existing = Hazelcast.newHazelcastInstance(config);
|
||||
try {
|
||||
this.contextRunner.withUserConfiguration(HazelcastConfigWithName.class)
|
||||
.withPropertyValues("spring.hazelcast.config=this-is-ignored.xml")
|
||||
.run((context) -> {
|
||||
HazelcastInstance hazelcast = context.getBean(HazelcastInstance.class);
|
||||
assertThat(hazelcast.getConfig().getInstanceName()).isEqualTo("my-test-instance");
|
||||
// Should reuse any existing instance by default.
|
||||
assertThat(hazelcast).isEqualTo(existing);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
existing.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void configInstanceWithoutName() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastConfigNoName.class)
|
||||
.withPropertyValues("spring.hazelcast.config=this-is-ignored.xml")
|
||||
.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
Map<String, QueueConfig> queueConfigs = config.getQueueConfigs();
|
||||
assertThat(queueConfigs.keySet()).containsOnly("another-queue");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredConfigUsesApplicationClassLoader() {
|
||||
this.contextRunner.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getClassLoader()).isSameAs(context.getSourceApplicationContext().getClassLoader());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredConfigUsesSpringManagedContext() {
|
||||
this.contextRunner.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getManagedContext()).isInstanceOf(SpringManagedContext.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredConfigCanUseSpringAwareComponent() {
|
||||
this.contextRunner.withPropertyValues("test.hazelcast.key=42").run((context) -> {
|
||||
HazelcastInstance hz = context.getBean(HazelcastInstance.class);
|
||||
IMap<String, String> map = hz.getMap("test");
|
||||
assertThat(map.executeOnKey("test.hazelcast.key", new SpringAwareEntryProcessor<>())).isEqualTo("42");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredConfigWithoutHazelcastSpringDoesNotUseSpringManagedContext() {
|
||||
this.contextRunner
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(Thread.currentThread().getContextClassLoader(), SpringManagedContext.class))
|
||||
.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getManagedContext()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredContextCanOverrideManagementContextUsingCustomizer() {
|
||||
this.contextRunner.withBean(TestHazelcastConfigCustomizer.class).run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getManagedContext()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithHazelcastXmlResource
|
||||
void autoConfiguredConfigSetsHazelcastLoggingToSlf4j() {
|
||||
this.contextRunner.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getProperty(HazelcastServerConfiguration.HAZELCAST_LOGGING_TYPE)).isEqualTo("slf4j");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguredConfigCanOverrideHazelcastLogging() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastConfigWithJDKLogging.class).run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getProperty(HazelcastServerConfiguration.HAZELCAST_LOGGING_TYPE)).isEqualTo("jdk");
|
||||
});
|
||||
}
|
||||
|
||||
private static Config createTestConfig(String instanceName) {
|
||||
Config config = new Config(instanceName);
|
||||
JoinConfig join = config.getNetworkConfig().getJoin();
|
||||
join.getAutoDetectionConfig().setEnabled(false);
|
||||
join.getMulticastConfig().setEnabled(false);
|
||||
return config;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastConfigWithName {
|
||||
|
||||
@Bean
|
||||
Config myHazelcastConfig() {
|
||||
return new Config("my-test-instance");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastConfigNoName {
|
||||
|
||||
@Bean
|
||||
Config anotherHazelcastConfig() {
|
||||
Config config = createTestConfig("another-test-instance");
|
||||
config.addQueueConfig(new QueueConfig("another-queue"));
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastConfigWithJDKLogging {
|
||||
|
||||
@Bean
|
||||
Config anotherHazelcastConfig() {
|
||||
Config config = new Config();
|
||||
config.setProperty(HazelcastServerConfiguration.HAZELCAST_LOGGING_TYPE, "jdk");
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpringAware
|
||||
static class SpringAwareEntryProcessor<V> implements EntryProcessor<String, V, String> {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public String process(Map.Entry<String, V> entry) {
|
||||
return this.environment.getProperty(entry.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Order(1)
|
||||
static class TestHazelcastConfigCustomizer implements HazelcastConfigCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(Config config) {
|
||||
config.setManagedContext(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@WithResource(name = "hazelcast.xml", content = """
|
||||
<hazelcast
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-5.0.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<instance-name>default-instance</instance-name>
|
||||
<map name="defaultCache" />
|
||||
<network>
|
||||
<join>
|
||||
<auto-detection enabled="false" />
|
||||
<multicast enabled="false" />
|
||||
</join>
|
||||
</network>
|
||||
</hazelcast>
|
||||
""")
|
||||
@interface WithHazelcastXmlResource {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastAutoConfiguration} with full classpath.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
@WithResource(name = "hazelcast.xml", content = """
|
||||
<hazelcast
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-5.0.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<instance-name>default-instance</instance-name>
|
||||
<map name="defaultCache" />
|
||||
<network>
|
||||
<join>
|
||||
<auto-detection enabled="false" />
|
||||
<multicast enabled="false" />
|
||||
</join>
|
||||
</network>
|
||||
</hazelcast>
|
||||
""")
|
||||
@WithResource(name = "hazelcast.yml", content = """
|
||||
hazelcast:
|
||||
network:
|
||||
join:
|
||||
auto-detection:
|
||||
enabled: false
|
||||
multicast:
|
||||
enabled: false
|
||||
""")
|
||||
@WithResource(name = "hazelcast.yaml", content = """
|
||||
hazelcast:
|
||||
network:
|
||||
join:
|
||||
auto-detection:
|
||||
enabled: false
|
||||
multicast:
|
||||
enabled: false
|
||||
""")
|
||||
void defaultConfigFileIsHazelcastXml() {
|
||||
// no hazelcast-client.xml and hazelcast.xml is present in root classpath
|
||||
// this also asserts that XML has priority over YAML
|
||||
// as hazelcast.yaml, hazelcast.yml, and hazelcast.xml are available.
|
||||
this.contextRunner.run((context) -> {
|
||||
Config config = context.getBean(HazelcastInstance.class).getConfig();
|
||||
assertThat(config.getConfigurationUrl()).isEqualTo(new ClassPathResource("hazelcast.xml").getURL());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastClientConfigAvailableCondition}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastClientConfigAvailableConditionTests {
|
||||
|
||||
private final HazelcastClientConfigAvailableCondition condition = new HazelcastClientConfigAvailableCondition();
|
||||
|
||||
@Test
|
||||
void explicitConfigurationWithClientConfigMatches() {
|
||||
ConditionOutcome outcome = getMatchOutcome(new MockEnvironment().withProperty("spring.hazelcast.config",
|
||||
"classpath:org/springframework/boot/hazelcast/autoconfigure/hazelcast-client-specific.xml"));
|
||||
assertThat(outcome.isMatch()).isTrue();
|
||||
assertThat(outcome.getMessage()).contains("Hazelcast client configuration detected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigurationWithServerConfigDoesNotMatch() {
|
||||
ConditionOutcome outcome = getMatchOutcome(new MockEnvironment().withProperty("spring.hazelcast.config",
|
||||
"classpath:org/springframework/boot/hazelcast/autoconfigure/hazelcast-specific.xml"));
|
||||
assertThat(outcome.isMatch()).isFalse();
|
||||
assertThat(outcome.getMessage()).contains("Hazelcast server configuration detected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitConfigurationWithMissingConfigDoesNotMatch() {
|
||||
ConditionOutcome outcome = getMatchOutcome(new MockEnvironment().withProperty("spring.hazelcast.config",
|
||||
"classpath:org/springframework/boot/hazelcast/autoconfigure/test-config-does-not-exist.xml"));
|
||||
assertThat(outcome.isMatch()).isFalse();
|
||||
assertThat(outcome.getMessage()).contains("Hazelcast configuration does not exist");
|
||||
}
|
||||
|
||||
private ConditionOutcome getMatchOutcome(Environment environment) {
|
||||
ConditionContext conditionContext = mock(ConditionContext.class);
|
||||
given(conditionContext.getEnvironment()).willReturn(environment);
|
||||
given(conditionContext.getResourceLoader()).willReturn(new DefaultResourceLoader());
|
||||
return this.condition.getMatchOutcome(conditionContext, mock(AnnotatedTypeMetadata.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.hazelcast.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.hazelcast.autoconfigure.HazelcastJpaDependencyAutoConfiguration.HazelcastInstanceEntityManagerFactoryDependsOnPostProcessor;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryDependsOnPostProcessor;
|
||||
import org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastJpaDependencyAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HazelcastJpaDependencyAutoConfigurationTests {
|
||||
|
||||
private static final String POST_PROCESSOR_BEAN_NAME = HazelcastInstanceEntityManagerFactoryDependsOnPostProcessor.class
|
||||
.getName();
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
|
||||
HazelcastJpaDependencyAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true");
|
||||
|
||||
@Test
|
||||
void registrationIfHazelcastInstanceHasRegularBeanName() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class).run((context) -> {
|
||||
assertThat(postProcessors(context)).containsKey(POST_PROCESSOR_BEAN_NAME);
|
||||
assertThat(entityManagerFactoryDependencies(context)).contains("hazelcastInstance");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void noRegistrationIfHazelcastInstanceHasCustomBeanName() {
|
||||
this.contextRunner.withUserConfiguration(HazelcastCustomNameConfiguration.class).run((context) -> {
|
||||
assertThat(entityManagerFactoryDependencies(context)).doesNotContain("hazelcastInstance");
|
||||
assertThat(postProcessors(context)).doesNotContainKey(POST_PROCESSOR_BEAN_NAME);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void noRegistrationWithNoHazelcastInstance() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(entityManagerFactoryDependencies(context)).doesNotContain("hazelcastInstance");
|
||||
assertThat(postProcessors(context)).doesNotContainKey(POST_PROCESSOR_BEAN_NAME);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void noRegistrationWithNoEntityManagerFactory() {
|
||||
new ApplicationContextRunner().withUserConfiguration(HazelcastConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastJpaDependencyAutoConfiguration.class))
|
||||
.run((context) -> assertThat(postProcessors(context)).doesNotContainKey(POST_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
private Map<String, EntityManagerFactoryDependsOnPostProcessor> postProcessors(
|
||||
AssertableApplicationContext context) {
|
||||
return context.getBeansOfType(EntityManagerFactoryDependsOnPostProcessor.class);
|
||||
}
|
||||
|
||||
private List<String> entityManagerFactoryDependencies(AssertableApplicationContext context) {
|
||||
String[] dependsOn = ((BeanDefinitionRegistry) context.getSourceApplicationContext())
|
||||
.getBeanDefinition("entityManagerFactory")
|
||||
.getDependsOn();
|
||||
return (dependsOn != null) ? Arrays.asList(dependsOn) : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastInstance hazelcastInstance() {
|
||||
return mock(HazelcastInstance.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HazelcastCustomNameConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastInstance myHazelcastInstance() {
|
||||
return mock(HazelcastInstance.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config hazelcast-client-config-5.0.xsd">
|
||||
<instance-name>spring-boot</instance-name>
|
||||
<connection-strategy>
|
||||
<connection-retry>
|
||||
<cluster-connect-timeout-millis>60000</cluster-connect-timeout-millis>
|
||||
</connection-retry>
|
||||
</connection-strategy>
|
||||
<network>
|
||||
<cluster-members>
|
||||
<address>${address}</address>
|
||||
</cluster-members>
|
||||
</network>
|
||||
</hazelcast-client>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config hazelcast-client-config-5.0.xsd">
|
||||
<client-labels>
|
||||
<label>explicit-xml</label>
|
||||
</client-labels>
|
||||
<connection-strategy>
|
||||
<connection-retry>
|
||||
<cluster-connect-timeout-millis>60000</cluster-connect-timeout-millis>
|
||||
</connection-retry>
|
||||
</connection-strategy>
|
||||
<network>
|
||||
<cluster-members>
|
||||
<address>${address}</address>
|
||||
</cluster-members>
|
||||
</network>
|
||||
</hazelcast-client>
|
||||
@@ -0,0 +1,9 @@
|
||||
hazelcast-client:
|
||||
client-labels:
|
||||
- explicit-yaml
|
||||
connection-strategy:
|
||||
connection-retry:
|
||||
cluster-connect-timeout-millis: 60000
|
||||
network:
|
||||
cluster-members:
|
||||
- ${address}
|
||||
@@ -0,0 +1,9 @@
|
||||
hazelcast-client:
|
||||
client-labels:
|
||||
- explicit-yml
|
||||
connection-strategy:
|
||||
connection-retry:
|
||||
cluster-connect-timeout-millis: 60000
|
||||
network:
|
||||
cluster-members:
|
||||
- ${address}
|
||||
@@ -0,0 +1,19 @@
|
||||
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-5.0.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<queue name="foobar"/>
|
||||
|
||||
<map name="foobar">
|
||||
<time-to-live-seconds>3600</time-to-live-seconds>
|
||||
<max-idle-seconds>600</max-idle-seconds>
|
||||
</map>
|
||||
|
||||
<network>
|
||||
<join>
|
||||
<auto-detection enabled="false" />
|
||||
<multicast enabled="false"/>
|
||||
</join>
|
||||
</network>
|
||||
|
||||
</hazelcast>
|
||||
@@ -0,0 +1,12 @@
|
||||
hazelcast:
|
||||
network:
|
||||
join:
|
||||
auto-detection:
|
||||
enabled: false
|
||||
multicast:
|
||||
enabled: false
|
||||
|
||||
map:
|
||||
foobar:
|
||||
time-to-live-seconds: 3600
|
||||
max-idle-seconds: 600
|
||||
@@ -0,0 +1,12 @@
|
||||
hazelcast:
|
||||
network:
|
||||
join:
|
||||
auto-detection:
|
||||
enabled: false
|
||||
multicast:
|
||||
enabled: false
|
||||
|
||||
map:
|
||||
foobar:
|
||||
time-to-live-seconds: 3600
|
||||
max-idle-seconds: 600
|
||||
Reference in New Issue
Block a user