From f8e4df4711ea91b4f8587ae7c11c322adcda4d45 Mon Sep 17 00:00:00 2001 From: Marius Bogoevici Date: Mon, 6 Jun 2016 14:54:43 -0400 Subject: [PATCH] Rabbit Configuration Corrections Fixes #564 - Correct an issue where if the `cloud` profile is enabled but no Connectors are available, no `ConnectionFactory` will be created; - Introduce a property named `spring.cloud.stream.overrideCloudConnectors` that allows to suppress the use of Cloud connectors in the application and fall back to Spring Boot configuration. This is provided so that specific binders can be created with arbitrary connectivity parameters if needed. - Add documentation. --- .../RabbitServiceAutoConfiguration.java | 85 ++++++++++++++++--- .../spring-cloud-stream-overview.adoc | 11 +++ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java index c69798c57..34a8ece22 100644 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java +++ b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java @@ -20,11 +20,13 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.RabbitHealthIndicator; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration; +import org.springframework.boot.autoconfigure.amqp.RabbitProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.Cloud; import org.springframework.cloud.CloudFactory; import org.springframework.cloud.stream.binder.Binder; @@ -41,11 +43,11 @@ import org.springframework.context.annotation.Profile; * @author Glenn Renfro * @author David Turanski * @author Eric Bottard + * @author Marius Bogoevici */ @Configuration @ConditionalOnMissingBean(Binder.class) @Import(RabbitMessageChannelBinderConfiguration.class) -@AutoConfigureBefore({CloudAutoConfiguration.class, RabbitAutoConfiguration.class}) public class RabbitServiceAutoConfiguration { @Bean @@ -53,26 +55,83 @@ public class RabbitServiceAutoConfiguration { return new RabbitHealthIndicator(rabbitTemplate); } + /** + * Configuration to be used when the cloud profile is set. + */ @Configuration @Profile("cloud") - @ConditionalOnClass(Cloud.class) - protected static class CloudConfig { + protected static class CloudProfile { - @Bean - public Cloud cloud() { - return new CloudFactory().getCloud(); + /** + * Configuration to be used when the cloud profile is set, and Cloud Connectors + * are found on the classpath. + */ + @Configuration + @ConditionalOnClass(Cloud.class) + protected static class CloudConnectors { + + @Bean + public Cloud cloud() { + return new CloudFactory().getCloud(); + } + + /** + * Active only if {@code spring.cloud.stream.overrideCloudConnectors} is not + * set to {@code true}. + */ + @Configuration + @ConditionalOnProperty(value = "spring.cloud.stream.overrideCloudConnectors", havingValue = "false", matchIfMissing = true) + // Required to parse Rabbit properties which are passed to the binder for + // clustering. We need to enable it here explicitly as the default Rabbit + // configuration is not triggered. + @EnableConfigurationProperties(RabbitProperties.class) + protected static class UseCloudConnectors { + + /** + * Creates a {@link ConnectionFactory} using the singleton service + * connector. + * + * @param cloud {@link Cloud} instance to be used for accessing services. + * @return the {@link ConnectionFactory} used by the binder. + */ + @Bean + ConnectionFactory rabbitConnectionFactory(Cloud cloud) { + return cloud.getSingletonServiceConnector(ConnectionFactory.class, null); + } + + @Bean + @ConditionalOnMissingBean(RabbitTemplate.class) + RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { + return new RabbitTemplate(connectionFactory); + } + } + + /** + * Configuration to be used if + * {@code spring.cloud.stream.overrideCloudConnectors} is set to {@code true}. + * Defers to Spring Boot Autoconfiguration. + */ + @Configuration + @ConditionalOnProperty("spring.cloud.stream.overrideCloudConnectors") + @Import(RabbitAutoConfiguration.class) + protected static class OverrideCloudConnectors { + } } - @Bean - @ConditionalOnMissingBean(ConnectionFactory.class) - ConnectionFactory rabbitConnectionFactory(Cloud cloud) { - return cloud.getSingletonServiceConnector(ConnectionFactory.class, null); + @Configuration + @ConditionalOnMissingClass("org.springframework.cloud.Cloud") + @Import(RabbitAutoConfiguration.class) + protected static class NoCloudConnectors { } } + /** + * Configuration to be used when the cloud profile is not set. Defer to Spring Boot + * autoconfiguration. + */ @Configuration @Profile("!cloud") @Import(RabbitAutoConfiguration.class) - protected static class NoCloudConfig { + protected static class NoCloudProfile { } } diff --git a/spring-cloud-stream-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc b/spring-cloud-stream-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc index 9898d1cfd..9514bea15 100644 --- a/spring-cloud-stream-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc +++ b/spring-cloud-stream-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc @@ -720,6 +720,7 @@ spring.cloud.stream.bindings.input.binder=kafka spring.cloud.stream.bindings.output.binder=rabbit ---- +[[multiple-systems]] === Connecting to Multiple Systems By default, binders share the application's Spring Boot auto-configuration, so that one instance of each binder found on the classpath will be created. @@ -842,6 +843,16 @@ Default: empty (allowing any destination to be bound). spring.cloud.stream.defaultBinder:: The default binder to use, if multiple binders are configured. See <>. ++ +Default: empty. + +spring.cloud.stream.overrideCloudConnectors:: + This property is only applicable when the `cloud` profile is active and Spring Cloud Connectors are provided with the application. +If the property is false (the default), the binder will detect a suitable bound service (e.g. a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and will use it for creating connections (usually via Spring Cloud Connectors). +When set to true, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (e.g. relying on the `spring.rabbitmq.*` properties provided in the environment for the RabbitMQ binder). +The typical usage of this property is to be nested in a customized environment <>. ++ +Default: false. [[binding-properties]] === Binding Properties