Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow injection of Spring-managed beans into Hazelcast components #28801

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -25,8 +25,12 @@
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;
Expand Down Expand Up @@ -60,10 +64,14 @@ private static HazelcastInstance getHazelcastInstance(Config config) {
static class HazelcastServerConfigFileConfiguration {

@Bean
HazelcastInstance hazelcastInstance(HazelcastProperties properties, ResourceLoader resourceLoader)
HazelcastInstance hazelcastInstance(HazelcastProperties properties, ResourceLoader resourceLoader,
ObjectProvider<ConfigurationCustomizer> customizerProvider)
throws IOException {
Resource configLocation = properties.resolveConfigLocation();
Config config = (configLocation != null) ? loadConfig(configLocation) : Config.load();
customizerProvider.ifAvailable(c -> {
c.customize(config);
});
config.setClassLoader(resourceLoader.getClassLoader());
return getHazelcastInstance(config);
}
Expand Down Expand Up @@ -93,14 +101,36 @@ private static Config loadConfig(URL configUrl) throws IOException {
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(Config.class)
static class HazelcastServerConfigConfiguration {

@Bean
HazelcastInstance hazelcastInstance(Config config) {
return getHazelcastInstance(config);
}

}

@FunctionalInterface
private interface ConfigurationCustomizer {
jerrinot marked this conversation as resolved.
Show resolved Hide resolved
jerrinot marked this conversation as resolved.
Show resolved Hide resolved
void customize(Config configuration);
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.hazelcast.spring.context.SpringManagedContext")
static class HazelcastConfigCustomizerConfiguration {
private final ApplicationContext applicationContext;

public HazelcastConfigCustomizerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Bean
public ConfigurationCustomizer springManagedContextConfigurationCustomizer() {
return configuration -> {
SpringManagedContext springManagedContext = new SpringManagedContext(applicationContext);
configuration.setManagedContext(springManagedContext);
};
}
}

/**
* {@link HazelcastConfigResourceCondition} that checks if the
* {@code spring.hazelcast.config} configuration key is defined.
Expand Down
Expand Up @@ -22,14 +22,21 @@
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.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.ClassPathExclusions;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
Expand Down Expand Up @@ -164,6 +171,55 @@ void autoConfiguredConfigUsesApplicationClassLoader() {
});
}

@Test
void defaultConfigFile_injectManagedContext() {
this.contextRunner.run((context) -> {
HazelcastInstance hz = context.getBean(HazelcastInstance.class);
IMap<Integer, Integer> map = hz.getMap("myMap");
boolean contextInjected = map.executeOnKey(42, new SpringAwareEntryProcessor<>());
assertThat(contextInjected).isEqualTo(true);
});
}

@Test
void defaultConfigFile_injectManagedContext_SpringHazelcastModuleNotAvailable() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(SpringManagedContext.class))
.run((context) -> {
HazelcastInstance hz = context.getBean(HazelcastInstance.class);
IMap<Integer, Integer> map = hz.getMap("myMap");
boolean contextInjected = map.executeOnKey(42, new SpringAwareEntryProcessor<>());
assertThat(contextInjected).isEqualTo(false);
});
}

@Test
void explicitConfigFile_injectManagedContext() {
this.contextRunner
.withSystemProperties(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY
+ "=classpath:org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.yaml")
.run((context) -> {
HazelcastInstance hz = context.getBean(HazelcastInstance.class);
IMap<Integer, Integer> map = hz.getMap("myMap");
boolean contextInjected = map.executeOnKey(42, new SpringAwareEntryProcessor<>());
assertThat(contextInjected).isEqualTo(true);
});
}

@Test
void explicitConfigFile_injectManagedContext_SpringHazelcastModuleNotAvailable() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(SpringManagedContext.class))
.withSystemProperties(HazelcastServerConfiguration.CONFIG_SYSTEM_PROPERTY
+ "=classpath:org/springframework/boot/autoconfigure/hazelcast/hazelcast-specific.yaml")
.run((context) -> {
HazelcastInstance hz = context.getBean(HazelcastInstance.class);
IMap<Integer, Integer> map = hz.getMap("myMap");
boolean contextInjected = map.executeOnKey(42, new SpringAwareEntryProcessor<>());
assertThat(contextInjected).isEqualTo(false);
});
}

@Configuration(proxyBeanMethods = false)
static class HazelcastConfigWithName {

Expand All @@ -174,6 +230,17 @@ Config myHazelcastConfig() {

}

@SpringAware
static class SpringAwareEntryProcessor<K, V> implements EntryProcessor<K, V, Boolean> {
@Autowired
private ApplicationContext context;

@Override
public Boolean process(Map.Entry<K, V> entry) {
return context != null;
}
}

@Configuration(proxyBeanMethods = false)
static class HazelcastConfigNoName {

Expand Down