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 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