diff --git a/binders/rabbit-binder/pom.xml b/binders/rabbit-binder/pom.xml
index 90f8e3f3f..a0c6d9bfc 100644
--- a/binders/rabbit-binder/pom.xml
+++ b/binders/rabbit-binder/pom.xml
@@ -53,7 +53,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- true
+ false
diff --git a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/properties/RabbitProducerProperties.java b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/properties/RabbitProducerProperties.java
index 7b8246e09..46d789657 100644
--- a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/properties/RabbitProducerProperties.java
+++ b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/properties/RabbitProducerProperties.java
@@ -20,9 +20,11 @@ import java.util.Optional;
import jakarta.validation.constraints.Min;
+import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -149,6 +151,12 @@ public class RabbitProducerProperties extends RabbitCommonProperties {
*/
private String streamMessageConverterBeanName;
+ /**
+ * Configure an alternate exchange for when no queues are bound.
+ * @since 4.0
+ */
+ private AlternateExchange alternateExchange;
+
/**
* @param requestHeaderPatterns the patterns.
* @deprecated - use {@link #setHeaderPatterns(String[])}.
@@ -302,4 +310,100 @@ public class RabbitProducerProperties extends RabbitCommonProperties {
this.streamMessageConverterBeanName = streamMessageConverterBeanName;
}
+ @Nullable
+ public AlternateExchange getAlternateExchange() {
+ return this.alternateExchange;
+ }
+
+ public void setAlternateExchange(AlternateExchange alternate) {
+ this.alternateExchange = alternate;
+ }
+
+ public static class AlternateExchange {
+
+ /**
+ * The alternate exchange name.
+ */
+ private String name;
+
+ /**
+ * Whether the exchange exists or should be provisioned.
+ */
+ private boolean exists = false;
+
+ /**
+ * The alternate exchange type.
+ */
+ private String type = ExchangeTypes.TOPIC;
+
+ /**
+ * Bind a durable queue to the alternate exchange.
+ */
+ private Binding binding;
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isExists() {
+ return this.exists;
+ }
+
+ public void setExists(boolean exists) {
+ this.exists = exists;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Binding getBinding() {
+ return this.binding;
+ }
+
+ public void setBinding(Binding binding) {
+ this.binding = binding;
+ }
+
+ public static class Binding {
+
+ /**
+ * The routing key.
+ */
+ private String routingKey = "#";
+
+ /**
+ * The queue name.
+ */
+ private String queue;
+
+ public String getRoutingKey() {
+ return this.routingKey;
+ }
+
+ public void setRoutingKey(String routingKey) {
+ this.routingKey = routingKey;
+ }
+
+ public String getQueue() {
+ return this.queue;
+ }
+
+ public void setQueue(String queue) {
+ this.queue = queue;
+ }
+
+
+ }
+
+ }
+
}
diff --git a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisioner.java b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisioner.java
index 87750dd82..f6f85b44d 100644
--- a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisioner.java
+++ b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/main/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisioner.java
@@ -56,6 +56,7 @@ import org.springframework.cloud.stream.binder.rabbit.properties.RabbitCommonPro
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties.ContainerType;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties;
+import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties.AlternateExchange;
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
import org.springframework.cloud.stream.provisioning.ProducerDestination;
import org.springframework.cloud.stream.provisioning.ProvisioningException;
@@ -119,9 +120,9 @@ public class RabbitExchangeQueueProvisioner
ExtendedProducerProperties producerProperties) {
final String exchangeName = applyPrefix(
producerProperties.getExtension().getPrefix(), name);
- Exchange exchange = buildExchange(producerProperties.getExtension(),
- exchangeName);
String beanNameQualifier = "prod" + this.producerExchangeBeanNameQualifier.incrementAndGet();
+ Exchange exchange = buildExchange(producerProperties.getExtension(),
+ exchangeName, producerProperties.getExtension().getAlternateExchange(), beanNameQualifier);
if (producerProperties.getExtension().isDeclareExchange()) {
declareExchange(exchangeName, beanNameQualifier, exchange);
}
@@ -245,7 +246,7 @@ public class RabbitExchangeQueueProvisioner
}
String prefix = properties.getExtension().getPrefix();
final String exchangeName = applyPrefix(prefix, name);
- Exchange exchange = buildExchange(properties.getExtension(), exchangeName);
+ Exchange exchange = buildExchange(properties.getExtension(), exchangeName, null, null);
if (properties.getExtension().isDeclareExchange()) {
declareExchange(exchangeName, anonymous ? anonymousGroup : group, exchange);
}
@@ -364,25 +365,31 @@ public class RabbitExchangeQueueProvisioner
routingKey = "#";
}
Map arguments = new HashMap<>(extendedProperties.getQueueBindingArguments());
+ return createBinding(exchange, queue, routingKey, arguments, queue.getName());
+ }
+
+ private Binding createBinding(Exchange exchange, Queue queue, String routingKey,
+ @Nullable Map arguments, String beanName) {
+
if (exchange instanceof TopicExchange) {
Binding binding = BindingBuilder.bind(queue).to((TopicExchange) exchange)
.with(routingKey);
- declareBinding(queue.getName(), binding);
+ declareBinding(beanName, binding);
return binding;
}
else if (exchange instanceof DirectExchange) {
Binding binding = BindingBuilder.bind(queue).to((DirectExchange) exchange)
.with(routingKey);
- declareBinding(queue.getName(), binding);
+ declareBinding(beanName, binding);
return binding;
}
else if (exchange instanceof FanoutExchange) {
Binding binding = BindingBuilder.bind(queue).to((FanoutExchange) exchange);
- declareBinding(queue.getName(), binding);
+ declareBinding(beanName, binding);
return binding;
}
else if (exchange instanceof HeadersExchange) {
- Binding binding = new Binding(queue.getName(), DestinationType.QUEUE, exchange.getName(), "", arguments);
+ Binding binding = new Binding(beanName, DestinationType.QUEUE, exchange.getName(), "", arguments);
declareBinding(queue.getName(), binding);
return binding;
}
@@ -587,8 +594,9 @@ public class RabbitExchangeQueueProvisioner
return prefix + name;
}
- private Exchange buildExchange(RabbitCommonProperties properties,
- String exchangeName) {
+ private Exchange buildExchange(RabbitCommonProperties properties, String exchangeName,
+ @Nullable AlternateExchange alternate, @Nullable String beanNameQualifier) {
+
try {
ExchangeBuilder builder = new ExchangeBuilder(exchangeName,
properties.getExchangeType());
@@ -599,6 +607,10 @@ public class RabbitExchangeQueueProvisioner
if (properties.isDelayedExchange()) {
builder.delayed();
}
+ if (alternate != null && !alternate.isExists()) {
+ builder.alternate(alternate.getName());
+ configureAlternate(alternate, beanNameQualifier);
+ }
return builder.build();
}
catch (Exception e) {
@@ -606,8 +618,26 @@ public class RabbitExchangeQueueProvisioner
}
}
+ private void configureAlternate(AlternateExchange alternate, String beanNameQualifier) {
+ Exchange exchange = customizeAndDeclare(new ExchangeBuilder(alternate.getName(), alternate.getType())
+ .durable(true)
+ .build());
+ addToAutoDeclareContext(alternate.getName() + "." + beanNameQualifier + ".exchange", exchange);
+ AlternateExchange.Binding binding = alternate.getBinding();
+ if (binding != null) {
+ Queue queue = new Queue(binding.getQueue());
+ String beanName = alternate.getName() + "." + binding.getQueue() + "." + beanNameQualifier;
+ declareQueue(beanName, queue);
+ Binding toBind = createBinding(exchange, queue, binding.getRoutingKey(), null, beanName);
+ }
+ }
+
private void declareExchange(final String rootName, String group, final Exchange exchangeArg) {
- Exchange exchange = exchangeArg;
+ Exchange exchange = customizeAndDeclare(exchangeArg);
+ addToAutoDeclareContext(rootName + "." + group + ".exchange", exchange);
+ }
+
+ private Exchange customizeAndDeclare(Exchange exchange) {
for (DeclarableCustomizer customizer : this.customizers) {
exchange = (Exchange) customizer.apply(exchange);
}
@@ -631,7 +661,7 @@ public class RabbitExchangeQueueProvisioner
e);
}
}
- addToAutoDeclareContext(rootName + "." + group + ".exchange", exchange);
+ return exchange;
}
private void addToAutoDeclareContext(String name, Declarable bean) {
@@ -719,6 +749,15 @@ public class RabbitExchangeQueueProvisioner
}
}
}
+ AlternateExchange alternate = properties.getExtension().getAlternateExchange();
+ if (alternate != null) {
+ removeSingleton(alternate.getName() + "." + qual + ".exchange");
+ RabbitProducerProperties.AlternateExchange.Binding binding = alternate.getBinding();
+ if (binding != null) {
+ removeSingleton(alternate.getName() + "." + binding.getQueue() + "." + qual);
+ removeSingleton(alternate.getName() + "." + binding.getQueue() + "." + qual + ".binding");
+ }
+ }
}
}
}
diff --git a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/test/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisionerTests.java b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/test/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisionerTests.java
index 11ad6ca09..3c00dcadc 100644
--- a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/test/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisionerTests.java
+++ b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit-core/src/test/java/org/springframework/cloud/stream/binder/rabbit/provisioning/RabbitExchangeQueueProvisionerTests.java
@@ -17,6 +17,7 @@
package org.springframework.cloud.stream.binder.rabbit.provisioning;
import java.io.IOException;
+import java.util.Map;
import java.util.Set;
import com.rabbitmq.client.Channel;
@@ -24,6 +25,10 @@ import com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Declarables;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Exchange;
+import org.springframework.amqp.core.ExchangeTypes;
+import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.utils.test.TestUtils;
@@ -31,6 +36,8 @@ import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
import org.springframework.cloud.stream.binder.ExtendedProducerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties;
+import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties.AlternateExchange;
+import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties.AlternateExchange.Binding;
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
import org.springframework.cloud.stream.provisioning.ProducerDestination;
import org.springframework.context.ApplicationContext;
@@ -126,6 +133,89 @@ public class RabbitExchangeQueueProvisionerTests {
assertThat(ctx.getBeansOfType(Declarables.class)).isEmpty();
}
+ @Test
+ void producerDeclarationsWithAlternateNoQueue() throws IOException {
+ ConnectionFactory cf = mock(ConnectionFactory.class);
+ Connection conn = mock(Connection.class);
+ given(cf.createConnection()).willReturn(conn);
+ Channel channel = mock(Channel.class);
+ given(conn.createChannel(anyBoolean())).willReturn(channel);
+ RabbitExchangeQueueProvisioner provisioner = new RabbitExchangeQueueProvisioner(cf);
+
+ RabbitProducerProperties props = new RabbitProducerProperties();
+ AlternateExchange alternate = new AlternateExchange();
+ alternate.setName("altEx");
+ props.setAlternateExchange(alternate);
+ ExtendedProducerProperties properties =
+ new ExtendedProducerProperties(props);
+ ProducerDestination dest = provisioner.provisionProducerDestination("withAlt", properties);
+ ApplicationContext ctx =
+ TestUtils.getPropertyValue(provisioner, "autoDeclareContext", ApplicationContext.class);
+ Map declarables = ctx.getBeansOfType(Declarables.class);
+ assertThat(declarables).hasSize(2);
+ String qual = TestUtils.getPropertyValue(dest, "beanNameQualifier", String.class);
+ Declarables mainEx = declarables.get("withAlt." + qual + ".exchange");
+ assertThat(mainEx).isNotNull();
+ Exchange exch = (Exchange) mainEx.getDeclarables().iterator().next();
+ assertThat(exch.getArguments().get("alternate-exchange")).isEqualTo("altEx");
+ Declarables altEx = declarables.get("altEx." + qual + ".exchange");
+ assertThat(altEx).isNotNull();
+ exch = (Exchange) altEx.getDeclarables().iterator().next();
+ assertThat(exch).isInstanceOf(TopicExchange.class);
+ provisioner.cleanAutoDeclareContext(dest, properties);
+ assertThat(ctx.getBeansOfType(Declarables.class)).isEmpty();
+ }
+
+ @Test
+ void producerDeclarationsWithAlternateWithQueue() throws IOException {
+ ConnectionFactory cf = mock(ConnectionFactory.class);
+ Connection conn = mock(Connection.class);
+ given(cf.createConnection()).willReturn(conn);
+ Channel channel = mock(Channel.class);
+ willReturn(new DeclareOk("x", 0, 0))
+ .given(channel).queueDeclare(any(), eq(Boolean.TRUE), eq(Boolean.FALSE), eq(Boolean.FALSE), any());
+ given(conn.createChannel(anyBoolean())).willReturn(channel);
+ RabbitExchangeQueueProvisioner provisioner = new RabbitExchangeQueueProvisioner(cf);
+
+ RabbitProducerProperties props = new RabbitProducerProperties();
+ AlternateExchange alternate = new AlternateExchange();
+ alternate.setName("altEx");
+ alternate.setType(ExchangeTypes.DIRECT);
+ Binding binding = new Binding();
+ binding.setQueue("altQ");
+ binding.setRoutingKey("altRK");
+ alternate.setBinding(binding);
+ props.setAlternateExchange(alternate);
+ ExtendedProducerProperties properties =
+ new ExtendedProducerProperties(props);
+ ProducerDestination dest = provisioner.provisionProducerDestination("withAlt", properties);
+ ApplicationContext ctx =
+ TestUtils.getPropertyValue(provisioner, "autoDeclareContext", ApplicationContext.class);
+ Map declarables = ctx.getBeansOfType(Declarables.class);
+ assertThat(declarables).hasSize(4);
+ String qual = TestUtils.getPropertyValue(dest, "beanNameQualifier", String.class);
+ Declarables mainEx = declarables.get("withAlt." + qual + ".exchange");
+ assertThat(mainEx).isNotNull();
+ Exchange exch = (Exchange) mainEx.getDeclarables().iterator().next();
+ assertThat(exch).isInstanceOf(TopicExchange.class);
+ assertThat(exch.getArguments().get("alternate-exchange")).isEqualTo("altEx");
+ Declarables altEx = declarables.get("altEx." + qual + ".exchange");
+ assertThat(altEx).isNotNull();
+ exch = (Exchange) altEx.getDeclarables().iterator().next();
+ assertThat(exch).isInstanceOf(DirectExchange.class);
+ Declarables queueDec = declarables.get("altEx.altQ." + qual);
+ assertThat(queueDec).isNotNull();
+ Declarables bindingDec = declarables.get("altEx.altQ." + qual + ".binding");
+ assertThat(bindingDec).isNotNull();
+ org.springframework.amqp.core.Binding bdg = (org.springframework.amqp.core.Binding) bindingDec.getDeclarables()
+ .iterator().next();
+ assertThat(bdg.getExchange()).isEqualTo("altEx");
+ assertThat(bdg.getDestination()).isEqualTo("altQ");
+ assertThat(bdg.getRoutingKey()).isEqualTo("altRK");
+ provisioner.cleanAutoDeclareContext(dest, properties);
+ assertThat(ctx.getBeansOfType(Declarables.class)).isEmpty();
+ }
+
@Test
void producerDeclarationsWithGroupsAndDlq() throws IOException {
ConnectionFactory cf = mock(ConnectionFactory.class);
diff --git a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java
index 1148e743e..8284a8609 100644
--- a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java
+++ b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java
@@ -104,6 +104,7 @@ import org.springframework.cloud.stream.binder.rabbit.properties.RabbitCommonPro
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties.ContainerType;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties;
+import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties.AlternateExchange;
import org.springframework.cloud.stream.binder.rabbit.provisioning.RabbitExchangeQueueProvisioner;
import org.springframework.cloud.stream.binder.test.junit.rabbit.RabbitTestSupport;
import org.springframework.cloud.stream.config.BindingProperties;
@@ -388,6 +389,35 @@ public class RabbitBinderTests extends
verifyAutoDeclareContextClear(binder);
}
+ @Test
+ void producerWithAlternateExchange(TestInfo testInfo) throws Exception {
+ RabbitTestBinder binder = getBinder();
+ DirectChannel moduleOutputChannel = createBindableChannel("output",
+ new BindingProperties());
+ ExtendedProducerProperties producerProps = createProducerProperties(testInfo);
+ AlternateExchange alternate = new AlternateExchange();
+ alternate.setName("altEx");
+ RabbitProducerProperties.AlternateExchange.Binding binding =
+ new RabbitProducerProperties.AlternateExchange.Binding();
+ binding.setQueue("altQ");
+ alternate.setBinding(binding);
+ producerProps.getExtension().setAlternateExchange(alternate);
+ Binding producerBinding = binder.bindProducer("alt.0",
+ moduleOutputChannel, producerProps);
+ final Message> message = MessageBuilder.withPayload("altMessage".getBytes())
+ .build();
+ moduleOutputChannel.send(message);
+ producerBinding.unbind();
+
+ verifyAutoDeclareContextClear(binder);
+ RabbitTemplate template = new RabbitTemplate(this.rabbitTestSupport.getResource());
+ Object received = template.receiveAndConvert("altQ", 10_000);
+ assertThat(received).isEqualTo("altMessage".getBytes());
+ RabbitAdmin admin = new RabbitAdmin(template.getConnectionFactory());
+ admin.deleteQueue("altQ");
+ admin.deleteExchange("altEx");
+ }
+
@Test
public void testConsumerProperties() throws Exception {
RabbitTestBinder binder = getBinder();
diff --git a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java
index 2e1b36b1b..e6370f9c8 100644
--- a/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java
+++ b/binders/rabbit-binder/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java
@@ -62,6 +62,7 @@ import org.springframework.cloud.stream.binder.rabbit.RabbitMessageChannelBinder
import org.springframework.cloud.stream.binder.rabbit.RabbitTestContainer;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties;
+import org.springframework.cloud.stream.binder.rabbit.properties.RabbitProducerProperties.AlternateExchange;
import org.springframework.cloud.stream.binder.test.junit.rabbit.RabbitTestSupport;
import org.springframework.cloud.stream.binding.BindingService;
import org.springframework.cloud.stream.config.ConsumerEndpointCustomizer;
@@ -100,7 +101,7 @@ public class RabbitBinderModuleTests {
private static final ConnectionFactory MOCK_CONNECTION_FACTORY = mock(ConnectionFactory.class, Mockito.RETURNS_MOCKS);
@RegisterExtension
- private RabbitTestSupport rabbitTestSupport = new RabbitTestSupport(true, RABBITMQ.getAmqpPort(), RABBITMQ.getHttpPort());
+ private final RabbitTestSupport rabbitTestSupport = new RabbitTestSupport(true, RABBITMQ.getAmqpPort(), RABBITMQ.getHttpPort());
private ConfigurableApplicationContext context;
@@ -365,7 +366,12 @@ public class RabbitBinderModuleTests {
"--spring.cloud.stream.rabbit.default.consumer.exchange-type=direct",
"--spring.cloud.stream.rabbit.bindings.process-out-0.producer.batch-size=512",
"--spring.cloud.stream.rabbit.default.consumer.max-concurrency=4",
- "--spring.cloud.stream.rabbit.bindings.process-in-0.consumer.exchange-type=fanout");
+ "--spring.cloud.stream.rabbit.bindings.process-in-0.consumer.exchange-type=fanout",
+ "--spring.cloud.stream.rabbit.bindings.process-out-0.producer.alternateExchange.name=altEx",
+ "--spring.cloud.stream.rabbit.bindings.process-out-0.producer.alternate-exchange.exists=true",
+ "--spring.cloud.stream.rabbit.bindings.process-out-0.producer.alternate-exchange.type=direct",
+ "--spring.cloud.stream.rabbit.bindings.process-out-0.producer.alternate-exchange.binding.queue=altQ",
+ "--spring.cloud.stream.rabbit.bindings.process-out-0.producer.alternate-exchange.binding.routing-key=altRK");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder, ?, ?> rabbitBinder = binderFactory.getBinder(null,
MessageChannel.class);
@@ -377,6 +383,12 @@ public class RabbitBinderModuleTests {
rabbitProducerProperties.getRoutingKeyExpression().getExpressionString())
.isEqualTo("fooRoutingKey");
assertThat(rabbitProducerProperties.getBatchSize()).isEqualTo(512);
+ AlternateExchange alternate = rabbitProducerProperties.getAlternateExchange();
+ assertThat(alternate.getName()).isEqualTo("altEx");
+ assertThat(alternate.isExists()).isTrue();
+ assertThat(alternate.getType()).isEqualTo("direct");
+ assertThat(alternate.getBinding().getQueue()).isEqualTo("altQ");
+ assertThat(alternate.getBinding().getRoutingKey()).isEqualTo("altRK");
RabbitConsumerProperties rabbitConsumerProperties = (RabbitConsumerProperties) ((ExtendedPropertiesBinder) rabbitBinder)
.getExtendedConsumerProperties("process-in-0");
diff --git a/docs/src/main/asciidoc/rabbit/rabbit_overview.adoc b/docs/src/main/asciidoc/rabbit/rabbit_overview.adoc
index 0a2377115..a5a952ba0 100644
--- a/docs/src/main/asciidoc/rabbit/rabbit_overview.adoc
+++ b/docs/src/main/asciidoc/rabbit/rabbit_overview.adoc
@@ -682,7 +682,27 @@ in the format of `spring.cloud.stream.rabbit.default.=`.
Also, keep in mind that binding specific property will override its equivalent in the default.
-
+altermateExchange.binding.queue::
+If the exchange does not already exist, and a `name` is provided, bind this queue to the alternate exhange.
+A simple durable queue with no arguments is provisioned; if more sophisticated configuration is required, you must configure and bind the queue yourself.
++
+Default: `null`
+alternateExchange.binding.routingKey
+If the exchange does not already exist, and a `name` and `queue` is provided, bind the queue to the alternate exhange using this routing key.
++
+Default: `#` (for the default `topic` alternate exchange)
+alternateExchange.exists::
+Whether the alternate exchange exists, or needs to be provisioned.
++
+Default: `false`
+alternateExchange.type::
+If the alternate exchange does not already exist, the type of exchange to provision.
++
+Default: `topic`
+alternateExchange.name::
+Configure an alternate exchange on the destination exchange.
++
+Default: `null`
autoBindDlq::
Whether to automatically declare the DLQ and bind it to the binder DLX.
+