/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xd.dirt.integration.bus.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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
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.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
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.expression.spel.standard.SpelExpression;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.xd.dirt.integration.bus.Binding;
import org.springframework.xd.dirt.integration.bus.BusProperties;
import org.springframework.xd.dirt.integration.bus.MessageBus;
import org.springframework.xd.dirt.integration.bus.PartitionCapableBusTests;
import org.springframework.xd.test.rabbit.RabbitTestSupport;
/**
* @author Mark Fisher
* @author Gary Russell
*/
public class RabbitMessageBusTests extends PartitionCapableBusTests {
@Rule
public RabbitTestSupport rabbitAvailableRule = new RabbitTestSupport();
@Override
protected MessageBus getMessageBus() {
if (testMessageBus == null) {
testMessageBus = new RabbitTestMessageBus(rabbitAvailableRule.getResource(), getCodec());
}
return testMessageBus;
}
@Override
protected boolean usesExplicitRouting() {
return true;
}
@Test
public void testSendAndReceiveBad() throws Exception {
MessageBus messageBus = getMessageBus();
DirectChannel moduleOutputChannel = new DirectChannel();
DirectChannel moduleInputChannel = new DirectChannel();
messageBus.bindProducer("bad.0", moduleOutputChannel, null);
messageBus.bindConsumer("bad.0", moduleInputChannel, null);
Message<?> message = MessageBuilder.withPayload("bad").setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar").build();
final CountDownLatch latch = new CountDownLatch(3);
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
latch.countDown();
throw new RuntimeException("bad");
}
});
moduleOutputChannel.send(message);
assertTrue(latch.await(10, TimeUnit.SECONDS));
messageBus.unbindConsumers("bad.0");
messageBus.unbindProducers("bad.0");
}
@Test
public void testConsumerProperties() throws Exception {
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("transacted", "true"); // test transacted with defaults; not allowed with ackmode NONE
bus.bindConsumer("props.0", new DirectChannel(), properties);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.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("xdbus.props.0", container.getQueueNames()[0]);
assertTrue(TestUtils.getPropertyValue(container, "transactional", Boolean.class));
assertEquals(1, TestUtils.getPropertyValue(container, "concurrentConsumers"));
assertNull(TestUtils.getPropertyValue(container, "maxConcurrentConsumers"));
assertTrue(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class));
assertEquals(1, TestUtils.getPropertyValue(container, "prefetchCount"));
assertEquals(1, TestUtils.getPropertyValue(container, "txSize"));
Advice retry = TestUtils.getPropertyValue(container, "adviceChain", Advice[].class)[0];
assertEquals(3, TestUtils.getPropertyValue(retry, "retryOperations.retryPolicy.maxAttempts"));
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"));
bus.unbindConsumers("props.0");
assertEquals(0, bindings.size());
properties = new Properties();
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("requestHeaderPatterns", "foo");
properties.put("requeue", "false");
properties.put("txSize", "10");
properties.put("partitionIndex", 0);
bus.bindConsumer("props.0", new DirectChannel(), properties);
@SuppressWarnings("unchecked")
List<Binding> bindingsNow = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(1, bindingsNow.size());
endpoint = bindingsNow.get(0).getEndpoint();
container = verifyContainer(endpoint);
assertEquals("foo.props.0", container.getQueueNames()[0]);
try {
bus.bindPubSubConsumer("dummy", null, properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(
containsString("RabbitMessageBus does not support consumer property: "),
containsString("partitionIndex"),
containsString(" for dummy.")));
}
try {
bus.bindConsumer("queue:dummy", null, properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertEquals("RabbitMessageBus does not support consumer property: partitionIndex for queue:dummy.",
e.getMessage());
}
bus.unbindConsumers("props.0");
assertEquals(0, bindingsNow.size());
}
@Test
public void testProducerProperties() throws Exception {
MessageBus bus = getMessageBus();
bus.bindProducer("props.0", new DirectChannel(), null);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(1, bindings.size());
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
assertEquals("xdbus.props.0", TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKey"));
MessageDeliveryMode mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
MessageDeliveryMode.class);
assertEquals(MessageDeliveryMode.PERSISTENT, mode);
List<?> requestHeaders = TestUtils.getPropertyValue(endpoint,
"handler.delegate.headerMapper.requestHeaderMatcher.strategies", List.class);
assertEquals(2, requestHeaders.size());
bus.unbindProducers("props.0");
assertEquals(0, bindings.size());
Properties properties = new Properties();
properties.put("prefix", "foo.");
properties.put("deliveryMode", "NON_PERSISTENT");
properties.put("requestHeaderPatterns", "foo");
properties.put("partitionKeyExpression", "'foo'");
properties.put("partitionKeyExtractorClass", "foo");
properties.put("partitionSelectorExpression", "0");
properties.put("partitionSelectorClass", "foo");
properties.put(BusProperties.NEXT_MODULE_COUNT, "2");
bus.bindProducer("props.0", new DirectChannel(), properties);
assertEquals(1, bindings.size());
endpoint = bindings.get(0).getEndpoint();
assertEquals(
"'foo.props.0-' + headers['partition']",
TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression", SpelExpression.class).getExpressionString());
mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode",
MessageDeliveryMode.class);
assertEquals(MessageDeliveryMode.NON_PERSISTENT, mode);
verifyFooRequestProducer(endpoint);
try {
bus.bindPubSubProducer("dummy", new DirectChannel(), properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(
containsString("RabbitMessageBus does not support producer properties: "),
containsString("partitionSelectorExpression"),
containsString("partitionKeyExtractorClass"),
containsString("partitionKeyExpression"),
containsString("partitionSelectorClass")));
assertThat(e.getMessage(), containsString("for dummy."));
}
try {
bus.bindProducer("queue:dummy", new DirectChannel(), properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(
containsString("RabbitMessageBus does not support producer properties: "),
containsString("partitionSelectorExpression"),
containsString("partitionKeyExtractorClass"),
containsString("partitionKeyExpression"),
containsString("partitionSelectorClass")));
assertThat(e.getMessage(), containsString("for queue:dummy."));
}
bus.unbindProducers("props.0");
assertEquals(0, bindings.size());
}
@Test
public void testRequestReplyRequestorProperties() throws Exception {
MessageBus bus = getMessageBus();
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");
bus.bindRequestor("props.0", new DirectChannel(), new DirectChannel(), properties);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(2, bindings.size());
AbstractEndpoint endpoint = bindings.get(0).getEndpoint(); // producer
assertEquals("foo.props.0.requests",
TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKey"));
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(BusProperties.NEXT_MODULE_COUNT, "1");
properties.put("partitionIndex", "0");
try {
bus.bindRequestor("dummy", null, null, properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(
containsString("RabbitMessageBus does not support producer properties: "),
containsString("partitionSelectorExpression"),
containsString("partitionKeyExtractorClass"),
containsString("partitionKeyExpression"),
containsString("partitionSelectorClass")));
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
}
bus.unbindConsumers("props.0");
bus.unbindProducers("props.0");
assertEquals(0, bindings.size());
}
@Test
public void testRequestReplyReplierProperties() throws Exception {
MessageBus bus = getMessageBus();
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");
bus.bindReplier("props.0", new DirectChannel(), new DirectChannel(), properties);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.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(BusProperties.NEXT_MODULE_COUNT, "1");
properties.put("partitionIndex", "0");
try {
bus.bindReplier("dummy", null, null, properties);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(
containsString("RabbitMessageBus does not support consumer properties: "),
containsString("partitionSelectorExpression"),
containsString("partitionKeyExtractorClass"),
containsString("partitionKeyExpression"),
containsString("partitionSelectorClass")));
assertThat(e.getMessage(), allOf(containsString("partitionIndex"), containsString("for dummy.")));
}
bus.unbindConsumers("props.0");
bus.unbindProducers("props.0");
assertEquals(0, bindings.size());
}
@Test
public void testDurablePubSubWithAutoBindDLQ() throws Exception {
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("prefix", "xdbustest.");
properties.put("autoBindDLQ", "true");
properties.put("durableSubscription", "true");
properties.put("maxAttempts", "1"); // disable retry
properties.put("requeue", "false");
properties.put("concurrency", "2");
DirectChannel moduleInputChannel = new DirectChannel();
moduleInputChannel.setBeanName("durableTest");
final List<Thread> threads = new ArrayList<>();
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
threads.add(Thread.currentThread());
throw new RuntimeException("foo");
}
});
bus.bindPubSubConsumer("teststream.tap:stream:durabletest.0", moduleInputChannel, properties);
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
template.convertAndSend("xdbustest.topic.tap:stream:durabletest.0", "", "foo");
template.setReceiveTimeout(10000);
Object deadLetter = template.receiveAndConvert("xdbustest.teststream.tap:stream:durabletest.0.dlq");
assertNotNull(deadLetter);
assertEquals("foo", deadLetter);
template.convertAndSend("xdbustest.topic.tap:stream:durabletest.0", "", "bar");
deadLetter = template.receiveAndConvert("xdbustest.teststream.tap:stream:durabletest.0.dlq");
assertNotNull(deadLetter);
assertEquals("bar", deadLetter);
template.setReceiveTimeout(10);
assertNull(template.receiveAndConvert("xdbustest.teststream.tap:stream:durabletest.0.dlq"));
assertEquals(2, threads.size());
assertNotSame(threads.get(0), threads.get(1));
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(1, bindings.size());
assertEquals(2,
TestUtils.getPropertyValue(bindings.get(0), "endpoint.messageListenerContainer.concurrentConsumers"));
bus.unbindConsumer("teststream.tap:stream:durabletest.0", moduleInputChannel);
assertNotNull(admin.getQueueProperties("xdbustest.teststream.tap:stream:durabletest.0.dlq"));
admin.deleteQueue("xdbustest.teststream.tap:stream:durabletest.0.dlq");
admin.deleteQueue("xdbustest.teststream.tap:stream:durabletest.0");
admin.deleteExchange("xdbustest.topic.tap:stream:durabletest.0");
admin.deleteExchange("xdbustest.DLX");
assertEquals(0, bindings.size());
}
@Test
public void testNonDurablePubSubWithAutoBindDLQ() throws Exception {
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("prefix", "xdbustest.");
properties.put("autoBindDLQ", "true");
properties.put("durableSubscription", "false");
properties.put("maxAttempts", "1"); // disable retry
properties.put("requeue", "false");
DirectChannel moduleInputChannel = new DirectChannel();
moduleInputChannel.setBeanName("nondurabletest");
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
throw new RuntimeException("foo");
}
});
bus.bindPubSubConsumer("teststream.tap:stream:nondurabletest.0", moduleInputChannel, properties);
bus.unbindConsumer("teststream.tap:stream:nondurabletest.0", moduleInputChannel);
assertNull(admin.getQueueProperties("xdbustest.teststream.tap:stream:nondurabletest.0.dlq"));
admin.deleteQueue("xdbustest.teststream.tap:stream:nondurabletest.0");
admin.deleteExchange("xdbustest.topic.tap:stream:nondurabletest.0");
}
@Test
public void testAutoBindDLQ() throws Exception {
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("prefix", "xdbustest.");
properties.put("autoBindDLQ", "true");
properties.put("maxAttempts", "1"); // disable retry
properties.put("requeue", "false");
DirectChannel moduleInputChannel = new DirectChannel();
moduleInputChannel.setBeanName("dlqTest");
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
throw new RuntimeException("foo");
}
});
bus.bindConsumer("dlqtest", moduleInputChannel, properties);
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
template.convertAndSend("", "xdbustest.dlqtest", "foo");
int n = 0;
while (n++ < 100) {
Object deadLetter = template.receiveAndConvert("xdbustest.dlqtest.dlq");
if (deadLetter != null) {
assertEquals("foo", deadLetter);
break;
}
Thread.sleep(100);
}
assertTrue(n < 100);
bus.unbindConsumer("dlqtest", moduleInputChannel);
admin.deleteQueue("xdbustest.dlqtest.dlq");
admin.deleteQueue("xdbustest.dlqtest");
admin.deleteExchange("xdbustest.DLX");
}
@Test
public void testAutoBindDLQPartioned() throws Exception {
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("prefix", "xdbustest.");
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");
bus.bindConsumer("partDLQ.0", input0, properties);
properties.put("partitionIndex", "1");
DirectChannel input1 = new DirectChannel();
input1.setBeanName("test.input1DLQ");
bus.bindConsumer("partDLQ.0", input1, properties);
properties.clear();
properties.put("prefix", "xdbustest.");
properties.put("partitionKeyExtractorClass", "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
properties.put("partitionSelectorClass", "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
properties.put(BusProperties.NEXT_MODULE_COUNT, "2");
DirectChannel output = new DirectChannel();
output.setBeanName("test.output");
bus.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 = "xdbustest.partDLQ.0.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"));
admin.deleteQueue(streamDLQName);
admin.deleteQueue("xdbustest.partDLQ.0-0");
admin.deleteQueue("xdbustest.partDLQ.0-1");
admin.deleteExchange("xdbustest.DLX");
}
@Test
public void testAutoBindDLQwithRepublish() throws Exception {
// pre-declare the queue with dead-lettering, users can also use a policy
RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "xdbustest.DLX");
Queue queue = new Queue("xdbustest.dlqpubtest", true, false, false, args);
admin.declareQueue(queue);
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("prefix", "xdbustest.");
properties.put("autoBindDLQ", "true");
properties.put("republishToDLQ", "true");
properties.put("maxAttempts", "1"); // disable retry
properties.put("requeue", "false");
DirectChannel moduleInputChannel = new DirectChannel();
moduleInputChannel.setBeanName("dlqPubTest");
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
throw new RuntimeException("foo");
}
});
bus.bindConsumer("dlqpubtest", moduleInputChannel, properties);
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
template.convertAndSend("", "xdbustest.dlqpubtest", "foo");
int n = 0;
while (n++ < 100) {
org.springframework.amqp.core.Message deadLetter = template.receive("xdbustest.dlqpubtest.dlq");
if (deadLetter != null) {
assertEquals("foo", new String(deadLetter.getBody()));
assertNotNull(deadLetter.getMessageProperties().getHeaders().get("x-exception-stacktrace"));
break;
}
Thread.sleep(100);
}
assertTrue(n < 100);
bus.unbindConsumer("dlqpubtest", moduleInputChannel);
admin.deleteQueue("xdbustest.dlqpubtest.dlq");
admin.deleteQueue("xdbustest.dlqpubtest");
admin.deleteExchange("xdbustest.DLX");
}
@SuppressWarnings("unchecked")
@Test
public void testBatchingAndCompression() throws Exception {
RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("deliveryMode", "NON_PERSISTENT");
properties.put("batchingEnabled", "true");
properties.put("batchSize", "2");
properties.put("batchBufferLimit", "100000");
properties.put("batchTimeout", "30000");
properties.put("compress", "true");
DirectChannel output = new DirectChannel();
output.setBeanName("batchingProducer");
bus.bindProducer("batching.0", output, properties);
while (template.receive("xdbus.batching.0") != null) {
}
Log logger = spy(TestUtils.getPropertyValue(bus, "messageBus.compressingPostProcessor.logger", Log.class));
new DirectFieldAccessor(TestUtils.getPropertyValue(bus, "messageBus.compressingPostProcessor"))
.setPropertyValue("logger", logger);
when(logger.isTraceEnabled()).thenReturn(true);
assertEquals(Deflater.BEST_SPEED, TestUtils.getPropertyValue(bus, "messageBus.compressingPostProcessor.level"));
output.send(new GenericMessage<>("foo".getBytes()));
output.send(new GenericMessage<>("bar".getBytes()));
Object out = spyOn("batching.0").receive(false);
assertThat(out, instanceOf(byte[].class));
assertEquals("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", new String((byte[]) out));
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
verify(logger).trace(captor.capture());
assertThat(captor.getValue().toString(), containsString("Compressed 14 to "));
QueueChannel input = new QueueChannel();
input.setBeanName("batchingConsumer");
bus.bindConsumer("batching.0", input, null);
output.send(new GenericMessage<>("foo".getBytes()));
output.send(new GenericMessage<>("bar".getBytes()));
Message<byte[]> in = (Message<byte[]>) input.receive(10000);
assertNotNull(in);
assertEquals("foo", new String(in.getPayload()));
in = (Message<byte[]>) input.receive(10000);
assertNotNull(in);
assertEquals("bar", new String(in.getPayload()));
assertNull(in.getHeaders().get(AmqpHeaders.DELIVERY_MODE));
bus.unbindProducers("batching.0");
bus.unbindConsumers("batching.0");
}
private SimpleMessageListenerContainer verifyContainer(AbstractEndpoint endpoint) {
SimpleMessageListenerContainer container;
Advice retry;
container = TestUtils.getPropertyValue(endpoint, "messageListenerContainer",
SimpleMessageListenerContainer.class);
assertEquals(AcknowledgeMode.NONE, container.getAcknowledgeMode());
assertThat(container.getQueueNames()[0], startsWith("foo.props.0"));
assertFalse(TestUtils.getPropertyValue(container, "transactional", Boolean.class));
assertEquals(2, TestUtils.getPropertyValue(container, "concurrentConsumers"));
assertEquals(3, TestUtils.getPropertyValue(container, "maxConcurrentConsumers"));
assertFalse(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class));
assertEquals(20, TestUtils.getPropertyValue(container, "prefetchCount"));
assertEquals(10, TestUtils.getPropertyValue(container, "txSize"));
retry = TestUtils.getPropertyValue(container, "adviceChain", Advice[].class)[0];
assertEquals(23, TestUtils.getPropertyValue(retry, "retryOperations.retryPolicy.maxAttempts"));
assertEquals(2000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.initialInterval"));
assertEquals(20000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.maxInterval"));
assertEquals(5.0, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.multiplier"));
List<?> requestMatchers = TestUtils.getPropertyValue(endpoint,
"headerMapper.requestHeaderMatcher.strategies",
List.class);
assertEquals(1, requestMatchers.size());
assertEquals("foo",
TestUtils.getPropertyValue(requestMatchers.get(0), "patterns", Collection.class).iterator().next());
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",
List.class);
assertEquals(1, requestMatchers.size());
assertEquals("foo",
TestUtils.getPropertyValue(requestMatchers.get(0), "patterns", Collection.class).iterator().next());
}
@Override
protected String getEndpointRouting(AbstractEndpoint endpoint) {
return TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression", SpelExpression.class).getExpressionString();
}
@Override
protected String getPubSubEndpointRouting(AbstractEndpoint endpoint) {
return TestUtils.getPropertyValue(endpoint, "handler.delegate.exchangeNameExpression", SpelExpression.class).getExpressionString();
}
@Override
public Spy spyOn(final String queue) {
final RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
template.setAfterReceivePostProcessors(new DelegatingDecompressingPostProcessor());
return new Spy() {
@Override
public Object receive(boolean expectNull) throws Exception {
if (expectNull) {
Thread.sleep(50);
return template.receiveAndConvert("xdbus." + queue);
}
Object bar = null;
int n = 0;
while (n++ < 100 && bar == null) {
bar = template.receiveAndConvert("xdbus." + queue);
Thread.sleep(100);
}
assertTrue("Message did not arrive in RabbitMQ", n < 100);
return bar;
}
};
}
}