Binder simplification
* removed pub/sub methods from binder * consumer group is now a parameter of the remaining bindConsumer method * remove DynamicProducer from Binder * Move logic to create the dynamic channel to the channel resolver. * Return bindings from bind methods and use them for unbinding * Suffix for dlq Move All Rabbit Binder CleanUp to Test Bindera More RabbitMQ Binder Test Cleanup Clean up declarations for remaining tests. removed BinderUtils use Redis ZSET for consumer groups copyright dates AutoBindDLQ: Single DLQ Per Group When Partitioned Configure a single DLQ for each group for all partitions. Add DLX Exchange binding for each original queue routing key, including the partition. Fix DLQ Binding (Producer Side) Option was not allowed and the routing key was wrong. Add test to verify producers can be bound before consumers. `autoBindDLQ` must be set (or reset) on both sides for success.
This commit is contained in:
committed by
Marius Bogoevici
parent
60e44b530d
commit
c3758b9dc0
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -117,11 +117,13 @@ import scala.collection.Seq;
|
||||
* that better be greater than number of containers</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
|
||||
@@ -184,12 +186,6 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
KafkaMessageChannelBinder.COMPRESSION_CODEC,
|
||||
}));
|
||||
|
||||
/**
|
||||
* The consumer group to use when achieving point to point semantics (that consumer group name is static and hence
|
||||
* shared by all containers).
|
||||
*/
|
||||
private static final String POINT_TO_POINT_SEMANTICS_CONSUMER_GROUP = "springXD";
|
||||
|
||||
private static final Set<Object> KAFKA_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.add(BinderPropertyKeys.MIN_PARTITION_COUNT)
|
||||
.build();
|
||||
@@ -210,25 +206,12 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
.add(BinderPropertyKeys.MIN_PARTITION_COUNT)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Basic + concurrency.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_NAMED_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(CONSUMER_STANDARD_PROPERTIES)
|
||||
.build();
|
||||
|
||||
private static final Set<Object> SUPPORTED_NAMED_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(PRODUCER_STANDARD_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_BASIC_PROPERTIES)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Partitioning + kafka producer properties.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(PRODUCER_PARTITIONING_PROPERTIES)
|
||||
.addAll(PRODUCER_STANDARD_PROPERTIES)
|
||||
.add(BinderPropertyKeys.DIRECT_BINDING_ALLOWED)
|
||||
.addAll(KAFKA_PRODUCER_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_BASIC_PROPERTIES)
|
||||
.addAll(PRODUCER_COMPRESSION_PROPERTIES)
|
||||
@@ -246,6 +229,7 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
private final String zkAddress;
|
||||
|
||||
// -------- Default values for properties -------
|
||||
|
||||
private int defaultReplicationFactor = 1;
|
||||
|
||||
private String defaultCompressionCodec = DEFAULT_COMPRESSION_CODEC;
|
||||
@@ -306,7 +290,6 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
else {
|
||||
this.headersToMap = BinderHeaders.STANDARD_HEADERS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setOffsetStoreTopic(String offsetStoreTopic) {
|
||||
@@ -333,7 +316,6 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
this.offsetStoreMaxFetchSize = offsetStoreMaxFetchSize;
|
||||
}
|
||||
|
||||
|
||||
public void setOffsetUpdateTimeWindow(int offsetUpdateTimeWindow) {
|
||||
this.offsetUpdateTimeWindow = offsetUpdateTimeWindow;
|
||||
}
|
||||
@@ -460,99 +442,66 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindConsumer(String name, final MessageChannel moduleInputChannel, Properties properties) {
|
||||
// Point-to-point consumers reset at the earliest time, which allows them to catch up with all messages
|
||||
createKafkaConsumer(name, moduleInputChannel, properties, POINT_TO_POINT_SEMANTICS_CONSUMER_GROUP,
|
||||
OffsetRequest.EarliestTime());
|
||||
bindExistingProducerDirectlyIfPossible(name, moduleInputChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(String name, MessageChannel inputChannel, String group, Properties properties) {
|
||||
protected Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel inputChannel, Properties properties) {
|
||||
// If the caller provides a group, use it; otherwise
|
||||
// usage of a different consumer group each time achieves pub-sub
|
||||
// but multiple instances of this binding will each get all messages
|
||||
// PubSub consumers reset at the latest time, which allows them to receive only messages sent after
|
||||
// they've been bound
|
||||
String consumerGroup = group == null ? UUID.randomUUID().toString() : group;
|
||||
createKafkaConsumer(name, inputChannel, properties, consumerGroup, OffsetRequest.LatestTime());
|
||||
return createKafkaConsumer(name, inputChannel, properties, consumerGroup, OffsetRequest.LatestTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(final String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
|
||||
public Binding<MessageChannel> bindProducer(final String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
KafkaPropertiesAccessor producerPropertiesAccessor = new KafkaPropertiesAccessor(properties);
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_NAMED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
if (!bindNewProducerDirectlyIfPossible(name, (SubscribableChannel) moduleOutputChannel,
|
||||
producerPropertiesAccessor)) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Using kafka topic for outbound: " + name);
|
||||
}
|
||||
|
||||
final String topicName = escapeTopicName(name);
|
||||
|
||||
int numPartitions = producerPropertiesAccessor.getNumberOfKafkaPartitionsForProducer();
|
||||
|
||||
Collection<Partition> partitions = ensureTopicCreated(topicName, numPartitions, defaultReplicationFactor);
|
||||
|
||||
ProducerMetadata<byte[], byte[]> producerMetadata = new ProducerMetadata<>(
|
||||
topicName, byte[].class, byte[].class, BYTE_ARRAY_SERIALIZER, BYTE_ARRAY_SERIALIZER);
|
||||
producerMetadata.setCompressionType(ProducerMetadata.CompressionType.valueOf(
|
||||
producerPropertiesAccessor.getCompressionCodec(this.defaultCompressionCodec)));
|
||||
producerMetadata.setBatchBytes(producerPropertiesAccessor.getBatchSize(this.defaultBatchSize));
|
||||
Properties additionalProps = new Properties();
|
||||
additionalProps.put(ProducerConfig.ACKS_CONFIG,
|
||||
String.valueOf(producerPropertiesAccessor.getRequiredAcks(this
|
||||
.defaultRequiredAcks)));
|
||||
additionalProps.put(ProducerConfig.LINGER_MS_CONFIG,
|
||||
String.valueOf(producerPropertiesAccessor.getBatchTimeout(this
|
||||
.defaultBatchTimeout)));
|
||||
ProducerFactoryBean<byte[], byte[]> producerFB =
|
||||
new ProducerFactoryBean<>(producerMetadata, brokers, additionalProps);
|
||||
|
||||
try {
|
||||
final ProducerConfiguration<byte[], byte[]> producerConfiguration = new ProducerConfiguration<>(
|
||||
producerMetadata, producerFB.getObject());
|
||||
|
||||
MessageHandler handler = new SendingHandler(topicName, producerPropertiesAccessor,
|
||||
partitions.size(), producerConfiguration);
|
||||
EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel,
|
||||
handler);
|
||||
consumer.setBeanFactory(this.getBeanFactory());
|
||||
consumer.setBeanName("outbound." + name);
|
||||
consumer.afterPropertiesSet();
|
||||
Binding producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer,
|
||||
producerPropertiesAccessor);
|
||||
addBinding(producerBinding);
|
||||
producerBinding.start();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Using kafka topic for outbound: " + name);
|
||||
}
|
||||
|
||||
}
|
||||
final String topicName = escapeTopicName(name);
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(String name, MessageChannel outputChannel, Properties properties) {
|
||||
bindProducer(name, outputChannel, properties);
|
||||
}
|
||||
int numPartitions = producerPropertiesAccessor.getNumberOfKafkaPartitionsForProducer();
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
|
||||
throw new UnsupportedOperationException("requestor binding is not supported by this binder");
|
||||
}
|
||||
Collection<Partition> partitions = ensureTopicCreated(topicName, numPartitions, defaultReplicationFactor);
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
|
||||
throw new UnsupportedOperationException("replier binding is not supported by this binder");
|
||||
ProducerMetadata<byte[], byte[]> producerMetadata = new ProducerMetadata<>(
|
||||
topicName, byte[].class, byte[].class, BYTE_ARRAY_SERIALIZER, BYTE_ARRAY_SERIALIZER);
|
||||
producerMetadata.setCompressionType(ProducerMetadata.CompressionType.valueOf(
|
||||
producerPropertiesAccessor.getCompressionCodec(this.defaultCompressionCodec)));
|
||||
producerMetadata.setBatchBytes(producerPropertiesAccessor.getBatchSize(this.defaultBatchSize));
|
||||
Properties additionalProps = new Properties();
|
||||
additionalProps.put(ProducerConfig.ACKS_CONFIG,
|
||||
String.valueOf(producerPropertiesAccessor.getRequiredAcks(this
|
||||
.defaultRequiredAcks)));
|
||||
additionalProps.put(ProducerConfig.LINGER_MS_CONFIG,
|
||||
String.valueOf(producerPropertiesAccessor.getBatchTimeout(this
|
||||
.defaultBatchTimeout)));
|
||||
ProducerFactoryBean<byte[], byte[]> producerFB =
|
||||
new ProducerFactoryBean<>(producerMetadata, brokers, additionalProps);
|
||||
|
||||
try {
|
||||
final ProducerConfiguration<byte[], byte[]> producerConfiguration = new ProducerConfiguration<>(
|
||||
producerMetadata, producerFB.getObject());
|
||||
|
||||
MessageHandler handler = new SendingHandler(topicName, producerPropertiesAccessor,
|
||||
partitions.size(), producerConfiguration);
|
||||
EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel,
|
||||
handler);
|
||||
consumer.setBeanFactory(this.getBeanFactory());
|
||||
consumer.setBeanName("outbound." + name);
|
||||
consumer.afterPropertiesSet();
|
||||
Binding<MessageChannel> producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer,
|
||||
producerPropertiesAccessor);
|
||||
addBinding(producerBinding);
|
||||
producerBinding.start();
|
||||
return producerBinding;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,15 +559,10 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void createKafkaConsumer(String name, final MessageChannel moduleInputChannel, Properties properties,
|
||||
private Binding<MessageChannel> createKafkaConsumer(String name, final MessageChannel moduleInputChannel, Properties properties,
|
||||
String group, long referencePoint) {
|
||||
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_NAMED_CONSUMER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
}
|
||||
validateConsumerProperties(groupedName(name, group), properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
KafkaPropertiesAccessor accessor = new KafkaPropertiesAccessor(properties);
|
||||
|
||||
int maxConcurrency = accessor.getConcurrency(defaultConcurrency);
|
||||
@@ -702,12 +646,13 @@ public class KafkaMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
super.doStop();
|
||||
}
|
||||
};
|
||||
edc.setBeanName("inbound." + name);
|
||||
String groupedName = groupedName(name, group);
|
||||
edc.setBeanName("inbound." + groupedName);
|
||||
|
||||
Binding consumerBinding = Binding.forConsumer(name, edc, moduleInputChannel, accessor);
|
||||
Binding<MessageChannel> consumerBinding = Binding.forConsumer(name, group, edc, moduleInputChannel, accessor);
|
||||
addBinding(consumerBinding);
|
||||
consumerBinding.start();
|
||||
|
||||
return consumerBinding;
|
||||
}
|
||||
|
||||
public KafkaMessageListenerContainer createMessageListenerContainer(Properties properties, String group,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -29,13 +29,12 @@ import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import kafka.api.OffsetRequest;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.binder.BinderPropertyKeys;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.binder.PartitionCapableBinderTests;
|
||||
import org.springframework.cloud.stream.binder.Spy;
|
||||
import org.springframework.cloud.stream.test.junit.kafka.KafkaTestSupport;
|
||||
@@ -48,12 +47,15 @@ import org.springframework.integration.kafka.listener.MessageListener;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
import kafka.api.OffsetRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Integration tests for the {@link KafkaMessageChannelBinder}.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Marius Bogoevici
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
|
||||
@@ -137,8 +139,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
if (codec != null) {
|
||||
props.put(KafkaMessageChannelBinder.COMPRESSION_CODEC, codec);
|
||||
}
|
||||
binder.bindProducer("foo.0", moduleOutputChannel, props);
|
||||
binder.bindConsumer("foo.0", moduleInputChannel, null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo.0", moduleOutputChannel, props);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel, null);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -146,8 +148,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Message<?> inbound = moduleInputChannel.receive(2000);
|
||||
assertNotNull(inbound);
|
||||
assertArrayEquals(ratherBigPayload, (byte[]) inbound.getPayload());
|
||||
binder.unbindProducers("foo.0");
|
||||
binder.unbindConsumers("foo.0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +168,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Properties consumerProperties = new Properties();
|
||||
consumerProperties.put(BinderPropertyKeys.MIN_PARTITION_COUNT, "10");
|
||||
long uniqueBindingId = System.currentTimeMillis();
|
||||
binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
binder.bindConsumer("foo" + uniqueBindingId + ".0", moduleInputChannel, consumerProperties);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, moduleInputChannel, consumerProperties);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -178,8 +180,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Collection<Partition> partitions = binder.getCoreBinder().getConnectionFactory().getPartitions(
|
||||
"foo" + uniqueBindingId + ".0");
|
||||
assertThat(partitions, hasSize(10));
|
||||
binder.unbindProducers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbindConsumers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,8 +201,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
consumerProps.put(BinderPropertyKeys.MIN_PARTITION_COUNT, "5");
|
||||
consumerProps.put(BinderPropertyKeys.CONCURRENCY, "6");
|
||||
long uniqueBindingId = System.currentTimeMillis();
|
||||
binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProps);
|
||||
binder.bindConsumer("foo" + uniqueBindingId + ".0", moduleInputChannel, consumerProps);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProps);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, moduleInputChannel, consumerProps);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -211,8 +213,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Collection<Partition> partitions = binder.getCoreBinder().getConnectionFactory().getPartitions(
|
||||
"foo" + uniqueBindingId + ".0");
|
||||
assertThat(partitions, hasSize(6));
|
||||
binder.unbindProducers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbindConsumers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -231,8 +233,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
consumerProps.put(BinderPropertyKeys.MIN_PARTITION_COUNT, "6");
|
||||
consumerProps.put(BinderPropertyKeys.CONCURRENCY, "5");
|
||||
long uniqueBindingId = System.currentTimeMillis();
|
||||
binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProps);
|
||||
binder.bindConsumer("foo" + uniqueBindingId + ".0", moduleInputChannel, consumerProps);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProps);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, moduleInputChannel, consumerProps);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -243,8 +245,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Collection<Partition> partitions = binder.getCoreBinder().getConnectionFactory().getPartitions(
|
||||
"foo" + uniqueBindingId + ".0");
|
||||
assertThat(partitions, hasSize(6));
|
||||
binder.unbindProducers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbindConsumers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -263,8 +265,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Properties consumerProperties = new Properties();
|
||||
consumerProperties.put(BinderPropertyKeys.MIN_PARTITION_COUNT, "3");
|
||||
long uniqueBindingId = System.currentTimeMillis();
|
||||
binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
binder.bindConsumer("foo" + uniqueBindingId + ".0", moduleInputChannel, consumerProperties);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, moduleInputChannel, consumerProperties);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -275,8 +277,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Collection<Partition> partitions = binder.getCoreBinder().getConnectionFactory().getPartitions(
|
||||
"foo" + uniqueBindingId + ".0");
|
||||
assertThat(partitions, hasSize(5));
|
||||
binder.unbindProducers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbindConsumers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -295,8 +297,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Properties consumerProperties = new Properties();
|
||||
consumerProperties.put(BinderPropertyKeys.MIN_PARTITION_COUNT, "5");
|
||||
long uniqueBindingId = System.currentTimeMillis();
|
||||
binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
binder.bindConsumer("foo" + uniqueBindingId + ".0", moduleInputChannel, consumerProperties);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", moduleOutputChannel, producerProperties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, moduleInputChannel, consumerProperties);
|
||||
Message<?> message = org.springframework.integration.support.MessageBuilder.withPayload(ratherBigPayload).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -307,22 +309,8 @@ public class KafkaBinderTests extends PartitionCapableBinderTests {
|
||||
Collection<Partition> partitions = binder.getCoreBinder().getConnectionFactory().getPartitions(
|
||||
"foo" + uniqueBindingId + ".0");
|
||||
assertThat(partitions, hasSize(5));
|
||||
binder.unbindProducers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbindConsumers("foo" + uniqueBindingId + ".0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Override @Ignore("https://github.com/spring-cloud/spring-cloud-stream/issues/243")
|
||||
public void testSendAndReceivePubSub() throws Exception {
|
||||
}
|
||||
|
||||
@Override @Ignore("https://github.com/spring-cloud/spring-cloud-stream/issues/243")
|
||||
public void createInboundPubSubBeforeOutboundPubSub() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Kafka binder does not support direct binding")
|
||||
@Override
|
||||
public void testDirectBinding() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -33,28 +33,24 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.binder.BinderHeaders;
|
||||
import org.springframework.cloud.stream.binder.BinderPropertyKeys;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.binder.TestUtils;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.channel.interceptor.WireTap;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
|
||||
@Ignore
|
||||
public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
|
||||
@Override
|
||||
@@ -74,9 +70,9 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
binder.bindProducer("partJ.0", output, properties);
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("partJ.0", output, properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
|
||||
properties.clear();
|
||||
@@ -85,15 +81,15 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
properties.put("partitionIndex", "0");
|
||||
QueueChannel input0 = new QueueChannel();
|
||||
input0.setBeanName("test.input0J");
|
||||
binder.bindConsumer("partJ.0", input0, properties);
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("partJ.0", "test", input0, properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
QueueChannel input1 = new QueueChannel();
|
||||
input1.setBeanName("test.input1J");
|
||||
binder.bindConsumer("partJ.0", input1, properties);
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("partJ.0", "test", input1, properties);
|
||||
properties.put("partitionIndex", "2");
|
||||
QueueChannel input2 = new QueueChannel();
|
||||
input2.setBeanName("test.input2J");
|
||||
binder.bindConsumer("partJ.0", input2, properties);
|
||||
Binding<MessageChannel> input2Binding = binder.bindConsumer("partJ.0", "test", input2, properties);
|
||||
|
||||
output.send(new GenericMessage<>(new byte[]{(byte)0}));
|
||||
output.send(new GenericMessage<>(new byte[]{(byte)1}));
|
||||
@@ -112,8 +108,10 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
((byte[]) receive2.getPayload())[0]),
|
||||
containsInAnyOrder((byte)0, (byte)1, (byte)2));
|
||||
|
||||
binder.unbindConsumers("partJ.0");
|
||||
binder.unbindProducers("partJ.0");
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(input2Binding);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,9 +126,9 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
binder.bindProducer("part.0", output, properties);
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("part.0", output, properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
try {
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
@@ -146,15 +144,15 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
properties.put("count","3");
|
||||
QueueChannel input0 = new QueueChannel();
|
||||
input0.setBeanName("test.input0S");
|
||||
binder.bindConsumer("part.0", input0, properties);
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("part.0", "test", input0, properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
QueueChannel input1 = new QueueChannel();
|
||||
input1.setBeanName("test.input1S");
|
||||
binder.bindConsumer("part.0", input1, properties);
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("part.0", "test", input1, properties);
|
||||
properties.put("partitionIndex", "2");
|
||||
QueueChannel input2 = new QueueChannel();
|
||||
input2.setBeanName("test.input2S");
|
||||
binder.bindConsumer("part.0", input2, properties);
|
||||
Binding<MessageChannel> input2Binding = binder.bindConsumer("part.0", "test", input2, properties);
|
||||
|
||||
Message<byte[]> message2 = MessageBuilder.withPayload(new byte[]{2})
|
||||
.setHeader(IntegrationMessageHeaderAccessor.CORRELATION_ID, "foo")
|
||||
@@ -180,74 +178,10 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
((byte[]) receive2.getPayload())[0]),
|
||||
containsInAnyOrder((byte)0, (byte)1, (byte)2));
|
||||
|
||||
binder.unbindConsumers("part.0");
|
||||
binder.unbindProducers("part.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void createInboundPubSubBeforeOutboundPubSub() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
// Test pub/sub by emulating how StreamPlugin handles taps
|
||||
DirectChannel tapChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
QueueChannel module2InputChannel = new QueueChannel();
|
||||
QueueChannel module3InputChannel = new QueueChannel();
|
||||
// Create the tap first
|
||||
String fooTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(fooTapName, module2InputChannel, null, null);
|
||||
|
||||
// Then create the stream
|
||||
binder.bindProducer("baz.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("baz.0", moduleInputChannel, null);
|
||||
moduleOutputChannel.addInterceptor(new WireTap(tapChannel));
|
||||
binder.bindPubSubProducer(fooTapName, tapChannel, null);
|
||||
|
||||
// Another new module is using tap as an input channel
|
||||
String barTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(barTapName, module3InputChannel, null, null);
|
||||
Message<?> message = MessageBuilder.withPayload("foo".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar").build();
|
||||
boolean success = false;
|
||||
boolean retried = false;
|
||||
while (!success) {
|
||||
moduleOutputChannel.send(message);
|
||||
Message<?> inbound = moduleInputChannel.receive(5000);
|
||||
assertNotNull(inbound);
|
||||
assertEquals("foo", new String((byte[])inbound.getPayload()));
|
||||
Message<?> tapped1 = module2InputChannel.receive(5000);
|
||||
Message<?> tapped2 = module3InputChannel.receive(5000);
|
||||
if (tapped1 == null || tapped2 == null) {
|
||||
// listener may not have started
|
||||
assertFalse("Failed to receive tap after retry", retried);
|
||||
retried = true;
|
||||
continue;
|
||||
}
|
||||
success = true;
|
||||
assertEquals("foo", new String((byte[]) tapped1.getPayload()));
|
||||
assertNull(tapped1.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo", new String((byte[])tapped2.getPayload()));
|
||||
}
|
||||
// delete one tap stream is deleted
|
||||
binder.unbindConsumer(barTapName, module3InputChannel);
|
||||
Message<?> message2 = MessageBuilder.withPayload("bar".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar").build();
|
||||
moduleOutputChannel.send(message2);
|
||||
|
||||
// other tap still receives messages
|
||||
Message<?> tapped = module2InputChannel.receive(5000);
|
||||
assertNotNull(tapped);
|
||||
|
||||
// Removed tap does not
|
||||
assertNull(module3InputChannel.receive(1000));
|
||||
|
||||
// when other tap stream is deleted
|
||||
binder.unbindConsumer(fooTapName, module2InputChannel);
|
||||
// Clean up as StreamPlugin would
|
||||
binder.unbindConsumer("baz.0", moduleInputChannel);
|
||||
binder.unbindProducers("baz.0");
|
||||
binder.unbindConsumers("baz.0");
|
||||
assertTrue(getBindings(binder).isEmpty());
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(input2Binding);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -256,8 +190,8 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
binder.bindProducer("foo.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("foo.0", moduleInputChannel, null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo.0", moduleOutputChannel, null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel, null);
|
||||
Message<?> message = MessageBuilder.withPayload("foo".getBytes()).build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
binderBindUnbindLatency();
|
||||
@@ -265,8 +199,8 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
Message<?> inbound = moduleInputChannel.receive(5000);
|
||||
assertNotNull(inbound);
|
||||
assertEquals("foo", new String((byte[])inbound.getPayload()));
|
||||
binder.unbindProducers("foo.0");
|
||||
binder.unbindConsumers("foo.0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
// Ignored, since raw mode does not support headers
|
||||
@@ -277,32 +211,29 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSendAndReceivePubSub() throws Exception {
|
||||
public void testSendAndReceiveWithExplicitConsumerGroup() {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
// Test pub/sub by emulating how StreamPlugin handles taps
|
||||
DirectChannel tapChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
QueueChannel module1InputChannel = new QueueChannel();
|
||||
QueueChannel module2InputChannel = new QueueChannel();
|
||||
QueueChannel module3InputChannel = new QueueChannel();
|
||||
binder.bindProducer("baz.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("baz.0", moduleInputChannel, null);
|
||||
moduleOutputChannel.addInterceptor(new WireTap(tapChannel));
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("baz.0", moduleOutputChannel, null);
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("baz.0", "test", module1InputChannel, null);
|
||||
// A new module is using the tap as an input channel
|
||||
String fooTapName = "baz.0";
|
||||
binder.bindPubSubProducer(fooTapName, tapChannel, null);
|
||||
binder.bindPubSubConsumer(fooTapName, module2InputChannel, null, null);
|
||||
Binding<MessageChannel> input2Binding = binder.bindConsumer(fooTapName, "tap1", module2InputChannel, null);
|
||||
// Another new module is using tap as an input channel
|
||||
String barTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(barTapName, module3InputChannel, null, null);
|
||||
Binding<MessageChannel> input3Binding = binder.bindConsumer(barTapName, "tap2", module3InputChannel, null);
|
||||
|
||||
Message<?> message = MessageBuilder.withPayload("foo".getBytes()).build();
|
||||
boolean success = false;
|
||||
boolean retried = false;
|
||||
while (!success) {
|
||||
moduleOutputChannel.send(message);
|
||||
Message<?> inbound = moduleInputChannel.receive(5000);
|
||||
Message<?> inbound = module1InputChannel.receive(5000);
|
||||
assertNotNull(inbound);
|
||||
assertEquals("foo", new String((byte[])inbound.getPayload()));
|
||||
|
||||
@@ -315,11 +246,11 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
continue;
|
||||
}
|
||||
success = true;
|
||||
assertEquals("foo", new String((byte[])tapped1.getPayload()));
|
||||
assertEquals("foo", new String((byte[])tapped2.getPayload()));
|
||||
assertEquals("foo", new String((byte[]) tapped1.getPayload()));
|
||||
assertEquals("foo", new String((byte[]) tapped2.getPayload()));
|
||||
}
|
||||
// delete one tap stream is deleted
|
||||
binder.unbindConsumer(barTapName, module3InputChannel);
|
||||
binder.unbind(input3Binding);
|
||||
Message<?> message2 = MessageBuilder.withPayload("bar".getBytes()).build();
|
||||
moduleOutputChannel.send(message2);
|
||||
|
||||
@@ -327,15 +258,18 @@ public class RawModeKafkaBinderTests extends KafkaBinderTests {
|
||||
Message<?> tapped = module2InputChannel.receive(5000);
|
||||
assertNotNull(tapped);
|
||||
|
||||
// Removed tap does not
|
||||
// removed tap does not
|
||||
assertNull(module3InputChannel.receive(1000));
|
||||
|
||||
// when other tap stream is deleted
|
||||
binder.unbindConsumer(fooTapName, module2InputChannel);
|
||||
// Clean up as StreamPlugin would
|
||||
binder.unbindConsumer("baz.0", moduleInputChannel);
|
||||
binder.unbindProducers("baz.0");
|
||||
binder.unbindConsumers("baz.0");
|
||||
// re-subscribed tap does receive the message
|
||||
input3Binding = binder.bindConsumer(barTapName, "tap2", module3InputChannel, null);
|
||||
assertNotNull(module3InputChannel.receive(1000));
|
||||
|
||||
// clean up
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(input2Binding);
|
||||
binder.unbind(input3Binding);
|
||||
binder.unbind(producerBinding);
|
||||
assertTrue(getBindings(binder).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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,7 +25,6 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.cloud.stream.binder.BinderUtils;
|
||||
import org.springframework.cloud.stream.binder.BindingCleaner;
|
||||
import org.springframework.cloud.stream.binder.MessageChannelBinderSupport;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -42,7 +41,9 @@ public class RabbitBindingCleaner implements BindingCleaner {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RabbitBindingCleaner.class);
|
||||
|
||||
public static final String BINDER_PREFIX = "binder.";
|
||||
private static final String PREFIX_DELIMITER = ".";
|
||||
|
||||
public static final String BINDER_PREFIX = "binder" + PREFIX_DELIMITER;
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> clean(String entity, boolean isJob) {
|
||||
@@ -128,7 +129,7 @@ public class RabbitBindingCleaner implements BindingCleaner {
|
||||
return prefix.substring(0, prefix.length() - 1);
|
||||
}
|
||||
else {
|
||||
return prefix + BinderUtils.GROUP_INDEX_DELIMITER;
|
||||
return prefix + PREFIX_DELIMITER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -62,7 +62,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binder.AbstractBindingPropertiesAccessor;
|
||||
import org.springframework.cloud.stream.binder.BinderPropertyKeys;
|
||||
import org.springframework.cloud.stream.binder.BinderUtils;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.binder.MessageChannelBinderSupport;
|
||||
import org.springframework.cloud.stream.binder.MessageValues;
|
||||
@@ -98,6 +97,7 @@ import com.rabbitmq.client.Envelope;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.cloud.stream.binder.Binder} implementation backed by RabbitMQ.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author Jennifer Hickey
|
||||
@@ -150,40 +150,16 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
.addAll(RABBIT_CONSUMER_PROPERTIES)
|
||||
.build();
|
||||
|
||||
private static final Set<Object> SUPPORTED_PUBSUB_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_BASIC_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.DURABLE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Basic + concurrency.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_NAMED_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_BASIC_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Basic + concurrency + partitioning.
|
||||
* Basic + durable + concurrency + partitioning.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_BASIC_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.DURABLE)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.add(BinderPropertyKeys.PARTITION_INDEX)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Basic + concurrency + reply headers + delivery mode (reply).
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_REPLYING_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
// request
|
||||
.addAll(SUPPORTED_BASIC_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
// reply
|
||||
.add(RabbitPropertiesAccessor.REPLY_HEADER_PATTERNS)
|
||||
.add(RabbitPropertiesAccessor.DELIVERY_MODE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Rabbit producer properties.
|
||||
*/
|
||||
@@ -195,39 +171,15 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
.add(BinderPropertyKeys.COMPRESS)
|
||||
.build();
|
||||
|
||||
private static final Set<Object> SUPPORTED_PUBSUB_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_BASIC_PRODUCER_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_BASIC_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_ADVANCED_PROPERTIES)
|
||||
.build();
|
||||
|
||||
private static final Set<Object> SUPPORTED_NAMED_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_BASIC_PRODUCER_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_BASIC_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_ADVANCED_PROPERTIES)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Partitioning + rabbit producer properties.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(PRODUCER_PARTITIONING_PROPERTIES)
|
||||
.addAll(SUPPORTED_BASIC_PRODUCER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.DIRECT_BINDING_ALLOWED)
|
||||
.addAll(PRODUCER_BATCHING_BASIC_PROPERTIES)
|
||||
.addAll(PRODUCER_BATCHING_ADVANCED_PROPERTIES)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Basic producer + basic consumer + concurrency + reply headers.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_REQUESTING_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
// request
|
||||
.addAll(SUPPORTED_BASIC_PRODUCER_PROPERTIES)
|
||||
// reply
|
||||
.addAll(SUPPORTED_BASIC_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.add(RabbitPropertiesAccessor.REPLY_HEADER_PATTERNS)
|
||||
.add(RabbitPropertiesAccessor.AUTO_BIND_DLQ)
|
||||
.build();
|
||||
|
||||
private static final MessagePropertiesConverter inboundMessagePropertiesConverter =
|
||||
@@ -436,51 +388,27 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindConsumer(final String name, MessageChannel moduleInputChannel, Properties properties) {
|
||||
public Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel inputChannel, Properties properties) {
|
||||
String baseQueueName = groupedName(name, group);
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("declaring queue for inbound: " + name);
|
||||
}
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_NAMED_CONSUMER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
this.logger.info("declaring queue for inbound: " + baseQueueName + ", bound to: " + name);
|
||||
}
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
String queueName = applyPrefix(accessor.getPrefix(this.defaultPrefix), name);
|
||||
TopicExchange exchange = new TopicExchange(queueName);
|
||||
declareExchange(queueName, exchange);
|
||||
validateConsumerProperties(baseQueueName, properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
String prefix = accessor.getPrefix(this.defaultPrefix);
|
||||
String exchangeName = applyPrefix(prefix, name);
|
||||
TopicExchange exchange = new TopicExchange(exchangeName);
|
||||
declareExchange(exchangeName, exchange);
|
||||
|
||||
String queueName = applyPrefix(prefix, baseQueueName);
|
||||
int partitionIndex = accessor.getPartitionIndex();
|
||||
String dlqNameRoot = name;
|
||||
if (partitionIndex >= 0) {
|
||||
String partitionSuffix = "-" + partitionIndex;
|
||||
queueName += partitionSuffix;
|
||||
dlqNameRoot += partitionSuffix;
|
||||
}
|
||||
Queue queue = new Queue(queueName, true, false, false, queueArgs(accessor, queueName));
|
||||
declareQueue(queueName, queue);
|
||||
autoBindDLQ(dlqNameRoot, accessor);
|
||||
org.springframework.amqp.core.Binding binding = BindingBuilder.bind(queue).to(exchange).with(queueName);
|
||||
declareBinding(queueName, binding);
|
||||
doRegisterConsumer(name, moduleInputChannel, queue, accessor, false);
|
||||
bindExistingProducerDirectlyIfPossible(name, moduleInputChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(String exchangeName, MessageChannel moduleInputChannel, String group,
|
||||
Properties properties) {
|
||||
String name = BinderUtils.groupedName(exchangeName, group);
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("declaring pubsub for inbound: " + name + ", bound to: " + exchangeName);
|
||||
}
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
validateConsumerProperties(name, properties, SUPPORTED_PUBSUB_CONSUMER_PROPERTIES);
|
||||
String prefix = accessor.getPrefix(this.defaultPrefix);
|
||||
TopicExchange exchange = new TopicExchange(applyPrefix(prefix, exchangeName));
|
||||
declareExchange(exchange.getName(), exchange);
|
||||
Queue queue;
|
||||
boolean durable = accessor.isDurable(this.defaultDurableSubscription);
|
||||
String queueName = applyPrefix(prefix, name);
|
||||
if (durable) {
|
||||
queue = new Queue(queueName, true, false, false, queueArgs(accessor, queueName));
|
||||
}
|
||||
@@ -488,11 +416,18 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
queue = new Queue(queueName, false, false, true);
|
||||
}
|
||||
declareQueue(queueName, queue);
|
||||
declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with("#"));
|
||||
doRegisterConsumer(name, moduleInputChannel, queue, accessor, true);
|
||||
if (durable) {
|
||||
autoBindDLQ(name, accessor);
|
||||
if (partitionIndex >= 0) {
|
||||
String bindingKey = String.format("%s-%d", name, partitionIndex);
|
||||
declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(bindingKey));
|
||||
}
|
||||
else {
|
||||
declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with("#"));
|
||||
}
|
||||
Binding<MessageChannel> binding = doRegisterConsumer(baseQueueName, group, inputChannel, queue, accessor);
|
||||
if (durable) {
|
||||
autoBindDLQ(applyPrefix(prefix, baseQueueName), queueName, accessor);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
private Map<String, Object> queueArgs(RabbitPropertiesAccessor accessor, String queueName) {
|
||||
@@ -504,8 +439,9 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
return args;
|
||||
}
|
||||
|
||||
private void doRegisterConsumer(String name, MessageChannel moduleInputChannel, Queue queue,
|
||||
RabbitPropertiesAccessor properties, boolean isPubSub) {
|
||||
private Binding<MessageChannel> doRegisterConsumer(String name, String group, MessageChannel moduleInputChannel, Queue queue,
|
||||
RabbitPropertiesAccessor properties) {
|
||||
Binding<MessageChannel> consumerBinding = null;
|
||||
// Fix for XD-2503
|
||||
// Temporarily overrides the thread context classloader with the one where the SimpleMessageListenerContainer
|
||||
// is defined
|
||||
@@ -520,15 +456,15 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
listenerContainer.setChannelTransacted(properties.getTransacted(this.defaultChannelTransacted));
|
||||
listenerContainer.setDefaultRequeueRejected(properties.getRequeueRejected(this
|
||||
.defaultDefaultRequeueRejected));
|
||||
if (!isPubSub) {
|
||||
int concurrency = properties.getConcurrency(this.defaultConcurrency);
|
||||
concurrency = concurrency > 0 ? concurrency : 1;
|
||||
listenerContainer.setConcurrentConsumers(concurrency);
|
||||
int maxConcurrency = properties.getMaxConcurrency(this.defaultMaxConcurrency);
|
||||
if (maxConcurrency > concurrency) {
|
||||
listenerContainer.setMaxConcurrentConsumers(maxConcurrency);
|
||||
}
|
||||
|
||||
int concurrency = properties.getConcurrency(this.defaultConcurrency);
|
||||
concurrency = concurrency > 0 ? concurrency : 1;
|
||||
listenerContainer.setConcurrentConsumers(concurrency);
|
||||
int maxConcurrency = properties.getMaxConcurrency(this.defaultMaxConcurrency);
|
||||
if (maxConcurrency > concurrency) {
|
||||
listenerContainer.setMaxConcurrentConsumers(maxConcurrency);
|
||||
}
|
||||
|
||||
listenerContainer.setPrefetchCount(properties.getPrefetchCount(this.defaultPrefetchCount));
|
||||
listenerContainer.setTxSize(properties.getTxSize(this.defaultTxSize));
|
||||
listenerContainer.setTaskExecutor(new SimpleAsyncTaskExecutor(queue.getName() + "-"));
|
||||
@@ -559,7 +495,7 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
mapper.setReplyHeaderNames(properties.getReplyHeaderPattens(this.defaultReplyHeaderPatterns));
|
||||
adapter.setHeaderMapper(mapper);
|
||||
adapter.afterPropertiesSet();
|
||||
Binding consumerBinding = Binding.forConsumer(name, adapter, moduleInputChannel, properties);
|
||||
consumerBinding = Binding.forConsumer(name, group, adapter, moduleInputChannel, properties);
|
||||
addBinding(consumerBinding);
|
||||
ReceivingHandler convertingBridge = new ReceivingHandler();
|
||||
convertingBridge.setOutputChannel(moduleInputChannel);
|
||||
@@ -571,6 +507,7 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(originalClassloader);
|
||||
}
|
||||
return consumerBinding;
|
||||
}
|
||||
|
||||
private MessageRecoverer determineRecoverer(String name, RabbitPropertiesAccessor properties) {
|
||||
@@ -587,54 +524,36 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
Properties properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_NAMED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
if (!bindNewProducerDirectlyIfPossible(name, (SubscribableChannel) moduleOutputChannel, accessor)) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("declaring queue for outbound: " + name);
|
||||
}
|
||||
AmqpOutboundEndpoint queue = this.buildOutboundEndpoint(name, accessor, determineRabbitTemplate(accessor));
|
||||
doRegisterProducer(name, moduleOutputChannel, queue, accessor);
|
||||
}
|
||||
}
|
||||
|
||||
private AmqpOutboundEndpoint buildOutboundEndpoint(final String name, RabbitPropertiesAccessor properties,
|
||||
RabbitTemplate rabbitTemplate) {
|
||||
String prefix = properties.getPrefix(this.defaultPrefix);
|
||||
String queueName = applyPrefix(prefix, name);
|
||||
String exchangeName = applyPrefix(prefix, name);
|
||||
String partitionKeyExtractorClass = properties.getPartitionKeyExtractorClass();
|
||||
Expression partitionKeyExpression = properties.getPartitionKeyExpression();
|
||||
TopicExchange exchange = new TopicExchange(queueName);
|
||||
declareExchange(queueName, exchange);
|
||||
TopicExchange exchange = new TopicExchange(exchangeName);
|
||||
declareExchange(exchangeName, exchange);
|
||||
AmqpOutboundEndpoint endpoint = new AmqpOutboundEndpoint(rabbitTemplate);
|
||||
endpoint.setExchangeName(exchange.getName());
|
||||
String baseQueueName = exchangeName + ".default";
|
||||
if (partitionKeyExpression == null && !StringUtils.hasText(partitionKeyExtractorClass)) {
|
||||
Queue queue = new Queue(queueName, true, false, false, queueArgs(properties, queueName));
|
||||
declareQueue(name, queue);
|
||||
autoBindDLQ(name, properties);
|
||||
endpoint.setRoutingKey(queueName);
|
||||
org.springframework.amqp.core.Binding binding = BindingBuilder.bind(queue).to(exchange).with(queueName);
|
||||
declareBinding(queueName, binding);
|
||||
Queue queue = new Queue(baseQueueName, true, false, false, queueArgs(properties, baseQueueName));
|
||||
declareQueue(baseQueueName, queue);
|
||||
autoBindDLQ(baseQueueName, baseQueueName, properties);
|
||||
endpoint.setRoutingKey(name);
|
||||
org.springframework.amqp.core.Binding binding = BindingBuilder.bind(queue).to(exchange).with(name);
|
||||
declareBinding(baseQueueName, binding);
|
||||
}
|
||||
else {
|
||||
endpoint.setExpressionRoutingKey(EXPRESSION_PARSER.parseExpression(buildPartitionRoutingExpression
|
||||
(queueName)));
|
||||
// if the stream is partitioned, create one queue for each target partition
|
||||
endpoint.setExpressionRoutingKey(EXPRESSION_PARSER.parseExpression(buildPartitionRoutingExpression(name)));
|
||||
// if the stream is partitioned, create one queue for each target partition for the default group
|
||||
for (int i = 0; i < properties.getNextModuleCount(); i++) {
|
||||
String partitionSuffix = "-" + i;
|
||||
Queue queue = new Queue(queueName + partitionSuffix, true, false, false, queueArgs(properties, queueName));
|
||||
String partitionQueueName = baseQueueName + partitionSuffix;
|
||||
Queue queue = new Queue(partitionQueueName, true, false, false,
|
||||
queueArgs(properties, partitionQueueName));
|
||||
declareQueue(queue.getName(), queue);
|
||||
autoBindDLQ(name + partitionSuffix, properties);
|
||||
declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(queue.getName()));
|
||||
autoBindDLQ(baseQueueName, baseQueueName + partitionSuffix, properties);
|
||||
declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(name + partitionSuffix));
|
||||
}
|
||||
}
|
||||
configureOutboundHandler(endpoint, properties);
|
||||
@@ -652,18 +571,14 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(String name, MessageChannel moduleOutputChannel,
|
||||
Properties properties) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PUBSUB_PRODUCER_PROPERTIES);
|
||||
public Binding<MessageChannel> bindProducer(String name, MessageChannel outputChannel, Properties properties) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
String exchangeName = applyPrefix(accessor.getPrefix(this.defaultPrefix), name);
|
||||
TopicExchange exchange = new TopicExchange(exchangeName);
|
||||
declareExchange(exchangeName, exchange);
|
||||
AmqpOutboundEndpoint endpoint = new AmqpOutboundEndpoint(determineRabbitTemplate(accessor));
|
||||
endpoint.setExchangeName(exchangeName);
|
||||
endpoint.setRoutingKey(name);
|
||||
configureOutboundHandler(endpoint, accessor);
|
||||
doRegisterProducer(name, moduleOutputChannel, endpoint, accessor);
|
||||
AmqpOutboundEndpoint endpoint = this.buildOutboundEndpoint(name, accessor, determineRabbitTemplate(accessor));
|
||||
return doRegisterProducer(name, outputChannel, endpoint, accessor);
|
||||
}
|
||||
|
||||
private RabbitTemplate determineRabbitTemplate(RabbitPropertiesAccessor properties) {
|
||||
@@ -691,12 +606,12 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
return rabbitTemplate;
|
||||
}
|
||||
|
||||
private void doRegisterProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
private Binding<MessageChannel> doRegisterProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
AmqpOutboundEndpoint delegate, RabbitPropertiesAccessor properties) {
|
||||
this.doRegisterProducer(name, moduleOutputChannel, delegate, null, properties);
|
||||
return this.doRegisterProducer(name, moduleOutputChannel, delegate, null, properties);
|
||||
}
|
||||
|
||||
private void doRegisterProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
private Binding<MessageChannel> doRegisterProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
AmqpOutboundEndpoint delegate, String replyTo, RabbitPropertiesAccessor properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
MessageHandler handler = new SendingHandler(delegate, replyTo, properties);
|
||||
@@ -704,72 +619,33 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
consumer.setBeanFactory(getBeanFactory());
|
||||
consumer.setBeanName("outbound." + name);
|
||||
consumer.afterPropertiesSet();
|
||||
Binding producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer, properties);
|
||||
Binding<MessageChannel> producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer, properties);
|
||||
addBinding(producerBinding);
|
||||
producerBinding.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("binding requestor: " + name);
|
||||
}
|
||||
validateProducerProperties(name, properties, SUPPORTED_REQUESTING_PRODUCER_PROPERTIES);
|
||||
Assert.isInstanceOf(SubscribableChannel.class, requests);
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
String queueName = applyRequests(name);
|
||||
AmqpOutboundEndpoint queue = this.buildOutboundEndpoint(queueName, accessor, this.rabbitTemplate);
|
||||
queue.setBeanFactory(this.getBeanFactory());
|
||||
|
||||
String replyQueueName = accessor.getPrefix(this.defaultPrefix) + name + ".replies."
|
||||
+ this.getIdGenerator().generateId();
|
||||
this.doRegisterProducer(name, requests, queue, replyQueueName, accessor);
|
||||
Queue replyQueue = new Queue(replyQueueName, false, false, true); // auto-delete
|
||||
declareQueue(replyQueueName, replyQueue);
|
||||
this.doRegisterConsumer(name, replies, replyQueue, accessor, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("binding replier: " + name);
|
||||
}
|
||||
validateConsumerProperties(name, properties, SUPPORTED_REPLYING_CONSUMER_PROPERTIES);
|
||||
RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
|
||||
Queue requestQueue = new Queue(applyPrefix(accessor.getPrefix(this.defaultPrefix), applyRequests(name)));
|
||||
declareQueue(requestQueue.getName(), requestQueue);
|
||||
this.doRegisterConsumer(name, requests, requestQueue, accessor, false);
|
||||
|
||||
AmqpOutboundEndpoint replyQueue = new AmqpOutboundEndpoint(this.rabbitTemplate);
|
||||
replyQueue.setExpressionRoutingKey(EXPRESSION_PARSER.parseExpression("headers['" + AmqpHeaders.REPLY_TO +
|
||||
"']"));
|
||||
configureOutboundHandler(replyQueue, accessor);
|
||||
doRegisterProducer(name, replies, replyQueue, accessor);
|
||||
return producerBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* If so requested, declare the DLX/DLQ and bind it. The DLQ is bound to the DLX with a routing key of the original
|
||||
* queue name because we use default exchange routing by queue name for the original message.
|
||||
* @param name The name.
|
||||
* @param queueName The base name for the queue (including the binder prefix, if any).
|
||||
* @param routingKey The routing key for the queue.
|
||||
* @param properties The properties accessor.
|
||||
*/
|
||||
private void autoBindDLQ(final String name, RabbitPropertiesAccessor properties) {
|
||||
private void autoBindDLQ(final String queueName, String routingKey, RabbitPropertiesAccessor properties) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("autoBindDLQ=" + properties.getAutoBindDLQ(this.defaultAutoBindDLQ)
|
||||
+ " for: " + name);
|
||||
+ " for: " + queueName);
|
||||
}
|
||||
if (properties.getAutoBindDLQ(this.defaultAutoBindDLQ)) {
|
||||
String prefix = properties.getPrefix(this.defaultPrefix);
|
||||
String queueName = applyPrefix(prefix, name);
|
||||
String dlqName = constructDLQName(queueName);
|
||||
Queue dlq = new Queue(dlqName);
|
||||
declareQueue(dlqName, dlq);
|
||||
final String dlxName = deadLetterExchangeName(prefix);
|
||||
final DirectExchange dlx = new DirectExchange(dlxName);
|
||||
declareExchange(dlxName, dlx);
|
||||
declareBinding(dlqName, BindingBuilder.bind(dlq).to(dlx).with(queueName));
|
||||
declareBinding(dlqName, BindingBuilder.bind(dlq).to(dlx).with(routingKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,21 +690,10 @@ public class RabbitMessageChannelBinder extends MessageChannelBinderSupport impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindConsumer(String name, MessageChannel channel) {
|
||||
super.unbindConsumer(name, channel);
|
||||
cleanAutoDeclareContext(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindConsumers(String name) {
|
||||
super.unbindConsumers(name);
|
||||
cleanAutoDeclareContext(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindPubSubConsumers(String name, String group) {
|
||||
super.unbindPubSubConsumers(name, group);
|
||||
cleanAutoDeclareContext(BinderUtils.groupedName(name, group));
|
||||
protected void afterUnbind(Binding<MessageChannel> binding) {
|
||||
if (Binding.Type.consumer.equals(binding.getType())) {
|
||||
cleanAutoDeclareContext(groupedName(binding.getName(), binding.getGroup()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addToAutoDeclareContext(String name, Object bean) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -46,7 +46,7 @@ public class LocalizedQueueConnectionFactoryIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
ConnectionFactory defaultConnectionFactory = new CachingConnectionFactory("localhost");
|
||||
ConnectionFactory defaultConnectionFactory = rabbitAvailableRule.getResource();
|
||||
String[] addresses = new String[] { "localhost:9999", "localhost:5672" };
|
||||
String[] adminAddresses = new String[] { "http://localhost:15672", "http://localhost:15672" };
|
||||
String[] nodes = new String[] { "foo@bar", "rabbit@localhost" };
|
||||
@@ -66,6 +66,8 @@ public class LocalizedQueueConnectionFactoryIntegrationTests {
|
||||
RabbitTemplate template = new RabbitTemplate(targetConnectionFactory);
|
||||
template.convertAndSend("", queue.getName(), "foo");
|
||||
assertEquals("foo", template.receiveAndConvert(queue.getName()));
|
||||
((CachingConnectionFactory) targetConnectionFactory).destroy();
|
||||
admin.deleteQueue(queue.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -69,8 +69,8 @@ public class RabbitBinderCleanerTests {
|
||||
CachingConnectionFactory connectionFactory = rabbitWithMgmtEnabled.getResource();
|
||||
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
String queue1Name = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream1 + "." + i);
|
||||
String queue2Name = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream2 + "." + i);
|
||||
String queue1Name = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream1 + ".default." + i);
|
||||
String queue2Name = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream2 + ".default." + i);
|
||||
if (firstQueue == null) {
|
||||
firstQueue = queue1Name;
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class RabbitBinderCleanerTests {
|
||||
|
||||
@Override
|
||||
public Void doInRabbit(Channel channel) throws Exception {
|
||||
String queueName = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream1 + "." + 4);
|
||||
String queueName = MessageChannelBinderSupport.applyPrefix(BINDER_PREFIX, stream1 + ".default." + 4);
|
||||
String consumerTag = channel.basicConsume(queueName, new DefaultConsumer(channel));
|
||||
try {
|
||||
waitForConsumerStateNot(queueName, 0);
|
||||
@@ -160,8 +160,8 @@ public class RabbitBinderCleanerTests {
|
||||
// should *not* clean stream2
|
||||
assertEquals(10, cleanedQueues.size());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertEquals(BINDER_PREFIX + stream1 + "." + i, cleanedQueues.get(i * 2));
|
||||
assertEquals(BINDER_PREFIX + stream1 + "." + i + ".dlq", cleanedQueues.get(i * 2 + 1));
|
||||
assertEquals(BINDER_PREFIX + stream1 + ".default." + i, cleanedQueues.get(i * 2));
|
||||
assertEquals(BINDER_PREFIX + stream1 + ".default." + i + ".dlq", cleanedQueues.get(i * 2 + 1));
|
||||
}
|
||||
List<String> cleanedExchanges = cleanedMap.get("exchanges");
|
||||
assertEquals(6, cleanedExchanges.size());
|
||||
@@ -172,7 +172,7 @@ public class RabbitBinderCleanerTests {
|
||||
cleanedQueues = cleanedMap.get("queues");
|
||||
assertEquals(5, cleanedQueues.size());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertEquals(BINDER_PREFIX + stream2 + "." + i, cleanedQueues.get(i));
|
||||
assertEquals(BINDER_PREFIX + stream2 + ".default." + i, cleanedQueues.get(i));
|
||||
}
|
||||
cleanedExchanges = cleanedMap.get("exchanges");
|
||||
assertEquals(6, cleanedExchanges.size());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2016 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,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.binder.rabbit;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
@@ -26,8 +25,6 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -58,14 +55,13 @@ import org.springframework.amqp.support.AmqpHeaders;
|
||||
import org.springframework.amqp.support.postprocessor.DelegatingDecompressingPostProcessor;
|
||||
import org.springframework.amqp.utils.test.TestUtils;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binder.AbstractTestBinder;
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.binder.BinderPropertyKeys;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.binder.PartitionCapableBinderTests;
|
||||
import org.springframework.cloud.stream.binder.Spy;
|
||||
import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
@@ -110,8 +106,8 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
DirectChannel moduleInputChannel = new DirectChannel();
|
||||
binder.bindProducer("bad.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("bad.0", moduleInputChannel, null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("bad.0", moduleOutputChannel, null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("bad.0", "test", moduleInputChannel, null);
|
||||
Message<?> message = MessageBuilder.withPayload("bad").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
final CountDownLatch latch = new CountDownLatch(3);
|
||||
@@ -125,8 +121,8 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
});
|
||||
moduleOutputChannel.send(message);
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
binder.unbindConsumers("bad.0");
|
||||
binder.unbindProducers("bad.0");
|
||||
binder.unbind(consumerBinding);
|
||||
binder.unbind(producerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,15 +130,15 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("transacted", "true"); // test transacted with defaults; not allowed with ackmode NONE
|
||||
binder.bindConsumer("props.0", new DirectChannel(), properties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("props.0", null, new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
SimpleMessageListenerContainer container = TestUtils.getPropertyValue(endpoint, "messageListenerContainer",
|
||||
SimpleMessageListenerContainer.class);
|
||||
assertEquals(AcknowledgeMode.AUTO, container.getAcknowledgeMode());
|
||||
assertEquals(RabbitMessageChannelBinder.DEFAULT_RABBIT_PREFIX + "props.0", container.getQueueNames()[0]);
|
||||
assertEquals(RabbitMessageChannelBinder.DEFAULT_RABBIT_PREFIX + "props.0.default", container.getQueueNames()[0]);
|
||||
assertTrue(TestUtils.getPropertyValue(container, "transactional", Boolean.class));
|
||||
assertEquals(1, TestUtils.getPropertyValue(container, "concurrentConsumers"));
|
||||
assertNull(TestUtils.getPropertyValue(container, "maxConcurrentConsumers"));
|
||||
@@ -154,7 +150,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
assertEquals(1000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.initialInterval"));
|
||||
assertEquals(10000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.maxInterval"));
|
||||
assertEquals(2.0, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.multiplier"));
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbind(consumerBinding);
|
||||
assertEquals(0, bindings.size());
|
||||
|
||||
properties = new Properties();
|
||||
@@ -171,46 +167,26 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
properties.put("requeue", "false");
|
||||
properties.put("txSize", "10");
|
||||
properties.put("partitionIndex", 0);
|
||||
binder.bindConsumer("props.0", new DirectChannel(), properties);
|
||||
consumerBinding = binder.bindConsumer("props.0", "test", new DirectChannel(), properties);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindingsNow = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindingsNow = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindingsNow.size());
|
||||
endpoint = bindingsNow.get(0).getEndpoint();
|
||||
container = verifyContainer(endpoint);
|
||||
|
||||
assertEquals("foo.props.0", container.getQueueNames()[0]);
|
||||
assertEquals("foo.props.0.test", container.getQueueNames()[0]);
|
||||
|
||||
try {
|
||||
binder.bindPubSubConsumer("dummy", null, null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support consumer properties: "),
|
||||
containsString("partitionIndex"),
|
||||
containsString("concurrency"),
|
||||
containsString(" for dummy.")));
|
||||
}
|
||||
try {
|
||||
binder.bindConsumer("queue:dummy", null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertEquals(getClassUnderTestName() + " does not support consumer property: partitionIndex for queue:dummy.",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbind(consumerBinding);
|
||||
assertEquals(0, bindingsNow.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProducerProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
binder.bindProducer("props.0", new DirectChannel(), null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("props.0", new DirectChannel(), null);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
MessageDeliveryMode mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
|
||||
@@ -219,7 +195,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
List<?> requestHeaders = TestUtils.getPropertyValue(endpoint,
|
||||
"handler.delegate.headerMapper.requestHeaderMatcher.strategies", List.class);
|
||||
assertEquals(2, requestHeaders.size());
|
||||
binder.unbindProducers("props.0");
|
||||
binder.unbind(producerBinding);
|
||||
assertEquals(0, bindings.size());
|
||||
|
||||
Properties properties = new Properties();
|
||||
@@ -232,11 +208,11 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "1");
|
||||
|
||||
binder.bindProducer("props.0", new DirectChannel(), properties);
|
||||
producerBinding = binder.bindProducer("props.0", new DirectChannel(), properties);
|
||||
assertEquals(1, bindings.size());
|
||||
endpoint = bindings.get(0).getEndpoint();
|
||||
assertEquals(
|
||||
"'foo.props.0-' + headers['partition']",
|
||||
"'props.0-' + headers['partition']",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression",
|
||||
SpelExpression.class).getExpressionString());
|
||||
mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
|
||||
@@ -244,167 +220,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
assertEquals(MessageDeliveryMode.NON_PERSISTENT, mode);
|
||||
verifyFooRequestProducer(endpoint);
|
||||
|
||||
try {
|
||||
binder.bindPubSubProducer("dummy", new DirectChannel(), properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), containsString("for dummy."));
|
||||
}
|
||||
try {
|
||||
binder.bindProducer("queue:dummy", new DirectChannel(), properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), containsString("for queue:dummy."));
|
||||
}
|
||||
|
||||
binder.unbindProducers("props.0");
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestReplyRequestorProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("prefix", "foo.");
|
||||
properties.put("deliveryMode", "NON_PERSISTENT");
|
||||
|
||||
properties.put("requestHeaderPatterns", "foo");
|
||||
properties.put("replyHeaderPatterns", "bar");
|
||||
|
||||
properties.put("ackMode", "NONE");
|
||||
properties.put("backOffInitialInterval", "2000");
|
||||
properties.put("backOffMaxInterval", "20000");
|
||||
properties.put("backOffMultiplier", "5.0");
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("maxAttempts", "23");
|
||||
properties.put("maxConcurrency", "3");
|
||||
properties.put("prefix", "foo.");
|
||||
properties.put("prefetch", "20");
|
||||
properties.put("requeue", "false");
|
||||
properties.put("txSize", "10");
|
||||
|
||||
binder.bindRequestor("props.0", new DirectChannel(), new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
|
||||
assertEquals(2, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint(); // producer
|
||||
MessageDeliveryMode mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
|
||||
MessageDeliveryMode.class);
|
||||
assertEquals(MessageDeliveryMode.NON_PERSISTENT, mode);
|
||||
verifyFooRequestBarReplyProducer(endpoint);
|
||||
|
||||
endpoint = bindings.get(1).getEndpoint(); // consumer
|
||||
|
||||
verifyContainer(endpoint);
|
||||
|
||||
verifyBarReplyConsumer(endpoint);
|
||||
|
||||
properties.put("partitionKeyExpression", "'foo'");
|
||||
properties.put("partitionKeyExtractorClass", "foo");
|
||||
properties.put("partitionSelectorExpression", "0");
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "1");
|
||||
properties.put("partitionIndex", "0");
|
||||
try {
|
||||
binder.bindRequestor("dummy", null, null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbindProducers("props.0");
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestReplyReplierProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("prefix", "foo.");
|
||||
properties.put("deliveryMode", "NON_PERSISTENT");
|
||||
|
||||
properties.put("requestHeaderPatterns", "foo");
|
||||
properties.put("replyHeaderPatterns", "bar");
|
||||
|
||||
properties.put("ackMode", "NONE");
|
||||
properties.put("backOffInitialInterval", "2000");
|
||||
properties.put("backOffMaxInterval", "20000");
|
||||
properties.put("backOffMultiplier", "5.0");
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("maxAttempts", "23");
|
||||
properties.put("maxConcurrency", "3");
|
||||
properties.put("prefix", "foo.");
|
||||
properties.put("prefetch", "20");
|
||||
properties.put("requeue", "false");
|
||||
properties.put("txSize", "10");
|
||||
|
||||
binder.bindReplier("props.0", new DirectChannel(), new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
|
||||
assertEquals(2, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(1).getEndpoint(); // producer
|
||||
assertEquals(
|
||||
"headers['amqp_replyTo']",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression",
|
||||
SpelExpression.class).getExpressionString());
|
||||
MessageDeliveryMode mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
|
||||
MessageDeliveryMode.class);
|
||||
assertEquals(MessageDeliveryMode.NON_PERSISTENT, mode);
|
||||
|
||||
verifyFooRequestBarReplyProducer(endpoint);
|
||||
|
||||
endpoint = bindings.get(0).getEndpoint(); // consumer
|
||||
|
||||
verifyContainer(endpoint);
|
||||
|
||||
verifyBarReplyConsumer(endpoint);
|
||||
|
||||
properties.put("partitionKeyExpression", "'foo'");
|
||||
properties.put("partitionKeyExtractorClass", "foo");
|
||||
properties.put("partitionSelectorExpression", "0");
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "1");
|
||||
properties.put("partitionIndex", "0");
|
||||
try {
|
||||
binder.bindReplier("dummy", null, null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support consumer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbindProducers("props.0");
|
||||
binder.unbind(producerBinding);
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@@ -430,14 +246,14 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
|
||||
});
|
||||
binder.bindPubSubConsumer("durabletest.0", moduleInputChannel, "tgroup", properties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("durabletest.0", "tgroup", moduleInputChannel, properties);
|
||||
|
||||
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
|
||||
template.convertAndSend(TEST_PREFIX + "durabletest.0", "", "foo");
|
||||
|
||||
int n = 0;
|
||||
while (n++ < 100) {
|
||||
Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "tgroup.durabletest.0.dlq");
|
||||
Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "durabletest.0.tgroup.dlq");
|
||||
if (deadLetter != null) {
|
||||
assertEquals("foo", deadLetter);
|
||||
break;
|
||||
@@ -446,13 +262,8 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
assertTrue(n < 100);
|
||||
|
||||
binder.unbindConsumer("durabletest.0", moduleInputChannel);
|
||||
binder.unbindPubSubConsumers("durabletest.0", "tgroup");
|
||||
assertNotNull(admin.getQueueProperties(TEST_PREFIX + "tgroup.durabletest.0.dlq"));
|
||||
admin.deleteQueue(TEST_PREFIX + "tgroup.durabletest.0.dlq");
|
||||
admin.deleteQueue(TEST_PREFIX + "tgroup.durabletest.0");
|
||||
admin.deleteExchange(TEST_PREFIX + "durabletest.0");
|
||||
admin.deleteExchange(TEST_PREFIX + "DLX");
|
||||
binder.unbind(consumerBinding);
|
||||
assertNotNull(admin.getQueueProperties(TEST_PREFIX + "durabletest.0.tgroup.dlq"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -476,18 +287,14 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
|
||||
});
|
||||
binder.bindPubSubConsumer("nondurabletest.0", moduleInputChannel, "tgroup", properties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("nondurabletest.0", "tgroup", moduleInputChannel, properties);
|
||||
|
||||
binder.unbindPubSubConsumers("nondurabletest.0", "tgroup");
|
||||
binder.unbind(consumerBinding);
|
||||
assertNull(admin.getQueueProperties(TEST_PREFIX + "nondurabletest.0.dlq"));
|
||||
admin.deleteQueue(TEST_PREFIX + "tgroup.nondurabletest.0");
|
||||
admin.deleteExchange(TEST_PREFIX + "nondurabletest.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoBindDLQ() throws Exception {
|
||||
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
|
||||
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("prefix", TEST_PREFIX);
|
||||
@@ -504,14 +311,14 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
|
||||
});
|
||||
binder.bindConsumer("dlqtest", moduleInputChannel, properties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("dlqtest", null, moduleInputChannel, properties);
|
||||
|
||||
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
|
||||
template.convertAndSend("", TEST_PREFIX + "dlqtest", "foo");
|
||||
template.convertAndSend("", TEST_PREFIX + "dlqtest.default", "foo");
|
||||
|
||||
int n = 0;
|
||||
while (n++ < 100) {
|
||||
Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "dlqtest.dlq");
|
||||
Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "dlqtest.default.dlq");
|
||||
if (deadLetter != null) {
|
||||
assertEquals("foo", deadLetter);
|
||||
break;
|
||||
@@ -520,10 +327,176 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
assertTrue(n < 100);
|
||||
|
||||
binder.unbindConsumer("dlqtest", moduleInputChannel);
|
||||
admin.deleteQueue(TEST_PREFIX + "dlqtest.dlq");
|
||||
admin.deleteQueue(TEST_PREFIX + "dlqtest");
|
||||
admin.deleteExchange(TEST_PREFIX + "DLX");
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoBindDLQPartionedConsumerFirst() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("prefix", "bindertest.");
|
||||
properties.put("autoBindDLQ", "true");
|
||||
properties.put("maxAttempts", "1"); // disable retry
|
||||
properties.put("requeue", "false");
|
||||
properties.put("partitionIndex", "0");
|
||||
DirectChannel input0 = new DirectChannel();
|
||||
input0.setBeanName("test.input0DLQ");
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("partDLQ.0", "dlqPartGrp", input0, properties);
|
||||
Binding<MessageChannel> defaultConsumerBinding1 = binder.bindConsumer("partDLQ.0", null, new QueueChannel(), properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
DirectChannel input1 = new DirectChannel();
|
||||
input1.setBeanName("test.input1DLQ");
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("partDLQ.0", "dlqPartGrp", input1, properties);
|
||||
Binding<MessageChannel> defaultConsumerBinding2 = binder.bindConsumer("partDLQ.0", null, new QueueChannel(), properties);
|
||||
|
||||
properties.clear();
|
||||
properties.put("prefix", "bindertest.");
|
||||
properties.put("autoBindDLQ", "true");
|
||||
properties.put("partitionKeyExtractorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put("partitionSelectorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "2");
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("partDLQ.0", output, properties);
|
||||
|
||||
final CountDownLatch latch0 = new CountDownLatch(1);
|
||||
input0.subscribe(new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
if (latch0.getCount() <= 0) {
|
||||
throw new RuntimeException("dlq");
|
||||
}
|
||||
latch0.countDown();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||
input1.subscribe(new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
if (latch1.getCount() <= 0) {
|
||||
throw new RuntimeException("dlq");
|
||||
}
|
||||
latch1.countDown();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
output.send(new GenericMessage<Integer>(1));
|
||||
assertTrue(latch1.await(10, TimeUnit.SECONDS));
|
||||
|
||||
output.send(new GenericMessage<Integer>(0));
|
||||
assertTrue(latch0.await(10, TimeUnit.SECONDS));
|
||||
|
||||
output.send(new GenericMessage<Integer>(1));
|
||||
|
||||
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
|
||||
template.setReceiveTimeout(10000);
|
||||
|
||||
String streamDLQName = "bindertest.partDLQ.0.dlqPartGrp.dlq";
|
||||
|
||||
org.springframework.amqp.core.Message received = template.receive(streamDLQName);
|
||||
assertNotNull(received);
|
||||
assertEquals(1, received.getMessageProperties().getHeaders().get("partition"));
|
||||
|
||||
output.send(new GenericMessage<Integer>(0));
|
||||
received = template.receive(streamDLQName);
|
||||
assertNotNull(received);
|
||||
assertEquals(0, received.getMessageProperties().getHeaders().get("partition"));
|
||||
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(defaultConsumerBinding1);
|
||||
binder.unbind(defaultConsumerBinding2);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoBindDLQPartionedProducerFirst() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
|
||||
properties.put("prefix", "bindertest.");
|
||||
properties.put("autoBindDLQ", "true");
|
||||
properties.put("partitionKeyExtractorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put("partitionSelectorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "2");
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("partDLQ.1", output, properties);
|
||||
|
||||
properties.clear();
|
||||
properties.put("prefix", "bindertest.");
|
||||
properties.put("autoBindDLQ", "true");
|
||||
properties.put("maxAttempts", "1"); // disable retry
|
||||
properties.put("requeue", "false");
|
||||
properties.put("partitionIndex", "0");
|
||||
DirectChannel input0 = new DirectChannel();
|
||||
input0.setBeanName("test.input0DLQ");
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("partDLQ.1", "dlqPartGrp", input0, properties);
|
||||
Binding<MessageChannel> defaultConsumerBinding1 = binder.bindConsumer("partDLQ.1", null, new QueueChannel(), properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
DirectChannel input1 = new DirectChannel();
|
||||
input1.setBeanName("test.input1DLQ");
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("partDLQ.1", "dlqPartGrp", input1, properties);
|
||||
Binding<MessageChannel> defaultConsumerBinding2 = binder.bindConsumer("partDLQ.1", null, new QueueChannel(), properties);
|
||||
|
||||
final CountDownLatch latch0 = new CountDownLatch(1);
|
||||
input0.subscribe(new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
if (latch0.getCount() <= 0) {
|
||||
throw new RuntimeException("dlq");
|
||||
}
|
||||
latch0.countDown();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||
input1.subscribe(new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
if (latch1.getCount() <= 0) {
|
||||
throw new RuntimeException("dlq");
|
||||
}
|
||||
latch1.countDown();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
output.send(new GenericMessage<Integer>(1));
|
||||
assertTrue(latch1.await(10, TimeUnit.SECONDS));
|
||||
|
||||
output.send(new GenericMessage<Integer>(0));
|
||||
assertTrue(latch0.await(10, TimeUnit.SECONDS));
|
||||
|
||||
output.send(new GenericMessage<Integer>(1));
|
||||
|
||||
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
|
||||
template.setReceiveTimeout(10000);
|
||||
|
||||
String streamDLQName = "bindertest.partDLQ.1.dlqPartGrp.dlq";
|
||||
|
||||
org.springframework.amqp.core.Message received = template.receive(streamDLQName);
|
||||
assertNotNull(received);
|
||||
assertEquals(1, received.getMessageProperties().getHeaders().get("partition"));
|
||||
|
||||
output.send(new GenericMessage<Integer>(0));
|
||||
received = template.receive(streamDLQName);
|
||||
assertNotNull(received);
|
||||
assertEquals(0, received.getMessageProperties().getHeaders().get("partition"));
|
||||
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(defaultConsumerBinding1);
|
||||
binder.unbind(defaultConsumerBinding2);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -532,8 +505,8 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
|
||||
Map<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("x-dead-letter-exchange", TEST_PREFIX + "DLX");
|
||||
args.put("x-dead-letter-routing-key", TEST_PREFIX + "dlqpubtest");
|
||||
Queue queue = new Queue(TEST_PREFIX + "dlqpubtest", true, false, false, args);
|
||||
args.put("x-dead-letter-routing-key", TEST_PREFIX + "dlqpubtest.default");
|
||||
Queue queue = new Queue(TEST_PREFIX + "dlqpubtest.default", true, false, false, args);
|
||||
admin.declareQueue(queue);
|
||||
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
@@ -553,14 +526,14 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
|
||||
});
|
||||
binder.bindConsumer("dlqpubtest", moduleInputChannel, properties);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("dlqpubtest", "default", moduleInputChannel, properties);
|
||||
|
||||
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
|
||||
template.convertAndSend("", TEST_PREFIX + "dlqpubtest", "foo");
|
||||
template.convertAndSend("", TEST_PREFIX + "dlqpubtest.default", "foo");
|
||||
|
||||
int n = 0;
|
||||
while (n++ < 100) {
|
||||
org.springframework.amqp.core.Message deadLetter = template.receive(TEST_PREFIX + "dlqpubtest.dlq");
|
||||
org.springframework.amqp.core.Message deadLetter = template.receive(TEST_PREFIX + "dlqpubtest.default.dlq");
|
||||
if (deadLetter != null) {
|
||||
assertEquals("foo", new String(deadLetter.getBody()));
|
||||
assertNotNull(deadLetter.getMessageProperties().getHeaders().get("x-exception-stacktrace"));
|
||||
@@ -570,10 +543,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
assertTrue(n < 100);
|
||||
|
||||
binder.unbindConsumer("dlqpubtest", moduleInputChannel);
|
||||
admin.deleteQueue(TEST_PREFIX + "dlqpubtest.dlq");
|
||||
admin.deleteQueue(TEST_PREFIX + "dlqpubtest");
|
||||
admin.deleteExchange(TEST_PREFIX + "DLX");
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -591,9 +561,9 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("batchingProducer");
|
||||
binder.bindProducer("batching.0", output, properties);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("batching.0", output, properties);
|
||||
|
||||
while (template.receive(RabbitMessageChannelBinder.DEFAULT_RABBIT_PREFIX + "batching.0") != null) {
|
||||
while (template.receive(RabbitMessageChannelBinder.DEFAULT_RABBIT_PREFIX + "batching.0.default") != null) {
|
||||
}
|
||||
|
||||
Log logger = spy(TestUtils.getPropertyValue(binder, "binder.compressingPostProcessor.logger", Log.class));
|
||||
@@ -606,7 +576,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
output.send(new GenericMessage<>("foo".getBytes()));
|
||||
output.send(new GenericMessage<>("bar".getBytes()));
|
||||
|
||||
Object out = spyOn("batching.0").receive(false);
|
||||
Object out = spyOn("batching.0.default").receive(false);
|
||||
assertThat(out, instanceOf(byte[].class));
|
||||
assertEquals("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", new String((byte[]) out));
|
||||
|
||||
@@ -616,7 +586,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
|
||||
QueueChannel input = new QueueChannel();
|
||||
input.setBeanName("batchingConsumer");
|
||||
binder.bindConsumer("batching.0", input, null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("batching.0", "test", input, null);
|
||||
|
||||
output.send(new GenericMessage<>("foo".getBytes()));
|
||||
output.send(new GenericMessage<>("bar".getBytes()));
|
||||
@@ -629,8 +599,8 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
assertEquals("bar", new String(in.getPayload()));
|
||||
assertNull(in.getHeaders().get(AmqpHeaders.DELIVERY_MODE));
|
||||
|
||||
binder.unbindProducers("batching.0");
|
||||
binder.unbindConsumers("batching.0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -641,53 +611,51 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
public void testLateBinding() throws Exception {
|
||||
RabbitTestSupport.RabbitProxy proxy = new RabbitTestSupport.RabbitProxy();
|
||||
CachingConnectionFactory cf = new CachingConnectionFactory("localhost", proxy.getPort());
|
||||
RabbitMessageChannelBinder binder = new RabbitMessageChannelBinder(cf);
|
||||
AbstractApplicationContext applicationContext = mock(AbstractApplicationContext.class);
|
||||
when(applicationContext.getBeanFactory()).thenReturn(mock(ConfigurableListableBeanFactory.class));
|
||||
binder.setApplicationContext(applicationContext);
|
||||
binder.setDefaultAutoBindDLQ(true);
|
||||
binder.afterPropertiesSet();
|
||||
RabbitMessageChannelBinder rabbitBinder = new RabbitMessageChannelBinder(cf);
|
||||
rabbitBinder.setDefaultAutoBindDLQ(true);
|
||||
AbstractTestBinder<RabbitMessageChannelBinder> binder = new RabbitTestBinder(cf, rabbitBinder);
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put("prefix", "latebinder.");
|
||||
|
||||
MessageChannel moduleOutputChannel = new DirectChannel();
|
||||
binder.bindProducer("late.0", moduleOutputChannel, properties);
|
||||
Binding<MessageChannel> late0ProducerBinding = binder.bindProducer("late.0", moduleOutputChannel, properties);
|
||||
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
binder.bindConsumer("late.0", moduleInputChannel, properties);
|
||||
Binding<MessageChannel> late0ConsumerBinding = binder.bindConsumer("late.0", "test", moduleInputChannel, properties);
|
||||
|
||||
properties.put("partitionKeyExpression", "payload.equals('0') ? 0 : 1");
|
||||
properties.put("partitionSelectorExpression", "hashCode()");
|
||||
properties.put("nextModuleCount", "2");
|
||||
|
||||
MessageChannel partOutputChannel = new DirectChannel();
|
||||
binder.bindProducer("partlate.0", partOutputChannel, properties);
|
||||
Binding<MessageChannel> partlate0ProducerBinding = binder.bindProducer("partlate.0", partOutputChannel, properties);
|
||||
|
||||
QueueChannel partInputChannel0 = new QueueChannel();
|
||||
QueueChannel partInputChannel1 = new QueueChannel();
|
||||
properties.clear();
|
||||
properties.put("prefix", "latebinder.");
|
||||
properties.put("partitionIndex", "0");
|
||||
binder.bindConsumer("partlate.0", partInputChannel0, properties);
|
||||
Binding<MessageChannel> partlate0Consumer0Binding = binder.bindConsumer("partlate.0", "test", partInputChannel0, properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
binder.bindConsumer("partlate.0", partInputChannel1, properties);
|
||||
Binding<MessageChannel> partlate0Consumer1Binding = binder.bindConsumer("partlate.0", "test", partInputChannel1, properties);
|
||||
|
||||
binder.setDefaultAutoBindDLQ(false);
|
||||
rabbitBinder.setDefaultAutoBindDLQ(false);
|
||||
properties.clear();
|
||||
properties.put("prefix", "latebinder.");
|
||||
MessageChannel noDLQOutputChannel = new DirectChannel();
|
||||
binder.bindProducer("lateNoDLQ.0", noDLQOutputChannel, properties);
|
||||
Binding<MessageChannel> noDlqProducerBinding = binder.bindProducer("lateNoDLQ.0", noDLQOutputChannel, properties);
|
||||
|
||||
QueueChannel noDLQInputChannel = new QueueChannel();
|
||||
binder.bindConsumer("lateNoDLQ.0", noDLQInputChannel, properties);
|
||||
Binding<MessageChannel> noDlqConsumerBinding = binder.bindConsumer("lateNoDLQ.0", "test", noDLQInputChannel, properties);
|
||||
|
||||
MessageChannel pubSubOutputChannel = new DirectChannel();
|
||||
binder.bindPubSubProducer("latePubSub", pubSubOutputChannel, properties);
|
||||
MessageChannel outputChannel = new DirectChannel();
|
||||
Binding<MessageChannel> pubSubProducerBinding = binder.bindProducer("latePubSub", outputChannel, properties);
|
||||
QueueChannel pubSubInputChannel = new QueueChannel();
|
||||
binder.bindPubSubConsumer("latePubSub", pubSubInputChannel, "lategroup", properties);
|
||||
Binding<MessageChannel> nonDurableConsumerBinding = binder.bindConsumer("latePubSub", "lategroup", pubSubInputChannel, properties);
|
||||
QueueChannel durablePubSubInputChannel = new QueueChannel();
|
||||
properties.setProperty("durableSubscription", "true");
|
||||
binder.bindPubSubConsumer("latePubSub", durablePubSubInputChannel, "lateDurableGroup", properties);
|
||||
Binding<MessageChannel> durableConsumerBinding = binder.bindConsumer("latePubSub", "lateDurableGroup", durablePubSubInputChannel, properties);
|
||||
|
||||
proxy.start();
|
||||
|
||||
@@ -701,7 +669,7 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
assertNotNull(message);
|
||||
assertEquals("bar", message.getPayload());
|
||||
|
||||
pubSubOutputChannel.send(new GenericMessage<>("baz"));
|
||||
outputChannel.send(new GenericMessage<>("baz"));
|
||||
message = pubSubInputChannel.receive(10000);
|
||||
assertNotNull(message);
|
||||
assertEquals("baz", message.getPayload());
|
||||
@@ -718,28 +686,22 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
assertNotNull(message);
|
||||
assertEquals("1", message.getPayload());
|
||||
|
||||
binder.unbindProducer("late.0", moduleOutputChannel);
|
||||
binder.unbindConsumer("late.0", moduleInputChannel);
|
||||
binder.unbindProducer("partlate.0", moduleOutputChannel);
|
||||
binder.unbindConsumers("partlate.0");
|
||||
binder.unbind(late0ProducerBinding);
|
||||
binder.unbind(late0ConsumerBinding);
|
||||
binder.unbind(partlate0ProducerBinding);
|
||||
binder.unbind(partlate0Consumer0Binding);
|
||||
binder.unbind(partlate0Consumer1Binding);
|
||||
binder.unbind(noDlqProducerBinding);
|
||||
binder.unbind(noDlqConsumerBinding);
|
||||
binder.unbind(pubSubProducerBinding);
|
||||
binder.unbind(nonDurableConsumerBinding);
|
||||
binder.unbind(durableConsumerBinding);
|
||||
|
||||
binder.cleanup();
|
||||
|
||||
proxy.stop();
|
||||
cf.destroy();
|
||||
|
||||
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
|
||||
admin.deleteQueue("latebinder.late.0");
|
||||
admin.deleteQueue("latebinder.lateNoDLQ.0");
|
||||
admin.deleteQueue("latebinder.partlate.0-0");
|
||||
admin.deleteQueue("latebinder.partlate.0-1");
|
||||
admin.deleteQueue("latebinder.late.0.dlq");
|
||||
admin.deleteQueue("latebinder.partlate.0-0.dlq");
|
||||
admin.deleteQueue("latebinder.partlate.0-1.dlq");
|
||||
admin.deleteQueue("latebinder.lateDurableGroup.latePubSub");
|
||||
admin.deleteExchange("latebinder.late.0");
|
||||
admin.deleteExchange("latebinder.lateNoDLQ.0");
|
||||
admin.deleteExchange("latebinder.partlate.0");
|
||||
admin.deleteExchange("latebinder.latePubSub");
|
||||
admin.deleteExchange("latebinder.DLX");
|
||||
this.rabbitAvailableRule.getResource().destroy();
|
||||
}
|
||||
|
||||
@@ -772,26 +734,6 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
return container;
|
||||
}
|
||||
|
||||
private void verifyBarReplyConsumer(AbstractEndpoint endpoint) {
|
||||
List<?> replyMatchers;
|
||||
replyMatchers = TestUtils.getPropertyValue(endpoint,
|
||||
"headerMapper.replyHeaderMatcher.strategies",
|
||||
List.class);
|
||||
assertEquals(1, replyMatchers.size());
|
||||
assertEquals("bar",
|
||||
TestUtils.getPropertyValue(replyMatchers.get(0), "patterns", Collection.class).iterator().next());
|
||||
}
|
||||
|
||||
private void verifyFooRequestBarReplyProducer(AbstractEndpoint endpoint) {
|
||||
verifyFooRequestProducer(endpoint);
|
||||
List<?> replyMatchers = TestUtils.getPropertyValue(endpoint,
|
||||
"handler.delegate.headerMapper.replyHeaderMatcher.strategies",
|
||||
List.class);
|
||||
assertEquals(1, replyMatchers.size());
|
||||
assertEquals("bar",
|
||||
TestUtils.getPropertyValue(replyMatchers.get(0), "patterns", Collection.class).iterator().next());
|
||||
}
|
||||
|
||||
private void verifyFooRequestProducer(AbstractEndpoint endpoint) {
|
||||
List<?> requestMatchers = TestUtils.getPropertyValue(endpoint,
|
||||
"handler.delegate.headerMapper.requestHeaderMatcher.strategies",
|
||||
@@ -807,6 +749,11 @@ public class RabbitBinderTests extends PartitionCapableBinderTests {
|
||||
SpelExpression.class).getExpressionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExpectedRoutingBaseDestination(String name, String group) {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPubSubEndpointRouting(AbstractEndpoint endpoint) {
|
||||
return TestUtils.getPropertyValue(endpoint, "handler.delegate.exchangeNameExpression",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -23,19 +23,20 @@ import java.util.Set;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.cloud.stream.binder.AbstractTestBinder;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.integration.codec.kryo.PojoCodec;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
|
||||
/**
|
||||
* Test support class for {@link RabbitMessageChannelBinder}.
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Gary Russell
|
||||
* @author David Turanski
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RabbitTestBinder extends AbstractTestBinder<RabbitMessageChannelBinder> {
|
||||
|
||||
@@ -45,8 +46,15 @@ public class RabbitTestBinder extends AbstractTestBinder<RabbitMessageChannelBin
|
||||
|
||||
private final Set<String> prefixes = new HashSet<>();
|
||||
|
||||
private final Set<String> queues = new HashSet<String>();
|
||||
|
||||
private final Set<String> exchanges = new HashSet<String>();
|
||||
|
||||
public RabbitTestBinder(ConnectionFactory connectionFactory) {
|
||||
RabbitMessageChannelBinder binder = new RabbitMessageChannelBinder(connectionFactory);
|
||||
this(connectionFactory, new RabbitMessageChannelBinder(connectionFactory));
|
||||
}
|
||||
|
||||
public RabbitTestBinder(ConnectionFactory connectionFactory, RabbitMessageChannelBinder binder) {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(1);
|
||||
@@ -60,75 +68,48 @@ public class RabbitTestBinder extends AbstractTestBinder<RabbitMessageChannelBin
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindConsumer(String name, MessageChannel moduleInputChannel, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindConsumer(name, moduleInputChannel, properties);
|
||||
public Binding<MessageChannel> bindConsumer(String name, String group, MessageChannel moduleInputChannel, Properties properties) {
|
||||
this.queues.add(prefix(properties) + name + (group == null ? ".default" : "." + group));
|
||||
this.exchanges.add(prefix(properties) + name);
|
||||
return super.bindConsumer(name, group, moduleInputChannel, properties);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindProducer(name, moduleOutputChannel, properties);
|
||||
public Binding<MessageChannel> bindProducer(String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
this.queues.add(prefix(properties) + name + ".default");
|
||||
this.exchanges.add(prefix(properties) + name);
|
||||
return super.bindProducer(name, moduleOutputChannel, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(String name, MessageChannel inputChannel, String group, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindPubSubConsumer(name, inputChannel, group, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(String name, MessageChannel outputChannel, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindPubSubProducer(name, outputChannel, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindRequestor(name, requests, replies, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
|
||||
capturePrefix(properties);
|
||||
super.bindReplier(name, requests, replies, properties);
|
||||
}
|
||||
|
||||
public void capturePrefix(Properties properties) {
|
||||
public String prefix(Properties properties) {
|
||||
if (properties != null) {
|
||||
String prefix = properties.getProperty("prefix");
|
||||
if (prefix != null) {
|
||||
this.prefixes.add(prefix);
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
return BINDER_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
if (!queues.isEmpty()) {
|
||||
for (String queue : queues) {
|
||||
rabbitAdmin.deleteQueue(BINDER_PREFIX + queue);
|
||||
// delete any partitioned queues
|
||||
for (int i = 0; i < 10; i++) {
|
||||
rabbitAdmin.deleteQueue(BINDER_PREFIX + queue + "-" + i);
|
||||
}
|
||||
for (String prefix : this.prefixes) {
|
||||
rabbitAdmin.deleteQueue(prefix + queue);
|
||||
// delete any partitioned queues
|
||||
for (int i = 0; i < 10; i++) {
|
||||
rabbitAdmin.deleteQueue(prefix + queue + "-" + i);
|
||||
}
|
||||
rabbitAdmin.deleteExchange(prefix + queue);
|
||||
rabbitAdmin.deleteExchange(prefix + queue + ".requests");
|
||||
}
|
||||
rabbitAdmin.deleteExchange(BINDER_PREFIX + queue);
|
||||
for (String queue : this.queues) {
|
||||
this.rabbitAdmin.deleteQueue(queue);
|
||||
this.rabbitAdmin.deleteQueue(queue + ".dlq");
|
||||
// delete any partitioned queues
|
||||
for (int i = 0; i < 10; i++) {
|
||||
this.rabbitAdmin.deleteQueue(queue + "-" + i);
|
||||
this.rabbitAdmin.deleteQueue(queue + "-" + i + ".dlq");
|
||||
}
|
||||
}
|
||||
if (!topics.isEmpty()) {
|
||||
for (String exchange : topics) {
|
||||
rabbitAdmin.deleteExchange(BINDER_PREFIX + exchange);
|
||||
}
|
||||
for (String exchange : this.exchanges) {
|
||||
this.rabbitAdmin.deleteExchange(exchange);
|
||||
}
|
||||
for (String prefix : this.prefixes) {
|
||||
this.rabbitAdmin.deleteExchange(prefix + "DLX");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -65,8 +65,8 @@ public class RabbitBinderModuleTests {
|
||||
context = null;
|
||||
}
|
||||
RabbitAdmin admin = new RabbitAdmin(rabbitTestSupport.getResource());
|
||||
admin.deleteQueue("binder.input");
|
||||
admin.deleteQueue("binder.output");
|
||||
admin.deleteQueue("binder.input.default");
|
||||
admin.deleteQueue("binder.output.default");
|
||||
admin.deleteExchange("binder.input");
|
||||
admin.deleteExchange("binder.output");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -18,7 +18,9 @@ package org.springframework.cloud.stream.binder.redis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -31,6 +33,8 @@ import org.springframework.cloud.stream.binder.EmbeddedHeadersMessageConverter;
|
||||
import org.springframework.cloud.stream.binder.MessageChannelBinderSupport;
|
||||
import org.springframework.cloud.stream.binder.MessageValues;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -38,9 +42,7 @@ import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.redis.inbound.RedisInboundChannelAdapter;
|
||||
import org.springframework.integration.redis.inbound.RedisQueueMessageDrivenEndpoint;
|
||||
import org.springframework.integration.redis.outbound.RedisPublishingMessageHandler;
|
||||
import org.springframework.integration.redis.outbound.RedisQueueOutboundChannelAdapter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -56,6 +58,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.cloud.stream.binder.Binder} implementation backed by Redis.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author David Turanski
|
||||
@@ -65,71 +68,30 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
|
||||
private static final String ERROR_HEADER = "errorKey";
|
||||
|
||||
private static final String CONSUMER_GROUPS_KEY_PREFIX = "groups.";
|
||||
|
||||
private static final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
private final String[] headersToMap;
|
||||
|
||||
/**
|
||||
* Retry only.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_PUBSUB_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(CONSUMER_STANDARD_PROPERTIES)
|
||||
.addAll(CONSUMER_RETRY_PROPERTIES)
|
||||
.build();
|
||||
private final RedisOperations<String, String> redisOperations;
|
||||
|
||||
/**
|
||||
* Retry + concurrency.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_NAMED_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(CONSUMER_STANDARD_PROPERTIES)
|
||||
.addAll(CONSUMER_RETRY_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Named + partitioning.
|
||||
* Retry + concurrency + partitioning.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
.addAll(SUPPORTED_NAMED_CONSUMER_PROPERTIES)
|
||||
.add(BinderPropertyKeys.PARTITION_INDEX)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Retry + concurrency (request).
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_REPLYING_CONSUMER_PROPERTIES = new SetBuilder()
|
||||
// request
|
||||
.addAll(CONSUMER_STANDARD_PROPERTIES)
|
||||
.addAll(CONSUMER_RETRY_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.add(BinderPropertyKeys.PARTITION_INDEX)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* None.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_PUBSUB_PRODUCER_PROPERTIES = PRODUCER_STANDARD_PROPERTIES;
|
||||
|
||||
/**
|
||||
* None.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_NAMED_PRODUCER_PROPERTIES = PRODUCER_STANDARD_PROPERTIES;
|
||||
|
||||
/**
|
||||
* Partitioning.
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
.addAll(PRODUCER_PARTITIONING_PROPERTIES)
|
||||
.addAll(PRODUCER_STANDARD_PROPERTIES)
|
||||
.add(BinderPropertyKeys.DIRECT_BINDING_ALLOWED)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Retry, concurrency (reply).
|
||||
*/
|
||||
private static final Set<Object> SUPPORTED_REQUESTING_PRODUCER_PROPERTIES = new SetBuilder()
|
||||
// reply
|
||||
.addAll(CONSUMER_RETRY_PROPERTIES)
|
||||
.add(BinderPropertyKeys.CONCURRENCY)
|
||||
.build();
|
||||
|
||||
private final RedisConnectionFactory connectionFactory;
|
||||
@@ -143,11 +105,12 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
this(connectionFactory, new String[0]);
|
||||
}
|
||||
|
||||
public RedisMessageChannelBinder(RedisConnectionFactory connectionFactory,
|
||||
String... headersToMap) {
|
||||
public RedisMessageChannelBinder(RedisConnectionFactory connectionFactory, String... headersToMap) {
|
||||
Assert.notNull(connectionFactory, "connectionFactory must not be null");
|
||||
this.connectionFactory = connectionFactory;
|
||||
|
||||
StringRedisTemplate template = new StringRedisTemplate(connectionFactory);
|
||||
template.afterPropertiesSet();
|
||||
this.redisOperations = template;
|
||||
if (headersToMap != null && headersToMap.length > 0) {
|
||||
String[] combinedHeadersToMap =
|
||||
Arrays.copyOfRange(BinderHeaders.STANDARD_HEADERS, 0, BinderHeaders.STANDARD_HEADERS.length
|
||||
@@ -159,7 +122,6 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
else {
|
||||
this.headersToMap = BinderHeaders.STANDARD_HEADERS;
|
||||
}
|
||||
|
||||
this.errorAdapter = new RedisQueueOutboundChannelAdapter(
|
||||
parser.parseExpression("headers['" + ERROR_HEADER + "']"), connectionFactory);
|
||||
}
|
||||
@@ -173,23 +135,16 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindConsumer(final String name, MessageChannel moduleInputChannel, Properties properties) {
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_NAMED_CONSUMER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateConsumerProperties(name, properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
}
|
||||
protected Binding<MessageChannel> doBindConsumer(final String name, String group, MessageChannel moduleInputChannel, Properties properties) {
|
||||
RedisPropertiesAccessor accessor = new RedisPropertiesAccessor(properties);
|
||||
String queueName = "queue." + name;
|
||||
String queueName = groupedName(name, group);
|
||||
validateConsumerProperties(queueName, properties, SUPPORTED_CONSUMER_PROPERTIES);
|
||||
int partitionIndex = accessor.getPartitionIndex();
|
||||
if (partitionIndex >= 0) {
|
||||
queueName += "-" + partitionIndex;
|
||||
}
|
||||
MessageProducerSupport adapter = createInboundAdapter(accessor, queueName);
|
||||
doRegisterConsumer(name, name + (partitionIndex >= 0 ? "-" + partitionIndex : ""), moduleInputChannel, adapter,
|
||||
accessor);
|
||||
bindExistingProducerDirectlyIfPossible(name, moduleInputChannel);
|
||||
return doRegisterConsumer(name, group, queueName, moduleInputChannel, adapter, accessor);
|
||||
}
|
||||
|
||||
private MessageProducerSupport createInboundAdapter(RedisPropertiesAccessor accessor, String queueName) {
|
||||
@@ -209,37 +164,25 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(final String name, MessageChannel moduleInputChannel, String group,
|
||||
Properties properties) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("declaring pubsub for inbound: " + name);
|
||||
}
|
||||
validateConsumerProperties(name, properties, SUPPORTED_PUBSUB_CONSUMER_PROPERTIES);
|
||||
RedisInboundChannelAdapter adapter = new RedisInboundChannelAdapter(this.connectionFactory);
|
||||
adapter.setBeanFactory(this.getBeanFactory());
|
||||
adapter.setSerializer(null);
|
||||
adapter.setTopics(applyPubSub(name));
|
||||
doRegisterConsumer(name, name, moduleInputChannel, adapter, new RedisPropertiesAccessor(properties));
|
||||
}
|
||||
|
||||
private void doRegisterConsumer(String bindingName, String channelName, MessageChannel moduleInputChannel,
|
||||
private Binding<MessageChannel> doRegisterConsumer(String bindingName, String group, String channelName, MessageChannel moduleInputChannel,
|
||||
MessageProducerSupport adapter, RedisPropertiesAccessor properties) {
|
||||
DirectChannel bridgeToModuleChannel = new DirectChannel();
|
||||
bridgeToModuleChannel.setBeanFactory(this.getBeanFactory());
|
||||
bridgeToModuleChannel.setBeanName(channelName + ".bridge");
|
||||
MessageChannel bridgeInputChannel = addRetryIfNeeded(channelName, bridgeToModuleChannel, properties);
|
||||
adapter.setOutputChannel(bridgeInputChannel);
|
||||
adapter.setBeanName("inbound." + bindingName);
|
||||
adapter.setBeanName("inbound." + channelName);
|
||||
adapter.afterPropertiesSet();
|
||||
Binding consumerBinding = Binding.forConsumer(bindingName, adapter, moduleInputChannel, properties);
|
||||
Binding<MessageChannel> consumerBinding = Binding.forConsumer(channelName, group, adapter, moduleInputChannel, properties);
|
||||
addBinding(consumerBinding);
|
||||
ReceivingHandler convertingBridge = new ReceivingHandler();
|
||||
convertingBridge.setOutputChannel(moduleInputChannel);
|
||||
convertingBridge.setBeanName(channelName + ".bridge.handler");
|
||||
convertingBridge.afterPropertiesSet();
|
||||
bridgeToModuleChannel.subscribe(convertingBridge);
|
||||
this.redisOperations.boundZSetOps(CONSUMER_GROUPS_KEY_PREFIX + bindingName).incrementScore(group, 1);
|
||||
consumerBinding.start();
|
||||
return consumerBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,101 +243,49 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
Properties properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
if (name.startsWith(P2P_NAMED_CHANNEL_TYPE_PREFIX)) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_NAMED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
}
|
||||
RedisPropertiesAccessor accessor = new RedisPropertiesAccessor(properties);
|
||||
if (!bindNewProducerDirectlyIfPossible(name, (SubscribableChannel) moduleOutputChannel, accessor)) {
|
||||
String partitionKeyExtractorClass = accessor.getPartitionKeyExtractorClass();
|
||||
Expression partitionKeyExpression = accessor.getPartitionKeyExpression();
|
||||
RedisQueueOutboundChannelAdapter queue;
|
||||
String queueName = "queue." + name;
|
||||
if (partitionKeyExpression == null && !StringUtils.hasText(partitionKeyExtractorClass)) {
|
||||
queue = new RedisQueueOutboundChannelAdapter(queueName, this.connectionFactory);
|
||||
}
|
||||
else {
|
||||
queue = new RedisQueueOutboundChannelAdapter(
|
||||
parser.parseExpression(buildPartitionRoutingExpression(queueName)), this.connectionFactory);
|
||||
}
|
||||
queue.setIntegrationEvaluationContext(this.evaluationContext);
|
||||
queue.setBeanFactory(this.getBeanFactory());
|
||||
queue.afterPropertiesSet();
|
||||
doRegisterProducer(name, moduleOutputChannel, queue, accessor);
|
||||
protected void afterUnbind(Binding<MessageChannel> binding) {
|
||||
if (Binding.Type.consumer.equals(binding.getType())) {
|
||||
String key = CONSUMER_GROUPS_KEY_PREFIX + binding.getName();
|
||||
this.redisOperations.boundZSetOps(key).incrementScore(binding.getGroup(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(final String name, MessageChannel moduleOutputChannel,
|
||||
Properties properties) {
|
||||
validateProducerProperties(name, properties, SUPPORTED_PUBSUB_PRODUCER_PROPERTIES);
|
||||
RedisPublishingMessageHandler topic = new RedisPublishingMessageHandler(connectionFactory);
|
||||
topic.setBeanFactory(this.getBeanFactory());
|
||||
topic.setTopic(applyPubSub(name));
|
||||
topic.afterPropertiesSet();
|
||||
doRegisterProducer(name, moduleOutputChannel, topic, new RedisPropertiesAccessor(properties));
|
||||
}
|
||||
|
||||
private void doRegisterProducer(final String name, MessageChannel moduleOutputChannel, MessageHandler delegate,
|
||||
RedisPropertiesAccessor properties) {
|
||||
this.doRegisterProducer(name, moduleOutputChannel, delegate, null, properties);
|
||||
}
|
||||
|
||||
private void doRegisterProducer(final String name, MessageChannel moduleOutputChannel, MessageHandler delegate,
|
||||
String replyTo, RedisPropertiesAccessor properties) {
|
||||
public Binding<MessageChannel> bindProducer(final String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
MessageHandler handler = new SendingHandler(delegate, replyTo, properties);
|
||||
validateProducerProperties(name, properties, SUPPORTED_PRODUCER_PROPERTIES);
|
||||
RedisPropertiesAccessor accessor = new RedisPropertiesAccessor(properties);
|
||||
return doRegisterProducer(name, moduleOutputChannel, accessor);
|
||||
}
|
||||
|
||||
private RedisQueueOutboundChannelAdapter createProducerEndpoint(String name, RedisPropertiesAccessor accessor) {
|
||||
String partitionKeyExtractorClass = accessor.getPartitionKeyExtractorClass();
|
||||
Expression partitionKeyExpression = accessor.getPartitionKeyExpression();
|
||||
RedisQueueOutboundChannelAdapter queue;
|
||||
if (partitionKeyExpression == null && !StringUtils.hasText(partitionKeyExtractorClass)) {
|
||||
queue = new RedisQueueOutboundChannelAdapter(name, this.connectionFactory);
|
||||
}
|
||||
else {
|
||||
queue = new RedisQueueOutboundChannelAdapter(
|
||||
parser.parseExpression(buildPartitionRoutingExpression(name)), this.connectionFactory);
|
||||
}
|
||||
queue.setIntegrationEvaluationContext(this.evaluationContext);
|
||||
queue.setBeanFactory(this.getBeanFactory());
|
||||
queue.afterPropertiesSet();
|
||||
return queue;
|
||||
}
|
||||
|
||||
private Binding<MessageChannel> doRegisterProducer(final String name, MessageChannel moduleOutputChannel, RedisPropertiesAccessor properties) {
|
||||
Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
|
||||
MessageHandler handler = new SendingHandler(name, properties);
|
||||
EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel, handler);
|
||||
consumer.setBeanFactory(this.getBeanFactory());
|
||||
consumer.setBeanName("outbound." + name);
|
||||
consumer.afterPropertiesSet();
|
||||
Binding producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer, properties);
|
||||
Binding<MessageChannel> producerBinding = Binding.forProducer(name, moduleOutputChannel, consumer, properties);
|
||||
addBinding(producerBinding);
|
||||
producerBinding.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("binding requestor: " + name);
|
||||
}
|
||||
Assert.isInstanceOf(SubscribableChannel.class, requests);
|
||||
validateProducerProperties(name, properties, SUPPORTED_REQUESTING_PRODUCER_PROPERTIES);
|
||||
RedisQueueOutboundChannelAdapter queue = new RedisQueueOutboundChannelAdapter("queue." + applyRequests(name),
|
||||
this.connectionFactory);
|
||||
queue.setBeanFactory(this.getBeanFactory());
|
||||
queue.afterPropertiesSet();
|
||||
String replyQueueName = name + ".replies." + this.getIdGenerator().generateId();
|
||||
RedisPropertiesAccessor accessor = new RedisPropertiesAccessor(properties);
|
||||
this.doRegisterProducer(name, requests, queue, replyQueueName, accessor);
|
||||
MessageProducerSupport adapter = createInboundAdapter(accessor, replyQueueName);
|
||||
this.doRegisterConsumer(name, name, replies, adapter, accessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("binding replier: " + name);
|
||||
}
|
||||
validateConsumerProperties(name, properties, SUPPORTED_REPLYING_CONSUMER_PROPERTIES);
|
||||
RedisPropertiesAccessor accessor = new RedisPropertiesAccessor(properties);
|
||||
MessageProducerSupport adapter = createInboundAdapter(accessor, "queue." + applyRequests(name));
|
||||
this.doRegisterConsumer(name, name, requests, adapter, accessor);
|
||||
|
||||
RedisQueueOutboundChannelAdapter replyQueue = new RedisQueueOutboundChannelAdapter(
|
||||
RedisMessageChannelBinder.parser.parseExpression("headers['" + BinderHeaders.REPLY_TO + "']"),
|
||||
this.connectionFactory);
|
||||
replyQueue.setBeanFactory(this.getBeanFactory());
|
||||
replyQueue.setIntegrationEvaluationContext(this.evaluationContext);
|
||||
replyQueue.afterPropertiesSet();
|
||||
this.doRegisterProducer(name, replies, replyQueue, accessor);
|
||||
return producerBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -404,37 +295,48 @@ public class RedisMessageChannelBinder extends MessageChannelBinderSupport imple
|
||||
|
||||
private class SendingHandler extends AbstractMessageHandler {
|
||||
|
||||
private final MessageHandler delegate;
|
||||
|
||||
private final String replyTo;
|
||||
private final String bindingName;
|
||||
|
||||
private final PartitioningMetadata partitioningMetadata;
|
||||
|
||||
private final RedisPropertiesAccessor accessor;
|
||||
|
||||
private SendingHandler(MessageHandler delegate, String replyTo, RedisPropertiesAccessor properties) {
|
||||
this.delegate = delegate;
|
||||
this.replyTo = replyTo;
|
||||
private final Map<String, RedisQueueOutboundChannelAdapter> adapters = new HashMap<>();
|
||||
|
||||
private SendingHandler(String bindingName, RedisPropertiesAccessor properties) {
|
||||
this.bindingName = bindingName;
|
||||
this.accessor = properties;
|
||||
this.partitioningMetadata = new PartitioningMetadata(properties, properties.getNextModuleCount());
|
||||
this.setBeanFactory(RedisMessageChannelBinder.this.getBeanFactory());
|
||||
refreshChannelAdapters();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
MessageValues transformed = serializePayloadIfNecessary(message);
|
||||
|
||||
if (replyTo != null) {
|
||||
transformed.put(BinderHeaders.REPLY_TO, this.replyTo);
|
||||
}
|
||||
if (this.partitioningMetadata.isPartitionedModule()) {
|
||||
|
||||
transformed.put(PARTITION_HEADER, determinePartition(message, this.partitioningMetadata));
|
||||
}
|
||||
|
||||
byte[] messageToSend = embeddedHeadersMessageConverter.embedHeaders(transformed,
|
||||
RedisMessageChannelBinder.this.headersToMap);
|
||||
delegate.handleMessage(MessageBuilder.withPayload(messageToSend).copyHeaders(transformed).build());
|
||||
|
||||
refreshChannelAdapters();
|
||||
for (RedisQueueOutboundChannelAdapter adapter : adapters.values()) {
|
||||
adapter.handleMessage((MessageBuilder.withPayload(messageToSend).copyHeaders(transformed).build()));
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshChannelAdapters() {
|
||||
Set<String> groups = redisOperations.boundZSetOps(CONSUMER_GROUPS_KEY_PREFIX + bindingName).rangeByScore(1, Double.MAX_VALUE);
|
||||
for (String group : groups) {
|
||||
if (!adapters.containsKey(group)) {
|
||||
String channel = String.format("%s.%s", this.bindingName, group);
|
||||
adapters.put(group, createProducerEndpoint(channel, accessor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ReceivingHandler extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2016 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,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.binder.redis;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -24,17 +23,15 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -52,6 +49,7 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.redis.inbound.RedisQueueMessageDrivenEndpoint;
|
||||
import org.springframework.integration.redis.outbound.RedisQueueOutboundChannelAdapter;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
@@ -60,6 +58,7 @@ import org.springframework.retry.support.RetryTemplate;
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author David Turanski
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
|
||||
@@ -86,34 +85,20 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
@Ignore("https://github.com/spring-cloud/spring-cloud-stream/issues/247")
|
||||
public void testSendAndReceivePubSub() throws Exception {
|
||||
|
||||
//TimeUnit.SECONDS.sleep(2);
|
||||
|
||||
super.testSendAndReceivePubSub();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createTemplate().boundListOps("queue.direct.0").trim(1, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsumerProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("maxAttempts", "1"); // disable retry
|
||||
binder.bindConsumer("props.0", new DirectChannel(), properties);
|
||||
Binding<MessageChannel> binding = binder.bindConsumer("props.0", "test", new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
assertEquals(binding, bindings.get(0));
|
||||
AbstractEndpoint endpoint = binding.getEndpoint();
|
||||
assertThat(endpoint, instanceOf(RedisQueueMessageDrivenEndpoint.class));
|
||||
assertSame(DirectChannel.class, TestUtils.getPropertyValue(endpoint, "outputChannel").getClass());
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbind(binding);
|
||||
assertEquals(0, bindings.size());
|
||||
|
||||
properties.put("backOffInitialInterval", "2000");
|
||||
@@ -123,48 +108,34 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
properties.put("maxAttempts", "23");
|
||||
properties.put("partitionIndex", 0);
|
||||
|
||||
binder.bindConsumer("props.0", new DirectChannel(), properties);
|
||||
binding = binder.bindConsumer("props.0", "test", new DirectChannel(), properties);
|
||||
assertEquals(1, bindings.size());
|
||||
endpoint = bindings.get(0).getEndpoint();
|
||||
assertEquals(binding, bindings.get(0));
|
||||
endpoint = binding.getEndpoint();
|
||||
verifyConsumer(endpoint);
|
||||
|
||||
try {
|
||||
binder.bindPubSubConsumer("dummy", null, null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support consumer properties: "),
|
||||
containsString("partitionIndex"),
|
||||
containsString("concurrency"),
|
||||
containsString(" for dummy.")));
|
||||
}
|
||||
try {
|
||||
binder.bindConsumer("queue:dummy", null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertEquals(getClassUnderTestName() + " does not support consumer property: partitionIndex for queue:dummy.",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbind(binding);
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProducerProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
binder.bindProducer("props.0", new DirectChannel(), null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("props.0", "test", new DirectChannel(), null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("props.0", new DirectChannel(), null);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(2, bindings.size());
|
||||
assertEquals(producerBinding, bindings.get(1));
|
||||
AbstractEndpoint endpoint = producerBinding.getEndpoint();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, RedisQueueOutboundChannelAdapter> adapters = TestUtils.getPropertyValue(endpoint, "handler.adapters", Map.class);
|
||||
RedisQueueOutboundChannelAdapter adapter = adapters.get("test");
|
||||
assertEquals(
|
||||
"queue.props.0",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.queueNameExpression", Expression.class).getExpressionString());
|
||||
binder.unbindProducers("props.0");
|
||||
assertEquals(0, bindings.size());
|
||||
"props.0.test",
|
||||
TestUtils.getPropertyValue(adapter, "queueNameExpression", Expression.class).getExpressionString());
|
||||
binder.unbind(producerBinding);
|
||||
assertEquals(1, bindings.size());
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put("partitionKeyExpression", "'foo'");
|
||||
@@ -173,138 +144,16 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "1");
|
||||
|
||||
binder.bindProducer("props.0", new DirectChannel(), properties);
|
||||
assertEquals(1, bindings.size());
|
||||
endpoint = bindings.get(0).getEndpoint();
|
||||
assertEquals(
|
||||
"'queue.props.0-' + headers['partition']",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.queueNameExpression", Expression.class).getExpressionString());
|
||||
|
||||
try {
|
||||
binder.bindPubSubProducer("dummy", null, properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), containsString("for dummy."));
|
||||
}
|
||||
try {
|
||||
binder.bindProducer("queue:dummy", new DirectChannel(), properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), containsString("for queue:dummy."));
|
||||
}
|
||||
|
||||
binder.unbindProducers("props.0");
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestReplyRequestorProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
|
||||
properties.put("backOffInitialInterval", "2000");
|
||||
properties.put("backOffMaxInterval", "20000");
|
||||
properties.put("backOffMultiplier", "5.0");
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("maxAttempts", "23");
|
||||
|
||||
binder.bindRequestor("props.0", new DirectChannel(), new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
|
||||
producerBinding = binder.bindProducer("props.0", new DirectChannel(), properties);
|
||||
assertEquals(2, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint(); // producer
|
||||
endpoint = bindings.get(1).getEndpoint();
|
||||
adapter = (RedisQueueOutboundChannelAdapter) TestUtils.getPropertyValue(endpoint, "handler.adapters", Map.class).get("test");
|
||||
assertEquals(
|
||||
"queue.props.0.requests",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.queueNameExpression", Expression.class).getExpressionString());
|
||||
"'props.0.test-' + headers['partition']",
|
||||
TestUtils.getPropertyValue(adapter, "queueNameExpression", Expression.class).getExpressionString());
|
||||
|
||||
endpoint = bindings.get(1).getEndpoint(); // consumer
|
||||
verifyConsumer(endpoint);
|
||||
|
||||
properties.put("partitionKeyExpression", "'foo'");
|
||||
properties.put("partitionKeyExtractorClass", "foo");
|
||||
properties.put("partitionSelectorExpression", "0");
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put("partitionIndex", "0");
|
||||
try {
|
||||
binder.bindRequestor("dummy", new DirectChannel(), new DirectChannel(), properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support producer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbindProducers("props.0");
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestReplyReplierProperties() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
|
||||
properties.put("backOffInitialInterval", "2000");
|
||||
properties.put("backOffMaxInterval", "20000");
|
||||
properties.put("backOffMultiplier", "5.0");
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("maxAttempts", "23");
|
||||
|
||||
binder.bindReplier("props.0", new DirectChannel(), new DirectChannel(), properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
|
||||
assertEquals(2, bindings.size());
|
||||
AbstractEndpoint endpoint = bindings.get(1).getEndpoint(); // producer
|
||||
assertEquals(
|
||||
"headers['replyTo']",
|
||||
TestUtils.getPropertyValue(endpoint, "handler.delegate.queueNameExpression", Expression.class).getExpressionString());
|
||||
|
||||
endpoint = bindings.get(0).getEndpoint(); // consumer
|
||||
verifyConsumer(endpoint);
|
||||
|
||||
properties.put("partitionKeyExpression", "'foo'");
|
||||
properties.put("partitionKeyExtractorClass", "foo");
|
||||
properties.put("partitionSelectorExpression", "0");
|
||||
properties.put("partitionSelectorClass", "foo");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "1");
|
||||
properties.put("partitionIndex", "0");
|
||||
try {
|
||||
binder.bindReplier("dummy", new DirectChannel(), new DirectChannel(), properties);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), allOf(
|
||||
containsString(getClassUnderTestName() + " does not support consumer properties: "),
|
||||
containsString("partitionSelectorExpression"),
|
||||
containsString("partitionKeyExtractorClass"),
|
||||
containsString("partitionKeyExpression"),
|
||||
containsString("partitionSelectorClass")));
|
||||
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
|
||||
}
|
||||
|
||||
binder.unbindConsumers("props.0");
|
||||
binder.unbindProducers("props.0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
assertEquals(0, bindings.size());
|
||||
}
|
||||
|
||||
@@ -335,12 +184,13 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
props.put("maxAttempts", 2);
|
||||
props.put("backOffInitialInterval", 100);
|
||||
props.put("backOffMultiplier", "1.0");
|
||||
binder.bindConsumer("retry.0", new DirectChannel(), props); // no subscriber
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("retry.0", "test", new DirectChannel(), props); // no subscriber
|
||||
channel.send(new GenericMessage<String>("foo"));
|
||||
RedisTemplate<String, Object> template = createTemplate();
|
||||
Object rightPop = template.boundListOps("ERRORS:retry.0").rightPop(5, TimeUnit.SECONDS);
|
||||
Object rightPop = template.boundListOps("ERRORS:retry.0.test").rightPop(5, TimeUnit.SECONDS);
|
||||
assertNotNull(rightPop);
|
||||
assertThat(new String((byte[]) rightPop), containsString("foo"));
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -352,10 +202,6 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
assertTrue(headers.contains("bar"));
|
||||
}
|
||||
|
||||
@Override @Ignore("https://github.com/spring-cloud/spring-cloud-stream/issues/247")
|
||||
public void createInboundPubSubBeforeOutboundPubSub() throws Exception {
|
||||
}
|
||||
|
||||
private RedisTemplate<String, Object> createTemplate() {
|
||||
if (this.redisTemplate != null) {
|
||||
return this.redisTemplate;
|
||||
@@ -370,13 +216,15 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected String getEndpointRouting(AbstractEndpoint endpoint) {
|
||||
return TestUtils.getPropertyValue(endpoint, "handler.delegate.queueNameExpression", Expression.class).getExpressionString();
|
||||
Map<String, RedisQueueOutboundChannelAdapter> adapters = TestUtils.getPropertyValue(endpoint, "handler.adapters", Map.class);
|
||||
return TestUtils.getPropertyValue(adapters.values().iterator().next(), "queueNameExpression", Expression.class).getExpressionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPubSubEndpointRouting(AbstractEndpoint endpoint) {
|
||||
return TestUtils.getPropertyValue(endpoint, "handler.delegate.topicExpression", Expression.class).getExpressionString();
|
||||
protected String getExpectedRoutingBaseDestination(String name, String group) {
|
||||
return name + "." + group;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -391,7 +239,7 @@ public class RedisBinderTests extends PartitionCapableBinderTests {
|
||||
|
||||
@Override
|
||||
public Object receive(boolean expectNull) throws Exception {
|
||||
byte[] bytes = (byte[]) template.boundListOps("queue." + queue).rightPop(50, TimeUnit.MILLISECONDS);
|
||||
byte[] bytes = (byte[]) template.boundListOps(queue).rightPop(50, TimeUnit.MILLISECONDS);
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2016 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,18 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.stream.binder.redis;
|
||||
|
||||
import org.springframework.cloud.stream.binder.AbstractTestBinder;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
|
||||
import org.springframework.integration.codec.Codec;
|
||||
import org.springframework.integration.codec.kryo.PojoCodec;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.support.DefaultMessageBuilderFactory;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.cloud.stream.binder.AbstractTestBinder;
|
||||
|
||||
|
||||
/**
|
||||
* Test support class for {@link RedisMessageChannelBinder}.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -17,19 +17,15 @@
|
||||
package org.springframework.cloud.stream.binder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
@@ -45,6 +41,7 @@ import org.springframework.messaging.MessageHeaders;
|
||||
* @author Gary Russell
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author David Turanski
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractBinderTests {
|
||||
|
||||
@@ -55,20 +52,20 @@ public abstract class AbstractBinderTests {
|
||||
@Test
|
||||
public void testClean() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
binder.bindProducer("foo.0", new DirectChannel(), null);
|
||||
binder.bindConsumer("foo.0", new DirectChannel(), null);
|
||||
binder.bindProducer("foo.1", new DirectChannel(), null);
|
||||
binder.bindConsumer("foo.1", new DirectChannel(), null);
|
||||
binder.bindProducer("foo.2", new DirectChannel(), null);
|
||||
Binding<MessageChannel> foo0ProducerBinding = binder.bindProducer("foo.0", new DirectChannel(), null);
|
||||
Binding<MessageChannel> foo0ConsumerBinding = binder.bindConsumer("foo.0", "test", new DirectChannel(), null);
|
||||
Binding<MessageChannel> foo1ProducerBinding = binder.bindProducer("foo.1", new DirectChannel(), null);
|
||||
Binding<MessageChannel> foo1ConsumerBinding = binder.bindConsumer("foo.1", "test", new DirectChannel(), null);
|
||||
Binding<MessageChannel> foo2ProducerBinding = binder.bindProducer("foo.2", new DirectChannel(), null);
|
||||
Collection<?> bindings = getBindings(binder);
|
||||
assertEquals(5, bindings.size());
|
||||
binder.unbindProducers("foo.0");
|
||||
binder.unbind(foo0ProducerBinding);
|
||||
assertEquals(4, bindings.size());
|
||||
binder.unbindConsumers("foo.0");
|
||||
binder.unbindProducers("foo.1");
|
||||
binder.unbind(foo0ConsumerBinding);
|
||||
binder.unbind(foo1ProducerBinding);
|
||||
assertEquals(2, bindings.size());
|
||||
binder.unbindConsumers("foo.1");
|
||||
binder.unbindProducers("foo.2");
|
||||
binder.unbind(foo1ConsumerBinding);
|
||||
binder.unbind(foo2ProducerBinding);
|
||||
assertTrue(bindings.isEmpty());
|
||||
}
|
||||
|
||||
@@ -77,8 +74,8 @@ public abstract class AbstractBinderTests {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
binder.bindProducer("foo.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("foo.0", moduleInputChannel, null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("foo.0", moduleOutputChannel, null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel, null);
|
||||
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
// Let the consumer actually bind to the producer before sending a msg
|
||||
@@ -89,8 +86,8 @@ public abstract class AbstractBinderTests {
|
||||
assertEquals("foo", inbound.getPayload());
|
||||
assertNull(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
binder.unbindProducers("foo.0");
|
||||
binder.unbindConsumers("foo.0");
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,8 +95,8 @@ public abstract class AbstractBinderTests {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
binder.bindProducer("bar.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("bar.0", moduleInputChannel, null);
|
||||
Binding<MessageChannel> producerBinding = binder.bindProducer("bar.0", moduleOutputChannel, null);
|
||||
Binding<MessageChannel> consumerBinding = binder.bindConsumer("bar.0", "test", moduleInputChannel, null);
|
||||
binderBindUnbindLatency();
|
||||
|
||||
Message<?> message = MessageBuilder.withPayload("foo").build();
|
||||
@@ -109,164 +106,13 @@ public abstract class AbstractBinderTests {
|
||||
assertEquals("foo", inbound.getPayload());
|
||||
assertNull(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertNull(inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
binder.unbindProducers("bar.0");
|
||||
binder.unbindConsumers("bar.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendAndReceivePubSub() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
QueueChannel module2InputChannel = new QueueChannel();
|
||||
QueueChannel module3InputChannel = new QueueChannel();
|
||||
binder.bindProducer("baz.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("baz.0", moduleInputChannel, null);
|
||||
// A new module is using the tap as an input channel
|
||||
String fooTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(fooTapName, module2InputChannel, "tgroup1", null);
|
||||
// Another new module is using tap as an input channel
|
||||
String barTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(barTapName, module3InputChannel, "tgroup2", null);
|
||||
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
boolean success = false;
|
||||
boolean retried = false;
|
||||
while (!success) {
|
||||
moduleOutputChannel.send(message);
|
||||
Message<?> inbound = moduleInputChannel.receive(5000);
|
||||
assertNotNull(inbound);
|
||||
assertEquals("foo", inbound.getPayload());
|
||||
assertNull(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
Message<?> tapped1 = module2InputChannel.receive(5000);
|
||||
Message<?> tapped2 = module3InputChannel.receive(5000);
|
||||
if (tapped1 == null || tapped2 == null) {
|
||||
// listener may not have started
|
||||
assertFalse("Failed to receive tap after retry", retried);
|
||||
retried = true;
|
||||
continue;
|
||||
}
|
||||
success = true;
|
||||
assertEquals("foo", tapped1.getPayload());
|
||||
assertNull(tapped1.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", tapped1.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
assertEquals("foo", tapped2.getPayload());
|
||||
assertNull(tapped2.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", tapped2.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
}
|
||||
// delete one tap stream is deleted
|
||||
binder.unbindPubSubConsumers(barTapName, "tgroup2");
|
||||
Message<?> message2 = MessageBuilder.withPayload("bar").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
moduleOutputChannel.send(message2);
|
||||
|
||||
// other tap still receives messages
|
||||
Message<?> tapped = module2InputChannel.receive(5000);
|
||||
assertNotNull(tapped);
|
||||
|
||||
// Removed tap does not
|
||||
assertNull(module3InputChannel.receive(1000));
|
||||
|
||||
// when other tap stream is deleted
|
||||
binder.unbindConsumer(fooTapName, module2InputChannel);
|
||||
// Clean up as StreamPlugin would
|
||||
binder.unbindConsumer("baz.0", moduleInputChannel);
|
||||
binder.unbindProducer("baz.0", moduleOutputChannel);
|
||||
binder.unbindPubSubConsumers(fooTapName, "tgroup1");
|
||||
assertTrue(getBindings(binder).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createInboundPubSubBeforeOutboundPubSub() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
QueueChannel moduleInputChannel = new QueueChannel();
|
||||
QueueChannel module2InputChannel = new QueueChannel();
|
||||
QueueChannel module3InputChannel = new QueueChannel();
|
||||
// Create the tap first
|
||||
String fooTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(fooTapName, module2InputChannel, "tgroup1", null);
|
||||
|
||||
// Then create the stream
|
||||
binder.bindProducer("baz.0", moduleOutputChannel, null);
|
||||
binder.bindConsumer("baz.0", moduleInputChannel, null);
|
||||
|
||||
// Another new module is using tap as an input channel
|
||||
String barTapName = "baz.0";
|
||||
binder.bindPubSubConsumer(barTapName, module3InputChannel, "tgroup2", null);
|
||||
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
boolean success = false;
|
||||
boolean retried = false;
|
||||
while (!success) {
|
||||
moduleOutputChannel.send(message);
|
||||
Message<?> inbound = moduleInputChannel.receive(5000);
|
||||
assertNotNull(inbound);
|
||||
assertEquals("foo", inbound.getPayload());
|
||||
assertNull(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
Message<?> tapped1 = module2InputChannel.receive(5000);
|
||||
Message<?> tapped2 = module3InputChannel.receive(5000);
|
||||
if (tapped1 == null || tapped2 == null) {
|
||||
// listener may not have started
|
||||
assertFalse("Failed to receive tap after retry", retried);
|
||||
retried = true;
|
||||
continue;
|
||||
}
|
||||
success = true;
|
||||
assertEquals("foo", tapped1.getPayload());
|
||||
assertNull(tapped1.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", tapped1.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
assertEquals("foo", tapped2.getPayload());
|
||||
assertNull(tapped2.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE));
|
||||
assertEquals("foo/bar", tapped2.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
}
|
||||
// delete one tap stream is deleted
|
||||
binder.unbindPubSubConsumers(barTapName, "tgroup2");
|
||||
Message<?> message2 = MessageBuilder.withPayload("bar").setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"foo/bar").build();
|
||||
moduleOutputChannel.send(message2);
|
||||
|
||||
// other tap still receives messages
|
||||
Message<?> tapped = module2InputChannel.receive(5000);
|
||||
assertNotNull(tapped);
|
||||
|
||||
// Removed tap does not
|
||||
assertNull(module3InputChannel.receive(1000));
|
||||
|
||||
// when other tap stream is deleted
|
||||
binder.unbindConsumer(fooTapName, module2InputChannel);
|
||||
// Clean up as StreamPlugin would
|
||||
binder.unbindConsumer("baz.0", moduleInputChannel);
|
||||
binder.unbindProducer("baz.0", moduleOutputChannel);
|
||||
binder.unbindPubSubConsumers(fooTapName, "tgroup1");
|
||||
assertTrue(getBindings(binder).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadDynamic() throws Exception {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(BinderPropertyKeys.PARTITION_KEY_EXPRESSION, "'foo'");
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
try {
|
||||
binder.bindDynamicProducer("queue:foo", properties);
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (BinderException mbe) {
|
||||
Assert.assertEquals("Failed to bind dynamic channel 'queue:foo' with properties " +
|
||||
"{partitionKeyExpression='foo'}",
|
||||
mbe.getMessage());
|
||||
if (binder instanceof AbstractTestBinder) {
|
||||
binder = ((AbstractTestBinder) binder).getCoreBinder();
|
||||
}
|
||||
assertFalse(((MessageChannelBinderSupport) binder).getApplicationContext().containsBean("queue:foo"));
|
||||
}
|
||||
binder.unbind(producerBinding);
|
||||
binder.unbind(consumerBinding);
|
||||
}
|
||||
|
||||
protected Collection<?> getBindings(Binder<MessageChannel> testBinder) {
|
||||
if (testBinder instanceof AbstractTestBinder) {
|
||||
return getBindingsFromBinder(((AbstractTestBinder) testBinder).getCoreBinder());
|
||||
return getBindingsFromBinder(((AbstractTestBinder<?>) testBinder).getCoreBinder());
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -22,19 +22,17 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class that adds test support for {@link Binder}.
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractTestBinder<C extends MessageChannelBinderSupport> implements Binder<MessageChannel> {
|
||||
|
||||
protected Set<String> queues = new HashSet<String>();
|
||||
|
||||
protected Set<String> topics = new HashSet<String>();
|
||||
|
||||
private C binder;
|
||||
|
||||
public void setBinder(C binder) {
|
||||
@@ -48,45 +46,15 @@ public abstract class AbstractTestBinder<C extends MessageChannelBinderSupport>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindConsumer(String name, MessageChannel moduleInputChannel, Properties properties) {
|
||||
binder.bindConsumer(name, moduleInputChannel, properties);
|
||||
public Binding<MessageChannel> bindConsumer(String name, String group, MessageChannel moduleInputChannel, Properties properties) {
|
||||
queues.add(name);
|
||||
return binder.bindConsumer(name, group, moduleInputChannel, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(String name, MessageChannel inputChannel, String group, Properties properties) {
|
||||
binder.bindPubSubConsumer(name, inputChannel, group, properties);
|
||||
addTopic(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
binder.bindProducer(name, moduleOutputChannel, properties);
|
||||
public Binding<MessageChannel> bindProducer(String name, MessageChannel moduleOutputChannel, Properties properties) {
|
||||
queues.add(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(String name, MessageChannel outputChannel, Properties properties) {
|
||||
binder.bindPubSubProducer(name, outputChannel, properties);
|
||||
addTopic(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
binder.bindRequestor(name, requests, replies, properties);
|
||||
queues.add(name + ".requests");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
binder.bindReplier(name, requests, replies, properties);
|
||||
queues.add(name + ".requests");
|
||||
}
|
||||
|
||||
private void addTopic(String topicName) {
|
||||
topics.add("topic." + topicName);
|
||||
return binder.bindProducer(name, moduleOutputChannel, properties);
|
||||
}
|
||||
|
||||
public C getCoreBinder() {
|
||||
@@ -96,40 +64,8 @@ public abstract class AbstractTestBinder<C extends MessageChannelBinderSupport>
|
||||
public abstract void cleanup();
|
||||
|
||||
@Override
|
||||
public void unbindConsumers(String name) {
|
||||
binder.unbindConsumers(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindPubSubConsumers(String name, String group) {
|
||||
binder.unbindPubSubConsumers(name, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindProducers(String name) {
|
||||
binder.unbindProducers(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindConsumer(String name, MessageChannel channel) {
|
||||
binder.unbindConsumer(name, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindProducer(String name, MessageChannel channel) {
|
||||
binder.unbindProducer(name, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel bindDynamicProducer(String name, Properties properties) {
|
||||
this.queues.add(name);
|
||||
return this.binder.bindDynamicProducer(name, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel bindDynamicPubSubProducer(String name, Properties properties) {
|
||||
this.topics.add(name);
|
||||
return this.binder.bindDynamicPubSubProducer(name, properties);
|
||||
public void unbind(Binding<MessageChannel> binding) {
|
||||
binder.unbind(binding);
|
||||
}
|
||||
|
||||
public C getBinder() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2016 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,91 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.stream.binder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for binders that use an external broker.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public abstract class BrokerBinderTests extends
|
||||
AbstractBinderTests {
|
||||
|
||||
@Test
|
||||
public void testDirectBinding() throws Exception {
|
||||
Binder binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(BinderPropertyKeys.DIRECT_BINDING_ALLOWED, "true");
|
||||
|
||||
DirectChannel moduleInputChannel = new DirectChannel();
|
||||
moduleInputChannel.setBeanName("direct.input");
|
||||
DirectChannel moduleOutputChannel = new DirectChannel();
|
||||
moduleOutputChannel.setBeanName("direct.output");
|
||||
binder.bindConsumer("direct.0", moduleInputChannel, null);
|
||||
binder.bindProducer("direct.0", moduleOutputChannel, properties);
|
||||
|
||||
final AtomicReference<Thread> caller = new AtomicReference<Thread>();
|
||||
final AtomicInteger count = new AtomicInteger();
|
||||
moduleInputChannel.subscribe(new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
caller.set(Thread.currentThread());
|
||||
count.incrementAndGet();
|
||||
}
|
||||
});
|
||||
|
||||
moduleOutputChannel.send(new GenericMessage<String>("foo"));
|
||||
moduleOutputChannel.send(new GenericMessage<String>("foo"));
|
||||
|
||||
assertNotNull(caller.get());
|
||||
assertSame(Thread.currentThread(), caller.get());
|
||||
assertEquals(2, count.get());
|
||||
assertNull(spyOn("direct.0").receive(true));
|
||||
|
||||
// Remove direct binding and bind the producer
|
||||
binder.unbindConsumers("direct.0");
|
||||
binderBindUnbindLatency();
|
||||
|
||||
Spy spy = spyOn("direct.0");
|
||||
count.set(0);
|
||||
moduleOutputChannel.send(new GenericMessage<String>("bar"));
|
||||
moduleOutputChannel.send(new GenericMessage<String>("baz"));
|
||||
Object bar = spy.receive(false);
|
||||
assertEquals("bar", bar);
|
||||
Object baz = spy.receive(false);
|
||||
assertEquals("baz", baz);
|
||||
assertEquals(0, count.get());
|
||||
|
||||
// Unbind producer from binder and bind directly again
|
||||
caller.set(null);
|
||||
binder.bindConsumer("direct.0", moduleInputChannel, null);
|
||||
moduleOutputChannel.send(new GenericMessage<String>("foo"));
|
||||
moduleOutputChannel.send(new GenericMessage<String>("foo"));
|
||||
assertNotNull(caller.get());
|
||||
assertSame(Thread.currentThread(), caller.get());
|
||||
assertEquals(2, count.get());
|
||||
assertNull(spy.receive(true));
|
||||
|
||||
binder.unbindProducers("direct.0");
|
||||
binder.unbindConsumers("direct.0");
|
||||
}
|
||||
public abstract class BrokerBinderTests extends AbstractBinderTests {
|
||||
|
||||
/**
|
||||
* Create a new spy on the given 'queue'. This allows de-correlating the creation of
|
||||
@@ -109,6 +30,4 @@ public abstract class BrokerBinderTests extends
|
||||
*/
|
||||
public abstract Spy spyOn(final String name);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -43,11 +43,11 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for binders that support partitioning.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
|
||||
@@ -67,58 +67,59 @@ abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
+ " does not support producer "),
|
||||
containsString("foo"),
|
||||
containsString("baz"),
|
||||
containsString(" for badprops.0.")));
|
||||
containsString(" for badprops.0")));
|
||||
}
|
||||
|
||||
properties.remove("baz");
|
||||
try {
|
||||
binder.bindConsumer("badprops.0", output, properties);
|
||||
binder.bindConsumer("badprops.0", "test", output, properties);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), equalTo(getClassUnderTestName()
|
||||
+ " does not support consumer property: foo for badprops.0."));
|
||||
+ " does not support consumer property: foo for badprops.0.test."));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartitionedModuleSpEL() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("partitionKeyExpression", "payload");
|
||||
properties.put("partitionSelectorExpression", "hashCode()");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "3");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_CONCURRENCY, "2");
|
||||
|
||||
Properties consumerProperties = new Properties();
|
||||
consumerProperties.put("concurrency", "2");
|
||||
consumerProperties.put("partitionIndex", "0");
|
||||
consumerProperties.put("count","3");
|
||||
QueueChannel input0 = new QueueChannel();
|
||||
input0.setBeanName("test.input0S");
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("part.0", "test", input0, consumerProperties);
|
||||
consumerProperties.put("partitionIndex", "1");
|
||||
QueueChannel input1 = new QueueChannel();
|
||||
input1.setBeanName("test.input1S");
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("part.0", "test", input1, consumerProperties);
|
||||
consumerProperties.put("partitionIndex", "2");
|
||||
QueueChannel input2 = new QueueChannel();
|
||||
input2.setBeanName("test.input2S");
|
||||
Binding<MessageChannel> input2Binding = binder.bindConsumer("part.0", "test", input2, consumerProperties);
|
||||
|
||||
Properties producerProperties = new Properties();
|
||||
producerProperties.put("partitionKeyExpression", "payload");
|
||||
producerProperties.put("partitionSelectorExpression", "hashCode()");
|
||||
producerProperties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "3");
|
||||
producerProperties.put(BinderPropertyKeys.NEXT_MODULE_CONCURRENCY, "2");
|
||||
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
binder.bindProducer("part.0", output, properties);
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("part.0", output, producerProperties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(4, bindings.size());
|
||||
try {
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
assertThat(getEndpointRouting(endpoint), containsString("part.0-' + headers['partition']"));
|
||||
AbstractEndpoint endpoint = bindings.get(3).getEndpoint();
|
||||
assertThat(getEndpointRouting(endpoint), containsString(
|
||||
getExpectedRoutingBaseDestination("part.0", "test") + "-' + headers['partition']"));
|
||||
}
|
||||
catch (UnsupportedOperationException ignored) {
|
||||
|
||||
}
|
||||
|
||||
properties.clear();
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("partitionIndex", "0");
|
||||
properties.put("count","3");
|
||||
QueueChannel input0 = new QueueChannel();
|
||||
input0.setBeanName("test.input0S");
|
||||
binder.bindConsumer("part.0", input0, properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
QueueChannel input1 = new QueueChannel();
|
||||
input1.setBeanName("test.input1S");
|
||||
binder.bindConsumer("part.0", input1, properties);
|
||||
properties.put("partitionIndex", "2");
|
||||
QueueChannel input2 = new QueueChannel();
|
||||
input2.setBeanName("test.input2S");
|
||||
binder.bindConsumer("part.0", input2, properties);
|
||||
|
||||
Message<Integer> message2 = MessageBuilder.withPayload(2)
|
||||
.setHeader(IntegrationMessageHeaderAccessor.CORRELATION_ID, "foo")
|
||||
.setHeader(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, 42)
|
||||
@@ -148,17 +149,13 @@ abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if (usesExplicitRouting()) {
|
||||
assertEquals(0, receive0.getPayload());
|
||||
assertEquals(1, receive1.getPayload());
|
||||
assertEquals(2, receive2.getPayload());
|
||||
|
||||
assertThat(receive2, fooMatcher);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
assertThat(Arrays.asList(
|
||||
(Integer) receive0.getPayload(),
|
||||
(Integer) receive1.getPayload(),
|
||||
@@ -176,46 +173,48 @@ abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
containsOur3Messages);
|
||||
|
||||
}
|
||||
|
||||
binder.unbindConsumers("part.0");
|
||||
binder.unbindProducers("part.0");
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(input2Binding);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartitionedModuleJava() throws Exception {
|
||||
Binder<MessageChannel> binder = getBinder();
|
||||
Properties properties = new Properties();
|
||||
properties.put("partitionKeyExtractorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put("partitionSelectorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "3");
|
||||
properties.put(BinderPropertyKeys.NEXT_MODULE_CONCURRENCY, "2");
|
||||
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
binder.bindProducer("partJ.0", output, properties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(1, bindings.size());
|
||||
if (usesExplicitRouting()) {
|
||||
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
|
||||
assertThat(getEndpointRouting(endpoint), containsString("partJ.0-' + headers['partition']"));
|
||||
}
|
||||
|
||||
properties.clear();
|
||||
properties.put("concurrency", "2");
|
||||
properties.put("count","3");
|
||||
properties.put("partitionIndex", "0");
|
||||
Properties consumerProperties = new Properties();
|
||||
consumerProperties.put("concurrency", "2");
|
||||
consumerProperties.put("count","3");
|
||||
consumerProperties.put("partitionIndex", "0");
|
||||
QueueChannel input0 = new QueueChannel();
|
||||
input0.setBeanName("test.input0J");
|
||||
binder.bindConsumer("partJ.0", input0, properties);
|
||||
properties.put("partitionIndex", "1");
|
||||
Binding<MessageChannel> input0Binding = binder.bindConsumer("partJ.0", "test", input0, consumerProperties);
|
||||
consumerProperties.put("partitionIndex", "1");
|
||||
QueueChannel input1 = new QueueChannel();
|
||||
input1.setBeanName("test.input1J");
|
||||
binder.bindConsumer("partJ.0", input1, properties);
|
||||
properties.put("partitionIndex", "2");
|
||||
Binding<MessageChannel> input1Binding = binder.bindConsumer("partJ.0", "test", input1, consumerProperties);
|
||||
consumerProperties.put("partitionIndex", "2");
|
||||
QueueChannel input2 = new QueueChannel();
|
||||
input2.setBeanName("test.input2J");
|
||||
binder.bindConsumer("partJ.0", input2, properties);
|
||||
Binding<MessageChannel> input2Binding = binder.bindConsumer("partJ.0", "test", input2, consumerProperties);
|
||||
|
||||
Properties producerProperties = new Properties();
|
||||
producerProperties.put("partitionKeyExtractorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
producerProperties.put("partitionSelectorClass", "org.springframework.cloud.stream.binder.PartitionTestSupport");
|
||||
producerProperties.put(BinderPropertyKeys.NEXT_MODULE_COUNT, "3");
|
||||
producerProperties.put(BinderPropertyKeys.NEXT_MODULE_CONCURRENCY, "2");
|
||||
DirectChannel output = new DirectChannel();
|
||||
output.setBeanName("test.output");
|
||||
Binding<MessageChannel> outputBinding = binder.bindProducer("partJ.0", output, producerProperties);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Binding<MessageChannel>> bindings = TestUtils.getPropertyValue(binder, "binder.bindings", List.class);
|
||||
assertEquals(4, bindings.size());
|
||||
if (usesExplicitRouting()) {
|
||||
AbstractEndpoint endpoint = bindings.get(3).getEndpoint();
|
||||
assertThat(getEndpointRouting(endpoint), containsString(
|
||||
getExpectedRoutingBaseDestination("partJ.0", "test") + "-' + headers['partition']"));
|
||||
}
|
||||
|
||||
output.send(new GenericMessage<Integer>(2));
|
||||
output.send(new GenericMessage<Integer>(1));
|
||||
@@ -242,8 +241,10 @@ abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
containsInAnyOrder(0, 1, 2));
|
||||
}
|
||||
|
||||
binder.unbindConsumers("partJ.0");
|
||||
binder.unbindProducers("partJ.0");
|
||||
binder.unbind(input0Binding);
|
||||
binder.unbind(input1Binding);
|
||||
binder.unbind(input2Binding);
|
||||
binder.unbind(outputBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,6 +262,14 @@ abstract public class PartitionCapableBinderTests extends BrokerBinderTests {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* For implementations that rely on explicit routing, return the expected base destination
|
||||
* (the part that precedes '-partition' within the expression).
|
||||
*/
|
||||
protected String getExpectedRoutingBaseDestination(String name, String group) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* For implementations that rely on explicit routing, return the routing expression.
|
||||
*/
|
||||
|
||||
@@ -269,31 +269,13 @@ public class MessageChannelBinderSupportTests {
|
||||
public class TestMessageChannelBinder extends MessageChannelBinderSupport {
|
||||
|
||||
@Override
|
||||
public void bindConsumer(String name, MessageChannel channel, Properties properties) {
|
||||
protected Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel channel, Properties properties) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubConsumer(String name, MessageChannel moduleInputChannel, String group,
|
||||
Properties properties) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPubSubProducer(String name, MessageChannel moduleOutputChannel,
|
||||
Properties properties) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindProducer(String name, MessageChannel channel, Properties properties) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
|
||||
Properties properties) {
|
||||
public Binding<MessageChannel> bindProducer(String name, MessageChannel channel, Properties properties) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user