diff --git a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java index fedf9162f..2750e837f 100644 --- a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java +++ b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java @@ -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 { + private final ApplicationContext context; + + InitializerWithOuterContext(ApplicationContext context) { + this.context = context; + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.getBeanFactory().registerSingleton("outerContext", context); + } + } + } diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java index 5737f6871..bcec896f5 100644 --- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java +++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java @@ -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 { diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1.java index 0f5ca7b77..8d4021fad 100644 --- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1.java +++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1.java @@ -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 { 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; } diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1Configuration.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1Configuration.java index eef040acd..66f147ef6 100644 --- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1Configuration.java +++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/stub1/StubBinder1Configuration.java @@ -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