From daccfb5e588b47b213745acc71e2ecd342138cef Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 19 Jul 2018 08:27:44 -0400 Subject: [PATCH] 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 --- .../stream/binder/DefaultBinderFactory.java | 25 +++++++++++++-- .../BinderFactoryConfigurationTests.java | 32 +++++++++++++++++++ .../stream/binder/stub1/StubBinder1.java | 12 +++++++ .../stub1/StubBinder1Configuration.java | 19 +++++++++-- 4 files changed, 84 insertions(+), 4 deletions(-) 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