Properties propagation in custom environment

Currently when the binder configuration provide properties in a custom environment,
Spring Cloud Stream does not include any beans from the outer context.
This change will ensure that in such cases, the binder context will have access
to the outer context as a non-parent bean.

Resolves #1420
Resolves #1423
This commit is contained in:
Soby Chacko
2018-07-19 08:27:44 -04:00
committed by Oleg Zhurakousky
parent 3bfe1775af
commit daccfb5e58
4 changed files with 84 additions and 4 deletions

View File

@@ -35,6 +35,7 @@ import org.springframework.cloud.stream.config.SpelExpressionConverterConfigurat
import org.springframework.cloud.stream.reflection.GenericsUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
@@ -50,6 +51,7 @@ import org.springframework.util.StringUtils;
* @author Ilayaperumal Gopinathan
* @author Gary Russell
* @author Oleg Zhurakousky
* @author Soby Chacko
*/
public class DefaultBinderFactory implements BinderFactory, DisposableBean, ApplicationContextAware {
@@ -88,8 +90,8 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl
}
@Override
public void destroy() throws Exception {
this.binderInstanceCache.values().stream().map(e -> e.getValue()).forEach(ctx -> ctx.close());
public void destroy() {
this.binderInstanceCache.values().stream().map(Entry::getValue).forEach(ConfigurableApplicationContext::close);
this.defaultBinderForBindingTargetType.clear();
}
@@ -229,6 +231,11 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl
if (useApplicationContextAsParent) {
springApplicationBuilder.parent(this.context);
}
// If the current application context is not set as parent and the environment is set,
// provide the current context as an additional bean in the BeanFactory.
if (environment != null && !useApplicationContextAsParent){
springApplicationBuilder.initializers(new InitializerWithOuterContext(this.context));
}
if (environment != null && (useApplicationContextAsParent || binderConfiguration.isInheritEnvironment())) {
StandardEnvironment binderEnvironment = new StandardEnvironment();
@@ -288,4 +295,18 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl
void afterBinderContextInitialized(String configurationName,
ConfigurableApplicationContext binderContext);
}
private static class InitializerWithOuterContext implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final ApplicationContext context;
InitializerWithOuterContext(ApplicationContext context) {
this.context = context;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton("outerContext", context);
}
}
}

View File

@@ -45,6 +45,7 @@ import static org.junit.Assert.fail;
/**
* @author Marius Bogoevici
* @author Ilayaperumal Gopinathan
* @author Soby Chacko
*/
public class BinderFactoryConfigurationTests {
@@ -138,6 +139,37 @@ public class BinderFactoryConfigurationTests {
assertThat(binderFactory.getBinder(null, MessageChannel.class)).isSameAs(binder1);
}
@SuppressWarnings("rawtypes")
@Test
public void testCustomEnvironmentHasAccessToOuterContext() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] { "binder1" }, "binder1.name=foo",
"spring.cloud.stream.binders.custom.environment.foo=bar",
"spring.cloud.stream.binders.custom.type=binder1");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("custom", MessageChannel.class);
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
assertThat(binder1).hasFieldOrPropertyWithValue("outerContext", context);
assertThat(binderFactory.getBinder(null, MessageChannel.class)).isSameAs(binder1);
}
@SuppressWarnings("rawtypes")
@Test
public void testStandardBinderDoesNotHaveTheOuterContextBean() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] { "binder1" }, "binder1.name=foo");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
assertThat(((StubBinder1) binder1).getOuterContext()).isNull();
}
@SuppressWarnings("rawtypes")
@Test
public void loadBinderTypeRegistryWithTwoBinders() throws Exception {

View File

@@ -20,15 +20,27 @@ import org.springframework.cloud.stream.binder.Binder;
import org.springframework.cloud.stream.binder.Binding;
import org.springframework.cloud.stream.binder.ConsumerProperties;
import org.springframework.cloud.stream.binder.ProducerProperties;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author Marius Bogoevici
* @author Mark Fisher
* @author Soby Chacko
*/
public class StubBinder1 implements Binder<Object, ConsumerProperties, ProducerProperties> {
private String name;
private ConfigurableApplicationContext outerContext;
public ConfigurableApplicationContext getOuterContext() {
return outerContext;
}
public void setOuterContext(ConfigurableApplicationContext outerContext) {
this.outerContext = outerContext;
}
public String getName() {
return this.name;
}

View File

@@ -16,16 +16,20 @@
package org.springframework.cloud.stream.binder.stub1;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.stream.binder.Binder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Marius Bogoevici
* @author Soby Chacko
*/
@Configuration
@EnableConfigurationProperties
@@ -33,8 +37,19 @@ public class StubBinder1Configuration {
@Bean
@ConfigurationProperties("binder1")
public Binder<?, ?, ?> binder() {
return new StubBinder1();
public Binder<?, ?, ?> binder(BeanFactory beanFactory) {
StubBinder1 stubBinder1 = new StubBinder1();
ConfigurableApplicationContext outerContext = null;
try {
outerContext = (ConfigurableApplicationContext) beanFactory.getBean("outerContext");
}
catch (BeansException be) {
//Pass through
}
if (outerContext != null) {
stubBinder1.setOuterContext(outerContext);
}
return stubBinder1;
}
@Bean