Not enforcing EnableAutoConfiguration on aggregate parent

- The aggregate parent configuration now imports the required auto configuration classes such as ChannelBindingAutoConfiguration and EndpointAutoConfiguration
while `@EnableBinding` is still at the parent configuration to be able to share the binding configuration among the child applications
 - Add `ConditionalOnMissingBean` in spring cloud stream auto configuration classes so that if `EnableAutoConfiguration` is enabled at child application classes
they don't get instantiated again
 - Conditionally import EmbeddedServlet auto-configuration when web environment is enabled for parent
 - Add test

Resolves #737
This commit is contained in:
Ilayaperumal Gopinathan
2017-02-15 17:01:43 +05:30
committed by Janne Valkealahti
parent 2df9f9b4ee
commit 8792423abc
7 changed files with 64 additions and 19 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 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.
@@ -51,6 +51,7 @@ public class KryoCodecAutoConfiguration {
KryoCodecProperties kryoCodecProperties;
@Bean
@ConditionalOnMissingBean(PojoCodec.class)
public PojoCodec codec() {
Map<String, KryoRegistrar> kryoRegistrarMap = applicationContext.getBeansOfType(KryoRegistrar
.class);
@@ -58,6 +59,7 @@ public class KryoCodecAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean(KryoRegistrar.class)
public KryoRegistrar fileRegistrar() {
return new FileKryoRegistrar();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -16,11 +16,9 @@
package org.springframework.cloud.stream.reactive;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.stream.binding.BindingService;
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
import org.springframework.context.annotation.Bean;
@@ -33,9 +31,8 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(BindingService.class)
public class ReactiveSupportAutoConfiguration {
private static Log log = LogFactory.getLog(ReactiveSupportAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean(MessageChannelToInputFluxParameterAdapter.class)
public MessageChannelToInputFluxParameterAdapter messageChannelToInputFluxArgumentAdapter(
CompositeMessageConverterFactory compositeMessageConverterFactory) {
return new MessageChannelToInputFluxParameterAdapter(
@@ -43,11 +40,13 @@ public class ReactiveSupportAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean(MessageChannelToFluxSenderParameterAdapter.class)
public MessageChannelToFluxSenderParameterAdapter messageChannelToFluxSenderArgumentAdapter() {
return new MessageChannelToFluxSenderParameterAdapter();
}
@Bean
@ConditionalOnMissingBean(FluxToMessageChannelResultAdapter.class)
public FluxToMessageChannelResultAdapter fluxToMessageChannelResultAdapter() {
return new FluxToMessageChannelResultAdapter();
}
@@ -57,18 +56,21 @@ public class ReactiveSupportAutoConfiguration {
public static class RxJava1SupportConfiguration {
@Bean
@ConditionalOnMissingBean(MessageChannelToInputObservableParameterAdapter.class)
public MessageChannelToInputObservableParameterAdapter messageChannelToInputObservableArgumentAdapter(
MessageChannelToInputFluxParameterAdapter messageChannelToFluxArgumentAdapter) {
return new MessageChannelToInputObservableParameterAdapter(messageChannelToFluxArgumentAdapter);
}
@Bean
@ConditionalOnMissingBean(MessageChannelToObservableSenderParameterAdapter.class)
public MessageChannelToObservableSenderParameterAdapter messageChannelToObservableSenderArgumentAdapter(
MessageChannelToFluxSenderParameterAdapter messageChannelToFluxSenderArgumentAdapter) {
return new MessageChannelToObservableSenderParameterAdapter(messageChannelToFluxSenderArgumentAdapter);
}
@Bean
@ConditionalOnMissingBean(ObservableToMessageChannelResultAdapter.class)
public ObservableToMessageChannelResultAdapter
observableToMessageChannelResultAdapter(
FluxToMessageChannelResultAdapter fluxToMessageChannelResultAdapter) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -19,6 +19,7 @@ package org.springframework.cloud.stream.schema.avro;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.stream.binder.StringConvertingContentTypeResolver;
@@ -42,6 +43,7 @@ public class AvroMessageConverterAutoConfiguration {
private AvroMessageConverterProperties avroMessageConverterProperties;
@Bean
@ConditionalOnMissingBean(AvroSchemaRegistryClientMessageConverter.class)
public AvroSchemaRegistryClientMessageConverter avroSchemaMessageConverter(
SchemaRegistryClient schemaRegistryClient) {
AvroSchemaRegistryClientMessageConverter

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -25,6 +25,7 @@ import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.Transformer;
@@ -41,7 +42,7 @@ public class AggregateTestWithMain {
@Test
public void testAggregateApplication() throws InterruptedException {
// emulate a main method
ConfigurableApplicationContext context = new AggregateApplicationBuilder().from(UppercaseProcessor.class)
ConfigurableApplicationContext context = new AggregateApplicationBuilder(TestSupportBinderAutoConfiguration.class).from(UppercaseProcessor.class)
.namespace("upper").to(SuffixProcessor.class).namespace("suffix").run();
AggregateApplication aggregateAccessor = context.getBean(AggregateApplication.class);

View File

@@ -29,16 +29,19 @@ import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.bind.PropertySourcesPropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedNames;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binding.BindableProxyFactory;
import org.springframework.cloud.stream.config.ChannelBindingAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
@@ -77,11 +80,11 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
private boolean webEnvironment = true;
public AggregateApplicationBuilder(String... args) {
this(new Object[] { ParentConfiguration.class }, args);
this(new Object[]{ParentConfiguration.class}, args);
}
public AggregateApplicationBuilder(Object source, String... args) {
this(new Object[] { source }, args);
this(new Object[]{source}, args);
}
public AggregateApplicationBuilder(Object[] sources, String[] args) {
@@ -101,7 +104,7 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
}
public AggregateApplicationBuilder parent(Object source, String... args) {
return parent(new Object[] { source }, args);
return parent(new Object[]{source}, args);
}
public AggregateApplicationBuilder parent(Object[] sources, String[] args) {
@@ -151,7 +154,8 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
}
try {
return bindableType.cast(parentContext.getBean(namespace + "." + bindableType.getName()));
} catch (BeansException e) {
}
catch (BeansException e) {
throw new IllegalStateException("Binding not found for '" + bindableType.getName() + "' into namespace " +
namespace);
}
@@ -190,6 +194,9 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
appConfigurers.put(appConfigurer, appConfigurer.namespace);
}
if (this.parentContext == null) {
if (Boolean.TRUE.equals(this.webEnvironment)) {
this.addParentSources(new Object[]{EmbeddedServletContainerAutoConfiguration.class});
}
this.parentContext = AggregateApplicationUtils.createParentContext(this.parentSources.toArray(new Object[0]),
this.parentArgs.toArray(new String[0]), selfContained(), this.webEnvironment, this.headless);
}
@@ -369,7 +376,7 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
void embed() {
final ConfigurableApplicationContext childContext = childContext(this.app,
AggregateApplicationBuilder.this.parentContext, this.namespace).args(this.args).config(this.names)
.profiles(this.profiles).run();
.profiles(this.profiles).run();
// Register bindable proxies as beans so they can be queried for later
Map<String, BindableProxyFactory> bindableProxies = BeanFactoryUtils
.beansOfTypeIncludingAncestors(childContext.getBeanFactory(), BindableProxyFactory.class);
@@ -455,7 +462,7 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
}
@EnableAutoConfiguration
@ImportAutoConfiguration({ChannelBindingAutoConfiguration.class, EndpointAutoConfiguration.class})
@EnableBinding
public static class ParentConfiguration {
@Bean

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-2017 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.
@@ -101,18 +101,21 @@ public class BindingServiceConfiguration {
}
@Bean
@ConditionalOnMissingBean(MessageConverterConfigurer.class)
public MessageConverterConfigurer messageConverterConfigurer(BindingServiceProperties bindingServiceProperties,
CompositeMessageConverterFactory compositeMessageConverterFactory) {
return new MessageConverterConfigurer(bindingServiceProperties, compositeMessageConverterFactory);
}
@Bean
@ConditionalOnMissingBean(SubscribableChannelBindingTargetFactory.class)
public SubscribableChannelBindingTargetFactory channelFactory(
CompositeMessageChannelConfigurer compositeMessageChannelConfigurer) {
return new SubscribableChannelBindingTargetFactory(compositeMessageChannelConfigurer);
}
@Bean
@ConditionalOnMissingBean(CompositeMessageChannelConfigurer.class)
public CompositeMessageChannelConfigurer compositeMessageChannelConfigurer(
MessageConverterConfigurer messageConverterConfigurer) {
List<MessageChannelConfigurer> configurerList = new ArrayList<>();
@@ -122,23 +125,27 @@ public class BindingServiceConfiguration {
@Bean
@DependsOn("bindingService")
@ConditionalOnMissingBean(OutputBindingLifecycle.class)
public OutputBindingLifecycle outputBindingLifecycle() {
return new OutputBindingLifecycle();
}
@Bean
@DependsOn("bindingService")
@ConditionalOnMissingBean(InputBindingLifecycle.class)
public InputBindingLifecycle inputBindingLifecycle() {
return new InputBindingLifecycle();
}
@Bean
@DependsOn("bindingService")
@ConditionalOnMissingBean(ContextStartAfterRefreshListener.class)
public ContextStartAfterRefreshListener contextStartAfterRefreshListener() {
return new ContextStartAfterRefreshListener();
}
@Bean
@ConditionalOnMissingBean(BinderAwareChannelResolver.class)
public BinderAwareChannelResolver binderAwareChannelResolver(BindingService bindingService,
AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory,
DynamicDestinationsBindable dynamicDestinationsBindable) {
@@ -147,17 +154,20 @@ public class BindingServiceConfiguration {
@Bean
@ConditionalOnProperty("spring.cloud.stream.bindings." + ERROR_CHANNEL_NAME + ".destination")
@ConditionalOnMissingBean(SingleBindingTargetBindable.class)
public SingleBindingTargetBindable<MessageChannel> errorChannelBindable(
@Qualifier(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) PublishSubscribeChannel errorChannel) {
return new SingleBindingTargetBindable<MessageChannel>(ERROR_CHANNEL_NAME, errorChannel);
}
@Bean
@ConditionalOnMissingBean(DynamicDestinationsBindable.class)
public DynamicDestinationsBindable dynamicDestinationsBindable() {
return new DynamicDestinationsBindable();
}
@Bean
@ConditionalOnMissingBean(CompositeMessageConverterFactory.class)
public CompositeMessageConverterFactory compositeMessageConverterFactory() {
List<MessageConverter> messageConverters = new ArrayList<>();
if (!CollectionUtils.isEmpty(this.customMessageConverters)) {
@@ -167,6 +177,7 @@ public class BindingServiceConfiguration {
}
@Bean
@ConditionalOnMissingBean(MessageHandlerMethodFactory.class)
public static MessageHandlerMethodFactory messageHandlerMethodFactory(
CompositeMessageConverterFactory compositeMessageConverterFactory) {
DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
@@ -176,6 +187,7 @@ public class BindingServiceConfiguration {
}
@Bean
@ConditionalOnMissingBean(StreamListenerAnnotationBeanPostProcessor.class)
public static StreamListenerAnnotationBeanPostProcessor bindToAnnotationBeanPostProcessor(
@Lazy BinderAwareChannelResolver binderAwareChannelResolver,
@Lazy MessageHandlerMethodFactory messageHandlerMethodFactory) {
@@ -196,6 +208,7 @@ public class BindingServiceConfiguration {
private BinderAwareChannelResolver binderAwareChannelResolver;
@Bean
@ConditionalOnMissingBean(BinderAwareRouterBeanPostProcessor.class)
public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(
final ConfigurableListableBeanFactory beanFactory) {
// IMPORTANT: Lazy delegate to avoid instantiating all of the above early

View File

@@ -28,6 +28,7 @@ import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder;
import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder.SourceConfigurer;
import org.springframework.cloud.stream.aggregate.SharedBindingTargetRegistry;
@@ -69,7 +70,7 @@ public class AggregationTest {
@Test
public void aggregation() {
aggregatedApplicationContext = new AggregateApplicationBuilder(
MockBinderRegistryConfiguration.class, "--server.port=0")
MockBinderRegistryConfiguration.class, "--server.port=0", "--debug=true")
.from(TestSource.class)
.to(TestProcessor.class)
.run();
@@ -118,6 +119,23 @@ public class AggregationTest {
"parentArgs");
assertThat(parentArgs).containsExactlyInAnyOrder(argsToVerify.toArray(new String[argsToVerify.size()]));
List<Object> sources = (List<Object>) aggregateApplicationBuilderAccessor.getPropertyValue("parentSources");
assertThat(sources).containsExactlyInAnyOrder(AggregateApplicationBuilder.ParentConfiguration.class,
MockBinderRegistryConfiguration.class, DummyConfig.class, EmbeddedServletContainerAutoConfiguration.class);
context.close();
}
@Test
public void testParentArgsAndSourcesWithWebDisabled() {
List<String> argsToVerify = new ArrayList<>();
AggregateApplicationBuilder aggregateApplicationBuilder =
new AggregateApplicationBuilder(MockBinderRegistryConfiguration.class, "--foo1=bar1");
final ConfigurableApplicationContext context =
aggregateApplicationBuilder.parent(DummyConfig.class, "--foo2=bar2").web(false)
.from(TestSource.class)
.namespace("foo").to(TestProcessor.class).namespace("bar")
.run("--server.port=0");
DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder);
List<Object> sources = (List<Object>) aggregateApplicationBuilderAccessor.getPropertyValue("parentSources");
assertThat(sources).containsExactlyInAnyOrder(AggregateApplicationBuilder.ParentConfiguration.class,
MockBinderRegistryConfiguration.class, DummyConfig.class);
context.close();