org.springframework.amqp.rabbit.core.RabbitTemplateIntegrationTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.amqp.rabbit.core.RabbitTemplateIntegrationTests.java

Source

/*
 * Copyright 2010-2014 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.amqp.rabbit.core;

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 static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Address;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.ReceiveAndReplyCallback;
import org.springframework.amqp.core.ReceiveAndReplyMessageCallback;
import org.springframework.amqp.core.ReplyToAddressCallback;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.SingleConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.amqp.rabbit.test.BrokerRunning;
import org.springframework.amqp.rabbit.test.BrokerTestUtils;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.amqp.utils.SerializationUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.GetResponse;

/**
 * @author Dave Syer
 * @author Mark Fisher
 * @author Tomas Lukosius
 * @author Gary Russell
 * @author Gunnar Hillert
 * @author Artem Bilan
 */
public class RabbitTemplateIntegrationTests {

    private static final String ROUTE = "test.queue";

    private static final Queue REPLY_QUEUE = new Queue("test.reply.queue");

    private RabbitTemplate template;

    @Before
    public void create() {
        final CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(BrokerTestUtils.getPort());
        template = new RabbitTemplate(connectionFactory);
    }

    @After
    public void cleanup() throws Exception {
        ((DisposableBean) template.getConnectionFactory()).destroy();
    }

    @Rule
    public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(ROUTE, REPLY_QUEUE.getName());

    @Test
    public void testSendToNonExistentAndThenReceive() throws Exception {
        // If transacted then the commit fails on send, so we get a nice synchronous exception
        template.setChannelTransacted(true);
        try {
            template.convertAndSend("", "no.such.route", "message");
            // fail("Expected AmqpException");
        } catch (AmqpException e) {
            // e.printStackTrace();
        }
        // Now send the real message, and all should be well...
        template.convertAndSend(ROUTE, "message");
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendAndReceiveWithPostProcessor() throws Exception {
        template.convertAndSend(ROUTE, (Object) "message", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setContentType("text/other");
                // message.getMessageProperties().setUserId("foo");
                return message;
            }
        });
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendAndReceive() throws Exception {
        template.convertAndSend(ROUTE, "message");
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendAndReceiveTransacted() throws Exception {
        template.setChannelTransacted(true);
        template.convertAndSend(ROUTE, "message");
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendAndReceiveTransactedWithUncachedConnection() throws Exception {
        final SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory("localhost");
        RabbitTemplate template = new RabbitTemplate(singleConnectionFactory);
        template.setChannelTransacted(true);
        template.convertAndSend(ROUTE, "message");
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
        singleConnectionFactory.destroy();
    }

    @Test
    public void testSendAndReceiveTransactedWithImplicitRollback() throws Exception {
        template.setChannelTransacted(true);
        template.convertAndSend(ROUTE, "message");
        // Rollback of manual receive is implicit because the channel is
        // closed...
        try {
            template.execute(new ChannelCallback<String>() {
                @Override
                public String doInRabbit(Channel channel) throws Exception {
                    // Switch off the auto-ack so the message is rolled back...
                    channel.basicGet(ROUTE, false);
                    // This is the way to rollback with a cached channel (it is
                    // the way the ConnectionFactoryUtils
                    // handles it via a synchronization):
                    channel.basicRecover(true);
                    throw new PlannedException();
                }
            });
            fail("Expected PlannedException");
        } catch (Exception e) {
            assertTrue(e.getCause() instanceof PlannedException);
        }
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendAndReceiveInCallback() throws Exception {
        template.convertAndSend(ROUTE, "message");
        final MessagePropertiesConverter messagePropertiesConverter = new DefaultMessagePropertiesConverter();
        String result = template.execute(new ChannelCallback<String>() {

            @Override
            public String doInRabbit(Channel channel) throws Exception {
                // We need noAck=false here for the message to be expicitly
                // acked
                GetResponse response = channel.basicGet(ROUTE, false);
                MessageProperties messageProps = messagePropertiesConverter.toMessageProperties(response.getProps(),
                        response.getEnvelope(), "UTF-8");
                // Explicit ack
                channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
                return (String) new SimpleMessageConverter()
                        .fromMessage(new Message(response.getBody(), messageProps));
            }
        });
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testReceiveInExternalTransaction() throws Exception {
        template.convertAndSend(ROUTE, "message");
        template.setChannelTransacted(true);
        String result = new TransactionTemplate(new TestTransactionManager())
                .execute(new TransactionCallback<String>() {
                    @Override
                    public String doInTransaction(TransactionStatus status) {
                        return (String) template.receiveAndConvert(ROUTE);
                    }
                });
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testReceiveInExternalTransactionAutoAck() throws Exception {
        template.convertAndSend(ROUTE, "message");
        // Should just result in auto-ack (not synched with external tx)
        template.setChannelTransacted(true);
        String result = new TransactionTemplate(new TestTransactionManager())
                .execute(new TransactionCallback<String>() {
                    @Override
                    public String doInTransaction(TransactionStatus status) {
                        return (String) template.receiveAndConvert(ROUTE);
                    }
                });
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testReceiveInExternalTransactionWithRollback() throws Exception {
        // Makes receive (and send in principle) transactional
        template.setChannelTransacted(true);
        template.convertAndSend(ROUTE, "message");
        try {
            new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallback<String>() {
                @Override
                public String doInTransaction(TransactionStatus status) {
                    template.receiveAndConvert(ROUTE);
                    throw new PlannedException();
                }
            });
            fail("Expected PlannedException");
        } catch (PlannedException e) {
            // Expected
        }
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testReceiveInExternalTransactionWithNoRollback() throws Exception {
        // Makes receive non-transactional
        template.setChannelTransacted(false);
        template.convertAndSend(ROUTE, "message");
        try {
            new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallback<String>() {
                @Override
                public String doInTransaction(TransactionStatus status) {
                    template.receiveAndConvert(ROUTE);
                    throw new PlannedException();
                }
            });
            fail("Expected PlannedException");
        } catch (PlannedException e) {
            // Expected
        }
        // No rollback
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendInExternalTransaction() throws Exception {
        template.setChannelTransacted(true);
        new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                template.convertAndSend(ROUTE, "message");
                return null;
            }
        });
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals("message", result);
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testSendInExternalTransactionWithRollback() throws Exception {
        template.setChannelTransacted(true);
        try {
            new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallback<Void>() {
                @Override
                public Void doInTransaction(TransactionStatus status) {
                    template.convertAndSend(ROUTE, "message");
                    throw new PlannedException();
                }
            });
            fail("Expected PlannedException");
        } catch (PlannedException e) {
            // Expected
        }
        String result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testAtomicSendAndReceive() throws Exception {
        final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setHost("localhost");
        final RabbitTemplate template = new RabbitTemplate(cachingConnectionFactory);
        template.setRoutingKey(ROUTE);
        template.setQueue(ROUTE);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<Message> received = executor.submit(new Callable<Message>() {

            @Override
            public Message call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive();
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return message;
            }

        });
        Message message = new Message("test-message".getBytes(), new MessageProperties());
        Message reply = template.sendAndReceive(message);
        assertEquals(new String(message.getBody()),
                new String(received.get(1000, TimeUnit.MILLISECONDS).getBody()));
        assertNotNull("Reply is expected", reply);
        assertEquals(new String(message.getBody()), new String(reply.getBody()));
        // Message was consumed so nothing left on queue
        reply = template.receive();
        assertEquals(null, reply);
        cachingConnectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveExternalExecutor() throws Exception {
        final CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost("localhost");
        ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
        final String execName = "make-sure-exec-passed-in";
        exec.setBeanName(execName);
        exec.afterPropertiesSet();
        connectionFactory.setExecutor(exec);
        final Field[] fields = new Field[1];
        ReflectionUtils.doWithFields(RabbitTemplate.class, new FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                field.setAccessible(true);
                fields[0] = field;
            }
        }, new FieldFilter() {
            @Override
            public boolean matches(Field field) {
                return field.getName().equals("logger");
            }
        });
        Log logger = Mockito.mock(Log.class);
        when(logger.isTraceEnabled()).thenReturn(true);

        final AtomicBoolean execConfiguredOk = new AtomicBoolean();

        doAnswer(new Answer<Object>() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                String log = (String) invocation.getArguments()[0];
                if (log.startsWith("Message received") && Thread.currentThread().getName().startsWith(execName)) {
                    execConfiguredOk.set(true);
                }
                return null;
            }
        }).when(logger).trace(Mockito.anyString());
        final RabbitTemplate template = new RabbitTemplate(connectionFactory);
        ReflectionUtils.setField(fields[0], template, logger);
        template.setRoutingKey(ROUTE);
        template.setQueue(ROUTE);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<Message> received = executor.submit(new Callable<Message>() {

            @Override
            public Message call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive();
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return message;
            }

        });
        Message message = new Message("test-message".getBytes(), new MessageProperties());
        Message reply = template.sendAndReceive(message);
        assertEquals(new String(message.getBody()),
                new String(received.get(1000, TimeUnit.MILLISECONDS).getBody()));
        assertNotNull("Reply is expected", reply);
        assertEquals(new String(message.getBody()), new String(reply.getBody()));
        // Message was consumed so nothing left on queue
        reply = template.receive();
        assertEquals(null, reply);

        assertTrue(execConfiguredOk.get());
        connectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveWithRoutingKey() throws Exception {
        final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setHost("localhost");
        final RabbitTemplate template = new RabbitTemplate(cachingConnectionFactory);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<Message> received = executor.submit(new Callable<Message>() {

            @Override
            public Message call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return message;
            }

        });
        Message message = new Message("test-message".getBytes(), new MessageProperties());
        Message reply = template.sendAndReceive(ROUTE, message);
        assertEquals(new String(message.getBody()),
                new String(received.get(1000, TimeUnit.MILLISECONDS).getBody()));
        assertNotNull("Reply is expected", reply);
        assertEquals(new String(message.getBody()), new String(reply.getBody()));
        // Message was consumed so nothing left on queue
        reply = template.receive(ROUTE);
        assertEquals(null, reply);
        cachingConnectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveWithExchangeAndRoutingKey() throws Exception {
        final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setHost("localhost");
        final RabbitTemplate template = new RabbitTemplate(cachingConnectionFactory);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<Message> received = executor.submit(new Callable<Message>() {

            @Override
            public Message call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return message;
            }

        });
        Message message = new Message("test-message".getBytes(), new MessageProperties());
        Message reply = template.sendAndReceive("", ROUTE, message);
        assertEquals(new String(message.getBody()),
                new String(received.get(1000, TimeUnit.MILLISECONDS).getBody()));
        assertNotNull("Reply is expected", reply);
        assertEquals(new String(message.getBody()), new String(reply.getBody()));
        // Message was consumed so nothing left on queue
        reply = template.receive(ROUTE);
        assertEquals(null, reply);
        cachingConnectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveWithConversion() throws Exception {
        final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setHost("localhost");
        final RabbitTemplate template = new RabbitTemplate(cachingConnectionFactory);
        template.setRoutingKey(ROUTE);
        template.setQueue(ROUTE);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive();
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive("message");
        assertEquals("message", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("message", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert();
        assertEquals(null, result);
        cachingConnectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveWithConversionUsingRoutingKey() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive(ROUTE, "message");
        assertEquals("message", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("message", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testAtomicSendAndReceiveWithConversionUsingExchangeAndRoutingKey() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive("", ROUTE, "message");
        assertEquals("message", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("message", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testAtomicSendAndReceiveWithConversionAndMessagePostProcessor() throws Exception {
        final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setHost("localhost");
        final RabbitTemplate template = new RabbitTemplate(cachingConnectionFactory);
        template.setRoutingKey(ROUTE);
        template.setQueue(ROUTE);
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive();
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive((Object) "message", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                try {
                    byte[] newBody = new String(message.getBody(), "UTF-8").toUpperCase().getBytes("UTF-8");
                    return new Message(newBody, message.getMessageProperties());
                } catch (Exception e) {
                    throw new AmqpException("unexpected failure in test", e);
                }
            }
        });
        assertEquals("MESSAGE", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("MESSAGE", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert();
        assertEquals(null, result);
        cachingConnectionFactory.destroy();
    }

    @Test
    public void testAtomicSendAndReceiveWithConversionAndMessagePostProcessorUsingRoutingKey() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive(ROUTE, (Object) "message",
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        try {
                            byte[] newBody = new String(message.getBody(), "UTF-8").toUpperCase().getBytes("UTF-8");
                            return new Message(newBody, message.getMessageProperties());
                        } catch (Exception e) {
                            throw new AmqpException("unexpected failure in test", e);
                        }
                    }
                });
        assertEquals("MESSAGE", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("MESSAGE", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testAtomicSendAndReceiveWithConversionAndMessagePostProcessorUsingExchangeAndRoutingKey()
            throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // Set up a consumer to respond to our producer
        Future<String> received = executor.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Message message = null;
                for (int i = 0; i < 10; i++) {
                    message = template.receive(ROUTE);
                    if (message != null) {
                        break;
                    }
                    Thread.sleep(100L);
                }
                assertNotNull("No message received", message);
                template.send(message.getMessageProperties().getReplyTo(), message);
                return (String) template.getMessageConverter().fromMessage(message);
            }

        });
        String result = (String) template.convertSendAndReceive("", ROUTE, "message", new MessagePostProcessor() {

            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                try {
                    byte[] newBody = new String(message.getBody(), "UTF-8").toUpperCase().getBytes("UTF-8");
                    return new Message(newBody, message.getMessageProperties());
                } catch (Exception e) {
                    throw new AmqpException("unexpected failure in test", e);
                }
            }
        });
        assertEquals("MESSAGE", received.get(1000, TimeUnit.MILLISECONDS));
        assertEquals("MESSAGE", result);
        // Message was consumed so nothing left on queue
        result = (String) template.receiveAndConvert(ROUTE);
        assertEquals(null, result);
    }

    @Test
    public void testReceiveAndReplyNonStandardCorrelationNotBytes() {
        this.template.setQueue(ROUTE);
        this.template.setRoutingKey(ROUTE);
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.getHeaders().put("baz", "bar");
        Message message = new Message("foo".getBytes(), messageProperties);
        this.template.send(ROUTE, message);

        this.template.setCorrelationKey("baz");
        boolean received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

            @Override
            public Message handle(Message message) {
                return new Message("fuz".getBytes(), new MessageProperties());
            }
        });
        assertTrue(received);
        message = this.template.receive();
        assertNotNull(message);
        assertEquals("bar", message.getMessageProperties().getHeaders().get("baz"));
    }

    @Test
    public void testReceiveAndReply() {
        this.template.setQueue(ROUTE);
        this.template.setRoutingKey(ROUTE);
        this.template.convertAndSend(ROUTE, "test");

        boolean received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

            @Override
            public Message handle(Message message) {
                message.getMessageProperties().setHeader("foo", "bar");
                return message;
            }
        });
        assertTrue(received);

        Message receive = this.template.receive();
        assertEquals("bar", receive.getMessageProperties().getHeaders().get("foo"));

        this.template.convertAndSend(ROUTE, 1);

        received = this.template.receiveAndReply(ROUTE, new ReceiveAndReplyCallback<Integer, Integer>() {

            @Override
            public Integer handle(Integer payload) {
                return payload + 1;
            }
        });
        assertTrue(received);

        Object result = this.template.receiveAndConvert(ROUTE);
        assertTrue(result instanceof Integer);
        assertEquals(2, result);

        this.template.convertAndSend(ROUTE, 2);

        received = this.template.receiveAndReply(ROUTE, new ReceiveAndReplyCallback<Integer, Integer>() {

            @Override
            public Integer handle(Integer payload) {
                return payload * 2;
            }
        }, "", ROUTE);
        assertTrue(received);

        result = this.template.receiveAndConvert(ROUTE);
        assertTrue(result instanceof Integer);
        assertEquals(4, result);

        received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

            @Override
            public Message handle(Message message) {
                return message;
            }
        });
        assertFalse(received);

        this.template.convertAndSend(ROUTE, "test");
        received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

            @Override
            public Message handle(Message message) {
                return null;
            }
        });
        assertTrue(received);

        result = this.template.receive();
        assertNull(result);

        this.template.convertAndSend(ROUTE, "TEST");
        received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

            @Override
            public Message handle(Message message) {
                MessageProperties messageProperties = new MessageProperties();
                messageProperties.setContentType(message.getMessageProperties().getContentType());
                messageProperties.setHeader("testReplyTo", new Address("", "", ROUTE));
                return new Message(message.getBody(), messageProperties);
            }

        }, new ReplyToAddressCallback<Message>() {

            @Override
            public Address getReplyToAddress(Message request, Message reply) {
                return (Address) reply.getMessageProperties().getHeaders().get("testReplyTo");
            }

        });
        assertTrue(received);
        result = this.template.receiveAndConvert(ROUTE);
        assertEquals("TEST", result);

        assertEquals(null, template.receive(ROUTE));

        template.setChannelTransacted(true);

        this.template.convertAndSend(ROUTE, "TEST");
        result = new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallback<String>() {
            @Override
            public String doInTransaction(TransactionStatus status) {
                final AtomicReference<String> payloadReference = new AtomicReference<String>();
                boolean received = template.receiveAndReply(new ReceiveAndReplyCallback<String, Void>() {

                    @Override
                    public Void handle(String payload) {
                        payloadReference.set(payload);
                        return null;
                    }
                });
                assertTrue(received);
                return payloadReference.get();
            }
        });
        assertEquals("TEST", result);
        assertEquals(null, template.receive(ROUTE));

        this.template.convertAndSend(ROUTE, "TEST");
        try {
            new TransactionTemplate(new TestTransactionManager()).execute(new TransactionCallbackWithoutResult() {

                @Override
                public void doInTransactionWithoutResult(TransactionStatus status) {
                    template.receiveAndReply(new ReceiveAndReplyMessageCallback() {

                        @Override
                        public Message handle(Message message) {
                            return message;
                        }
                    }, new ReplyToAddressCallback<Message>() {

                        @Override
                        public Address getReplyToAddress(Message request, Message reply) {
                            throw new PlannedException();
                        }
                    });
                }
            });
            fail("Expected PlannedException");
        } catch (Exception e) {
            assertTrue(e.getCause() instanceof PlannedException);
        }

        assertEquals("TEST", template.receiveAndConvert(ROUTE));
        assertEquals(null, template.receive(ROUTE));

        template.convertAndSend("test");

        try {
            this.template.receiveAndReply(new ReceiveAndReplyCallback<Double, Void>() {

                @Override
                public Void handle(Double message) {
                    return null;
                }
            });
            fail("IllegalArgumentException expected");
        } catch (Exception e) {
            assertTrue(e.getCause() instanceof IllegalArgumentException);
            assertTrue(e.getCause().getCause() instanceof ClassCastException);
        }

    }

    @Test
    public void testSymmetricalReceiveAndReply() throws InterruptedException, UnsupportedEncodingException {
        this.template.setQueue(ROUTE);
        this.template.setRoutingKey(ROUTE);
        this.template.setReplyQueue(REPLY_QUEUE);
        this.template.setReplyTimeout(10000);

        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(this.template.getConnectionFactory());
        container.setQueues(REPLY_QUEUE);
        container.setMessageListener(this.template);
        container.start();

        int count = 10;

        final Map<Double, Object> results = new HashMap<Double, Object>();

        ExecutorService executor = Executors.newFixedThreadPool(10);

        this.template.setCorrelationKey("CorrelationKey");

        for (int i = 0; i < count; i++) {
            executor.execute(new Runnable() {

                @Override
                public void run() {
                    Double request = Math.random() * 100;
                    Object reply = template.convertSendAndReceive(request);
                    results.put(request, reply);
                }
            });
        }

        for (int i = 0; i < count; i++) {
            executor.execute(new Runnable() {

                @Override
                public void run() {
                    Double request = Math.random() * 100;
                    MessageProperties messageProperties = new MessageProperties();
                    messageProperties.setContentType(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT);
                    Message reply = template
                            .sendAndReceive(new Message(SerializationUtils.serialize(request), messageProperties));
                    results.put(request, SerializationUtils.deserialize(reply.getBody()));
                }
            });
        }

        final AtomicInteger receiveCount = new AtomicInteger();

        long start = System.currentTimeMillis();
        do {
            template.receiveAndReply(new ReceiveAndReplyCallback<Double, Double>() {

                @Override
                public Double handle(Double payload) {
                    receiveCount.incrementAndGet();
                    return payload * 3;
                }
            });
            if (System.currentTimeMillis() > start + 10000) {
                fail("Something wrong with RabbitMQ");
            }
        } while (receiveCount.get() < count * 2);

        executor.shutdown();
        assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
        container.stop();

        assertEquals(count * 2, results.size());

        for (Map.Entry<Double, Object> entry : results.entrySet()) {
            assertEquals(entry.getKey() * 3, entry.getValue());
        }

        String messageId = UUID.randomUUID().toString();

        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setMessageId(messageId);
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        messageProperties.setReplyTo(REPLY_QUEUE.getName());

        template.send(new Message("test".getBytes(), messageProperties));

        template.receiveAndReply(new ReceiveAndReplyCallback<String, String>() {

            @Override
            public String handle(String payload) {
                return payload.toUpperCase();
            }
        });

        Message result = this.template.receive(REPLY_QUEUE.getName());
        assertEquals("TEST", new String(result.getBody()));
        assertEquals(messageId, new String(result.getMessageProperties().getCorrelationId()));
    }

    @SuppressWarnings("serial")
    private class PlannedException extends RuntimeException {
        public PlannedException() {
            super("Planned");
        }
    }

    @SuppressWarnings("serial")
    private class TestTransactionManager extends AbstractPlatformTransactionManager {

        @Override
        protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
        }

        @Override
        protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
        }

        @Override
        protected Object doGetTransaction() throws TransactionException {
            return new Object();
        }

        @Override
        protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
        }

    }

}