diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/Connection.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/Connection.java index 33365112b5..98139e7947 100755 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/Connection.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/Connection.java @@ -1,42 +1,42 @@ -/** - * - */ -package org.springframework.amqp.rabbit.connection; - -import org.springframework.amqp.AmqpException; - -import com.rabbitmq.client.Channel; - -/** - * @author Dave Syer - * - */ -public interface Connection { - - /** - * Create a new channel, using an internally allocated channel number. - * @param transactional true if the channel should support transactions - * @return a new channel descriptor, or null if none is available - * @throws AmqpException if an I/O problem is encountered - */ - Channel createChannel(boolean transactional) throws AmqpException; - - /** - * Close this connection and all its channels - * with the {@link com.rabbitmq.client.AMQP#REPLY_SUCCESS} close code - * and message 'OK'. - * - * Waits for all the close operations to complete. - * - * @throws AmqpException if an I/O problem is encountered - */ - void close() throws AmqpException; - - /** - * Flag to indicate the status of the connection. - * - * @return true if the connection is open - */ - boolean isOpen(); - -} +/** + * + */ +package org.springframework.amqp.rabbit.connection; + +import org.springframework.amqp.AmqpException; + +import com.rabbitmq.client.Channel; + +/** + * @author Dave Syer + * + */ +public interface Connection { + + /** + * Create a new channel, using an internally allocated channel number. + * @param transactional true if the channel should support transactions + * @return a new channel descriptor, or null if none is available + * @throws AmqpException if an I/O problem is encountered + */ + Channel createChannel(boolean transactional) throws AmqpException; + + /** + * Close this connection and all its channels + * with the {@link com.rabbitmq.client.AMQP#REPLY_SUCCESS} close code + * and message 'OK'. + * + * Waits for all the close operations to complete. + * + * @throws AmqpException if an I/O problem is encountered + */ + void close() throws AmqpException; + + /** + * Flag to indicate the status of the connection. + * + * @return true if the connection is open + */ + boolean isOpen(); + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/SimpleConnection.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/SimpleConnection.java index 6dfc999666..a794817323 100755 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/SimpleConnection.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/SimpleConnection.java @@ -1,52 +1,52 @@ -/* - * Copyright 2002-2010 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.connection; - -import java.io.IOException; - -import com.rabbitmq.client.Channel; - -public class SimpleConnection implements Connection { - - private final com.rabbitmq.client.Connection delegate; - - public SimpleConnection(com.rabbitmq.client.Connection delegate) { - this.delegate = delegate; - } - - public Channel createChannel(boolean transactional) { - try { - Channel channel = delegate.createChannel(); - if (transactional) { - // Just created so we want to start the transaction - channel.txSelect(); - } - return channel; - } catch (IOException e) { - throw RabbitUtils.convertRabbitAccessException(e); - } - } - - public void close() { - try { - delegate.close(); - } catch (IOException e) { - throw RabbitUtils.convertRabbitAccessException(e); - } - } - - public boolean isOpen() { - return delegate != null && delegate.isOpen(); - } - -} +/* + * Copyright 2002-2010 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.connection; + +import java.io.IOException; + +import com.rabbitmq.client.Channel; + +public class SimpleConnection implements Connection { + + private final com.rabbitmq.client.Connection delegate; + + public SimpleConnection(com.rabbitmq.client.Connection delegate) { + this.delegate = delegate; + } + + public Channel createChannel(boolean transactional) { + try { + Channel channel = delegate.createChannel(); + if (transactional) { + // Just created so we want to start the transaction + channel.txSelect(); + } + return channel; + } catch (IOException e) { + throw RabbitUtils.convertRabbitAccessException(e); + } + } + + public void close() { + try { + delegate.close(); + } catch (IOException e) { + throw RabbitUtils.convertRabbitAccessException(e); + } + } + + public boolean isOpen() { + return delegate != null && delegate.isOpen(); + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/BlockingQueueConsumerIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/BlockingQueueConsumerIntegrationTests.java index 36472d317b..bcebed9097 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/BlockingQueueConsumerIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/BlockingQueueConsumerIntegrationTests.java @@ -1,50 +1,50 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNull; - -import org.apache.log4j.Level; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; - -public class BlockingQueueConsumerIntegrationTests { - - private static Queue queue = new Queue("test.queue"); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, - BlockingQueueConsumerIntegrationTests.class); - - @Test - public void testTransactionalLowLevel() throws Exception { - - RabbitTemplate template = new RabbitTemplate(); - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - - BlockingQueueConsumer blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory, - new DefaultMessagePropertiesConverter(), new ActiveObjectCounter(), - AcknowledgeMode.AUTO, true, 1, queue.getName()); - blockingQueueConsumer.start(); - connectionFactory.destroy(); - - // TODO: make this into a proper assertion. An exception can be thrown here by the Rabbit client and printed to - // stderr without being rethrown (so hard to make a test fail). - blockingQueueConsumer.stop(); - assertNull(template.receiveAndConvert(queue.getName())); - - } - -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNull; + +import org.apache.log4j.Level; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; + +public class BlockingQueueConsumerIntegrationTests { + + private static Queue queue = new Queue("test.queue"); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, + BlockingQueueConsumerIntegrationTests.class); + + @Test + public void testTransactionalLowLevel() throws Exception { + + RabbitTemplate template = new RabbitTemplate(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + + BlockingQueueConsumer blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory, + new DefaultMessagePropertiesConverter(), new ActiveObjectCounter(), + AcknowledgeMode.AUTO, true, 1, queue.getName()); + blockingQueueConsumer.start(); + connectionFactory.destroy(); + + // TODO: make this into a proper assertion. An exception can be thrown here by the Rabbit client and printed to + // stderr without being rethrown (so hard to make a test fail). + blockingQueueConsumer.stop(); + assertNull(template.receiveAndConvert(queue.getName())); + + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerBrokerInterruptionIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerBrokerInterruptionIntegrationTests.java index eca01ed061..2a81e16952 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerBrokerInterruptionIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerBrokerInterruptionIntegrationTests.java @@ -1,178 +1,178 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.admin.QueueInfo; -import org.springframework.amqp.rabbit.admin.RabbitBrokerAdmin; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerPanic; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.EnvironmentAvailable; - -import com.rabbitmq.client.Channel; - -public class MessageListenerBrokerInterruptionIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerBrokerInterruptionIntegrationTests.class); - - // Ensure queue is durable, or it won't survive the broker restart - private Queue queue = new Queue("test.queue", true); - - private int concurrentConsumers = 2; - - private int messageCount = 60; - - private int txSize = 1; - - private boolean transactional = false; - - private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; - - private SimpleMessageListenerContainer container; - - @Rule - public static EnvironmentAvailable environment = new EnvironmentAvailable("BROKER_INTEGRATION_TEST"); - - /* - * Ensure broker dies if a test fails (otherwise the erl process might have to be killed manually) - */ - @Rule - public static BrokerPanic panic = new BrokerPanic(); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - private ConnectionFactory connectionFactory; - - private RabbitBrokerAdmin brokerAdmin; - - public MessageListenerBrokerInterruptionIntegrationTests() throws Exception { - FileUtils.deleteDirectory(new File("target/rabbitmq")); - brokerIsRunning.setPort(BrokerTestUtils.getAdminPort()); - logger.debug("Setting up broker"); - brokerAdmin = BrokerTestUtils.getRabbitBrokerAdmin(); - panic.setBrokerAdmin(brokerAdmin); - if (environment.isActive()) { - brokerAdmin.startNode(); - } - } - - @Before - public void createConnectionFactory() { - if (environment.isActive()) { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getAdminPort()); - this.connectionFactory = connectionFactory; - } - } - - @After - public void clear() throws Exception { - if (environment.isActive()) { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.debug("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - brokerAdmin.stopNode(); - // Remove all trace of the durable queue... - FileUtils.deleteDirectory(new File("target/rabbitmq")); - } - } - - @Test - public void testListenerRecoversFromDeadBroker() throws Exception { - - List queues = brokerAdmin.getQueues(); - logger.info("Queues: " + queues); - assertEquals(1, queues.size()); - assertTrue(queues.get(0).isDurable()); - - RabbitTemplate template = new RabbitTemplate(connectionFactory); - - CountDownLatch latch = new CountDownLatch(messageCount); - assertEquals("No more messages to receive before even sent!", messageCount, latch.getCount()); - container = createContainer(queue.getName(), new VanillaListener(latch), connectionFactory); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - assertTrue("No more messages to receive before broker stopped", latch.getCount() > 0); - brokerAdmin.stopBrokerApplication(); - assertTrue("No more messages to receive after broker stopped", latch.getCount() > 0); - boolean waited = latch.await(500, TimeUnit.MILLISECONDS); - assertFalse("Did not time out waiting for message", waited); - - container.stop(); - assertEquals(0, container.getActiveConsumerCount()); - - brokerAdmin.startBrokerApplication(); - queues = brokerAdmin.getQueues(); - logger.info("Queues: " + queues); - container.start(); - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - - int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - private SimpleMessageListenerContainer createContainer(String queueName, Object listener, - ConnectionFactory connectionFactory) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setQueueNames(queueName); - container.setTxSize(txSize); - container.setPrefetchCount(txSize); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(acknowledgeMode); - container.afterPropertiesSet(); - container.start(); - return container; - } - - public static class VanillaListener implements ChannelAwareMessageListener { - - private final CountDownLatch latch; - - public VanillaListener(CountDownLatch latch) { - this.latch = latch; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - logger.debug("Receiving: " + value); - latch.countDown(); - } - } -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.admin.QueueInfo; +import org.springframework.amqp.rabbit.admin.RabbitBrokerAdmin; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerPanic; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.EnvironmentAvailable; + +import com.rabbitmq.client.Channel; + +public class MessageListenerBrokerInterruptionIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerBrokerInterruptionIntegrationTests.class); + + // Ensure queue is durable, or it won't survive the broker restart + private Queue queue = new Queue("test.queue", true); + + private int concurrentConsumers = 2; + + private int messageCount = 60; + + private int txSize = 1; + + private boolean transactional = false; + + private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; + + private SimpleMessageListenerContainer container; + + @Rule + public static EnvironmentAvailable environment = new EnvironmentAvailable("BROKER_INTEGRATION_TEST"); + + /* + * Ensure broker dies if a test fails (otherwise the erl process might have to be killed manually) + */ + @Rule + public static BrokerPanic panic = new BrokerPanic(); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + private ConnectionFactory connectionFactory; + + private RabbitBrokerAdmin brokerAdmin; + + public MessageListenerBrokerInterruptionIntegrationTests() throws Exception { + FileUtils.deleteDirectory(new File("target/rabbitmq")); + brokerIsRunning.setPort(BrokerTestUtils.getAdminPort()); + logger.debug("Setting up broker"); + brokerAdmin = BrokerTestUtils.getRabbitBrokerAdmin(); + panic.setBrokerAdmin(brokerAdmin); + if (environment.isActive()) { + brokerAdmin.startNode(); + } + } + + @Before + public void createConnectionFactory() { + if (environment.isActive()) { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getAdminPort()); + this.connectionFactory = connectionFactory; + } + } + + @After + public void clear() throws Exception { + if (environment.isActive()) { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.debug("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + brokerAdmin.stopNode(); + // Remove all trace of the durable queue... + FileUtils.deleteDirectory(new File("target/rabbitmq")); + } + } + + @Test + public void testListenerRecoversFromDeadBroker() throws Exception { + + List queues = brokerAdmin.getQueues(); + logger.info("Queues: " + queues); + assertEquals(1, queues.size()); + assertTrue(queues.get(0).isDurable()); + + RabbitTemplate template = new RabbitTemplate(connectionFactory); + + CountDownLatch latch = new CountDownLatch(messageCount); + assertEquals("No more messages to receive before even sent!", messageCount, latch.getCount()); + container = createContainer(queue.getName(), new VanillaListener(latch), connectionFactory); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + assertTrue("No more messages to receive before broker stopped", latch.getCount() > 0); + brokerAdmin.stopBrokerApplication(); + assertTrue("No more messages to receive after broker stopped", latch.getCount() > 0); + boolean waited = latch.await(500, TimeUnit.MILLISECONDS); + assertFalse("Did not time out waiting for message", waited); + + container.stop(); + assertEquals(0, container.getActiveConsumerCount()); + + brokerAdmin.startBrokerApplication(); + queues = brokerAdmin.getQueues(); + logger.info("Queues: " + queues); + container.start(); + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + + int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + private SimpleMessageListenerContainer createContainer(String queueName, Object listener, + ConnectionFactory connectionFactory) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setQueueNames(queueName); + container.setTxSize(txSize); + container.setPrefetchCount(txSize); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(acknowledgeMode); + container.afterPropertiesSet(); + container.start(); + return container; + } + + public static class VanillaListener implements ChannelAwareMessageListener { + + private final CountDownLatch latch; + + public VanillaListener(CountDownLatch latch) { + this.latch = latch; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + logger.debug("Receiving: " + value); + latch.countDown(); + } + } +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerErrorHandlerIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerErrorHandlerIntegrationTests.java index ceb940b17b..43b6e0dc31 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerErrorHandlerIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerErrorHandlerIntegrationTests.java @@ -1,240 +1,240 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageListener; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; -import org.springframework.util.ErrorHandler; - -import com.rabbitmq.client.Channel; - -public class MessageListenerContainerErrorHandlerIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerContainerErrorHandlerIntegrationTests.class); - - private static Queue queue = new Queue("test.queue"); - - // Mock error handler - private ErrorHandler errorHandler = mock(ErrorHandler.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, - MessageListenerContainerErrorHandlerIntegrationTests.class); - - @Before - public void setUp() { - reset(errorHandler); - } - - @Test - public void testErrorHandlerInvokeExceptionFromPojo() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new MessageListenerAdapter(new PojoThrowingExceptionListener(latch, - new Exception("Pojo exception")))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - @Test - public void testErrorHandlerInvokeRuntimeExceptionFromPojo() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new MessageListenerAdapter(new PojoThrowingExceptionListener(latch, - new RuntimeException("Pojo runtime exception")))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - @Test - public void testErrorHandlerListenerExecutionFailedExceptionFromListener() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new ThrowingExceptionListener(latch, - new ListenerExecutionFailedException("Listener throws specific runtime exception", null))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - @Test - public void testErrorHandlerRegularRuntimeExceptionFromListener() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new ThrowingExceptionListener(latch, new RuntimeException( - "Listener runtime exception"))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - @Test - public void testErrorHandlerInvokeExceptionFromChannelAwareListener() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new ThrowingExceptionChannelAwareListener(latch, new Exception( - "Channel aware listener exception"))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - @Test - public void testErrorHandlerInvokeRuntimeExceptionFromChannelAwareListener() throws Exception { - int messageCount = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - doTest(messageCount, errorHandler, latch, new ThrowingExceptionChannelAwareListener(latch, - new RuntimeException("Channel aware listener runtime exception"))); - - // Verify that error handler was invoked - verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); - } - - public void doTest(int messageCount, ErrorHandler errorHandler, CountDownLatch latch, Object listener) - throws Exception { - int concurrentConsumers = 1; - RabbitTemplate template = createTemplate(concurrentConsumers); - - // Send messages to the queue - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); - container.setMessageListener(listener); - container.setAcknowledgeMode(AcknowledgeMode.NONE); - container.setChannelTransacted(false); - container.setConcurrentConsumers(concurrentConsumers); - - container.setPrefetchCount(messageCount); - container.setTxSize(messageCount); - container.setQueueNames(queue.getName()); - container.setErrorHandler(errorHandler); - container.afterPropertiesSet(); - container.start(); - - boolean waited = latch.await(500, TimeUnit.MILLISECONDS); - if (messageCount > 1) { - assertTrue("Expected to receive all messages before stop", waited); - } - - try { - assertNull(template.receiveAndConvert(queue.getName())); - } finally { - container.shutdown(); - } - } - - private RabbitTemplate createTemplate(int concurrentConsumers) { - RabbitTemplate template = new RabbitTemplate(); - // SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - return template; - } - - // /////////////// - // Helper classes - // /////////////// - public static class PojoThrowingExceptionListener { - private CountDownLatch latch; - private Throwable exception; - - public PojoThrowingExceptionListener(CountDownLatch latch, Throwable exception) { - this.latch = latch; - this.exception = exception; - } - - public void handleMessage(String value) throws Throwable { - try { - logger.debug("Message in pojo: " + value); - Thread.sleep(100L); - throw exception; - } finally { - latch.countDown(); - } - } - } - - public static class ThrowingExceptionListener implements MessageListener { - private CountDownLatch latch; - private RuntimeException exception; - - public ThrowingExceptionListener(CountDownLatch latch, RuntimeException exception) { - this.latch = latch; - this.exception = exception; - } - - public void onMessage(Message message) { - try { - String value = new String(message.getBody()); - logger.debug("Message in listener: " + value); - try { - Thread.sleep(100L); - } catch (InterruptedException e) { - // Ignore this exception - } - throw exception; - } finally { - latch.countDown(); - } - } - } - - public static class ThrowingExceptionChannelAwareListener implements ChannelAwareMessageListener { - private CountDownLatch latch; - private Exception exception; - - public ThrowingExceptionChannelAwareListener(CountDownLatch latch, Exception exception) { - this.latch = latch; - this.exception = exception; - } - - public void onMessage(Message message, Channel channel) throws Exception { - try { - String value = new String(message.getBody()); - logger.debug("Message in channel aware listener: " + value); - try { - Thread.sleep(100L); - } catch (InterruptedException e) { - // Ignore this exception - } - throw exception; - } finally { - latch.countDown(); - } - } - } -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; +import org.springframework.util.ErrorHandler; + +import com.rabbitmq.client.Channel; + +public class MessageListenerContainerErrorHandlerIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerContainerErrorHandlerIntegrationTests.class); + + private static Queue queue = new Queue("test.queue"); + + // Mock error handler + private ErrorHandler errorHandler = mock(ErrorHandler.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, + MessageListenerContainerErrorHandlerIntegrationTests.class); + + @Before + public void setUp() { + reset(errorHandler); + } + + @Test + public void testErrorHandlerInvokeExceptionFromPojo() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new MessageListenerAdapter(new PojoThrowingExceptionListener(latch, + new Exception("Pojo exception")))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + @Test + public void testErrorHandlerInvokeRuntimeExceptionFromPojo() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new MessageListenerAdapter(new PojoThrowingExceptionListener(latch, + new RuntimeException("Pojo runtime exception")))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + @Test + public void testErrorHandlerListenerExecutionFailedExceptionFromListener() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new ThrowingExceptionListener(latch, + new ListenerExecutionFailedException("Listener throws specific runtime exception", null))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + @Test + public void testErrorHandlerRegularRuntimeExceptionFromListener() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new ThrowingExceptionListener(latch, new RuntimeException( + "Listener runtime exception"))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + @Test + public void testErrorHandlerInvokeExceptionFromChannelAwareListener() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new ThrowingExceptionChannelAwareListener(latch, new Exception( + "Channel aware listener exception"))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + @Test + public void testErrorHandlerInvokeRuntimeExceptionFromChannelAwareListener() throws Exception { + int messageCount = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + doTest(messageCount, errorHandler, latch, new ThrowingExceptionChannelAwareListener(latch, + new RuntimeException("Channel aware listener runtime exception"))); + + // Verify that error handler was invoked + verify(errorHandler, times(messageCount)).handleError(any(Throwable.class)); + } + + public void doTest(int messageCount, ErrorHandler errorHandler, CountDownLatch latch, Object listener) + throws Exception { + int concurrentConsumers = 1; + RabbitTemplate template = createTemplate(concurrentConsumers); + + // Send messages to the queue + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); + container.setMessageListener(listener); + container.setAcknowledgeMode(AcknowledgeMode.NONE); + container.setChannelTransacted(false); + container.setConcurrentConsumers(concurrentConsumers); + + container.setPrefetchCount(messageCount); + container.setTxSize(messageCount); + container.setQueueNames(queue.getName()); + container.setErrorHandler(errorHandler); + container.afterPropertiesSet(); + container.start(); + + boolean waited = latch.await(500, TimeUnit.MILLISECONDS); + if (messageCount > 1) { + assertTrue("Expected to receive all messages before stop", waited); + } + + try { + assertNull(template.receiveAndConvert(queue.getName())); + } finally { + container.shutdown(); + } + } + + private RabbitTemplate createTemplate(int concurrentConsumers) { + RabbitTemplate template = new RabbitTemplate(); + // SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + return template; + } + + // /////////////// + // Helper classes + // /////////////// + public static class PojoThrowingExceptionListener { + private CountDownLatch latch; + private Throwable exception; + + public PojoThrowingExceptionListener(CountDownLatch latch, Throwable exception) { + this.latch = latch; + this.exception = exception; + } + + public void handleMessage(String value) throws Throwable { + try { + logger.debug("Message in pojo: " + value); + Thread.sleep(100L); + throw exception; + } finally { + latch.countDown(); + } + } + } + + public static class ThrowingExceptionListener implements MessageListener { + private CountDownLatch latch; + private RuntimeException exception; + + public ThrowingExceptionListener(CountDownLatch latch, RuntimeException exception) { + this.latch = latch; + this.exception = exception; + } + + public void onMessage(Message message) { + try { + String value = new String(message.getBody()); + logger.debug("Message in listener: " + value); + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + // Ignore this exception + } + throw exception; + } finally { + latch.countDown(); + } + } + } + + public static class ThrowingExceptionChannelAwareListener implements ChannelAwareMessageListener { + private CountDownLatch latch; + private Exception exception; + + public ThrowingExceptionChannelAwareListener(CountDownLatch latch, Exception exception) { + this.latch = latch; + this.exception = exception; + } + + public void onMessage(Message message, Channel channel) throws Exception { + try { + String value = new String(message.getBody()); + logger.debug("Message in channel aware listener: " + value); + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + // Ignore this exception + } + throw exception; + } finally { + latch.countDown(); + } + } + } +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerLifecycleIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerLifecycleIntegrationTests.java index 127dca42f7..f3933afc38 100755 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerLifecycleIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerLifecycleIntegrationTests.java @@ -1,249 +1,249 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; - -public class MessageListenerContainerLifecycleIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerContainerLifecycleIntegrationTests.class); - - private static Queue queue = new Queue("test.queue"); - - private enum TransactionMode { - ON, OFF, PREFETCH; - public boolean isTransactional() { - return this != OFF; - } - - public AcknowledgeMode getAcknowledgeMode() { - return this == OFF ? AcknowledgeMode.NONE : AcknowledgeMode.AUTO; - } - - public int getPrefetch() { - return this == PREFETCH ? 10 : -1; - } - - public int getTxSize() { - return this == PREFETCH ? 5 : -1; - } - } - - private enum Concurrency { - LOW(1), HIGH(5); - private final int value; - - private Concurrency(int value) { - this.value = value; - } - - public int value() { - return this.value; - } - } - - private enum MessageCount { - LOW(1), MEDIUM(20), HIGH(500); - private final int value; - - private MessageCount(int value) { - this.value = value; - } - - public int value() { - return this.value; - } - } - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, - MessageListenerContainerLifecycleIntegrationTests.class); - - private RabbitTemplate createTemplate(int concurrentConsumers) { - RabbitTemplate template = new RabbitTemplate(); - // SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - return template; - } - - @Test - public void testTransactionalLowLevel() throws Exception { - doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.ON); - } - - @Test - public void testTransactionalHighLevel() throws Exception { - doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.ON); - } - - @Test - public void testTransactionalLowLevelWithPrefetch() throws Exception { - doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.PREFETCH); - } - - @Test - public void testTransactionalHighLevelWithPrefetch() throws Exception { - doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.PREFETCH); - } - - @Test - public void testNonTransactionalLowLevel() throws Exception { - doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.OFF); - } - - @Test - public void testNonTransactionalHighLevel() throws Exception { - doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.OFF); - } - - private void doTest(MessageCount level, Concurrency concurrency, TransactionMode transactionMode) throws Exception { - - int messageCount = level.value(); - int concurrentConsumers = concurrency.value(); - boolean transactional = transactionMode.isTransactional(); - - RabbitTemplate template = createTemplate(concurrentConsumers); - - CountDownLatch latch = new CountDownLatch(messageCount); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); - PojoListener listener = new PojoListener(latch); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setAcknowledgeMode(transactionMode.getAcknowledgeMode()); - container.setChannelTransacted(transactionMode.isTransactional()); - container.setConcurrentConsumers(concurrentConsumers); - - if (transactionMode.getPrefetch() > 0) { - container.setPrefetchCount(transactionMode.getPrefetch()); - container.setTxSize(transactionMode.getTxSize()); - } - container.setQueueNames(queue.getName()); - container.afterPropertiesSet(); - container.start(); - - try { - - boolean waited = latch.await(50, TimeUnit.MILLISECONDS); - logger.info("All messages received before stop: " + waited); - if (messageCount > 1) { - assertFalse("Expected not to receive all messages before stop", waited); - } - - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - container.stop(); - int n = 0; - while (n++ < 100 && container.getActiveConsumerCount() > 0) { - Thread.sleep(100); - } - assertEquals(0, container.getActiveConsumerCount()); - if (!transactional) { - - int messagesReceivedAfterStop = listener.getCount(); - waited = latch.await(500, TimeUnit.MILLISECONDS); - logger.info("All messages received after stop: " + waited); - if (messageCount < 100) { - assertTrue("Expected to receive all messages after stop", waited); - } - assertEquals("Unexpected additional messages received after stop", messagesReceivedAfterStop, - listener.getCount()); - - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "bar"); - } - latch = new CountDownLatch(messageCount); - listener.reset(latch); - - } - - int messagesReceivedBeforeStart = listener.getCount(); - container.start(); - int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); - - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - waited = latch.await(timeout, TimeUnit.SECONDS); - logger.info("All messages received after start: " + waited); - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - if (transactional) { - assertTrue("Timed out waiting for message", waited); - } else { - int count = listener.getCount(); - assertTrue("Expected additional messages received after start: " + messagesReceivedBeforeStart + ">=" - + count, messagesReceivedBeforeStart < count); - assertNull("Messages still available", template.receive(queue.getName())); - } - - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - - } finally { - // Wait for broker communication to finish before trying to stop - // container - Thread.sleep(500L); - container.shutdown(); - } - - int n = 0; - while (n++ < 100 && container.getActiveConsumerCount() > 0) { - Thread.sleep(100); - } - assertEquals(0, container.getActiveConsumerCount()); - assertNull(template.receiveAndConvert(queue.getName())); - - } - - public static class PojoListener { - private AtomicInteger count = new AtomicInteger(); - - private CountDownLatch latch; - - public PojoListener(CountDownLatch latch) { - this.latch = latch; - } - - public void reset(CountDownLatch latch) { - this.latch = latch; - } - - public void handleMessage(String value) throws Exception { - try { - logger.debug(value + count.getAndIncrement()); - Thread.sleep(100L); - } finally { - latch.countDown(); - } - } - - public int getCount() { - return count.get(); - } - } - -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; + +public class MessageListenerContainerLifecycleIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerContainerLifecycleIntegrationTests.class); + + private static Queue queue = new Queue("test.queue"); + + private enum TransactionMode { + ON, OFF, PREFETCH; + public boolean isTransactional() { + return this != OFF; + } + + public AcknowledgeMode getAcknowledgeMode() { + return this == OFF ? AcknowledgeMode.NONE : AcknowledgeMode.AUTO; + } + + public int getPrefetch() { + return this == PREFETCH ? 10 : -1; + } + + public int getTxSize() { + return this == PREFETCH ? 5 : -1; + } + } + + private enum Concurrency { + LOW(1), HIGH(5); + private final int value; + + private Concurrency(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + } + + private enum MessageCount { + LOW(1), MEDIUM(20), HIGH(500); + private final int value; + + private MessageCount(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + } + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, + MessageListenerContainerLifecycleIntegrationTests.class); + + private RabbitTemplate createTemplate(int concurrentConsumers) { + RabbitTemplate template = new RabbitTemplate(); + // SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + return template; + } + + @Test + public void testTransactionalLowLevel() throws Exception { + doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.ON); + } + + @Test + public void testTransactionalHighLevel() throws Exception { + doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.ON); + } + + @Test + public void testTransactionalLowLevelWithPrefetch() throws Exception { + doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.PREFETCH); + } + + @Test + public void testTransactionalHighLevelWithPrefetch() throws Exception { + doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.PREFETCH); + } + + @Test + public void testNonTransactionalLowLevel() throws Exception { + doTest(MessageCount.MEDIUM, Concurrency.LOW, TransactionMode.OFF); + } + + @Test + public void testNonTransactionalHighLevel() throws Exception { + doTest(MessageCount.HIGH, Concurrency.HIGH, TransactionMode.OFF); + } + + private void doTest(MessageCount level, Concurrency concurrency, TransactionMode transactionMode) throws Exception { + + int messageCount = level.value(); + int concurrentConsumers = concurrency.value(); + boolean transactional = transactionMode.isTransactional(); + + RabbitTemplate template = createTemplate(concurrentConsumers); + + CountDownLatch latch = new CountDownLatch(messageCount); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); + PojoListener listener = new PojoListener(latch); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setAcknowledgeMode(transactionMode.getAcknowledgeMode()); + container.setChannelTransacted(transactionMode.isTransactional()); + container.setConcurrentConsumers(concurrentConsumers); + + if (transactionMode.getPrefetch() > 0) { + container.setPrefetchCount(transactionMode.getPrefetch()); + container.setTxSize(transactionMode.getTxSize()); + } + container.setQueueNames(queue.getName()); + container.afterPropertiesSet(); + container.start(); + + try { + + boolean waited = latch.await(50, TimeUnit.MILLISECONDS); + logger.info("All messages received before stop: " + waited); + if (messageCount > 1) { + assertFalse("Expected not to receive all messages before stop", waited); + } + + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + container.stop(); + int n = 0; + while (n++ < 100 && container.getActiveConsumerCount() > 0) { + Thread.sleep(100); + } + assertEquals(0, container.getActiveConsumerCount()); + if (!transactional) { + + int messagesReceivedAfterStop = listener.getCount(); + waited = latch.await(500, TimeUnit.MILLISECONDS); + logger.info("All messages received after stop: " + waited); + if (messageCount < 100) { + assertTrue("Expected to receive all messages after stop", waited); + } + assertEquals("Unexpected additional messages received after stop", messagesReceivedAfterStop, + listener.getCount()); + + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "bar"); + } + latch = new CountDownLatch(messageCount); + listener.reset(latch); + + } + + int messagesReceivedBeforeStart = listener.getCount(); + container.start(); + int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); + + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + waited = latch.await(timeout, TimeUnit.SECONDS); + logger.info("All messages received after start: " + waited); + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + if (transactional) { + assertTrue("Timed out waiting for message", waited); + } else { + int count = listener.getCount(); + assertTrue("Expected additional messages received after start: " + messagesReceivedBeforeStart + ">=" + + count, messagesReceivedBeforeStart < count); + assertNull("Messages still available", template.receive(queue.getName())); + } + + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + + } finally { + // Wait for broker communication to finish before trying to stop + // container + Thread.sleep(500L); + container.shutdown(); + } + + int n = 0; + while (n++ < 100 && container.getActiveConsumerCount() > 0) { + Thread.sleep(100); + } + assertEquals(0, container.getActiveConsumerCount()); + assertNull(template.receiveAndConvert(queue.getName())); + + } + + public static class PojoListener { + private AtomicInteger count = new AtomicInteger(); + + private CountDownLatch latch; + + public PojoListener(CountDownLatch latch) { + this.latch = latch; + } + + public void reset(CountDownLatch latch) { + this.latch = latch; + } + + public void handleMessage(String value) throws Exception { + try { + logger.debug(value + count.getAndIncrement()); + Thread.sleep(100L); + } finally { + latch.countDown(); + } + } + + public int getCount() { + return count.get(); + } + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerMultipleQueueIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerMultipleQueueIntegrationTests.java index 013735e5ce..529f0f06ee 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerMultipleQueueIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerMultipleQueueIntegrationTests.java @@ -1,174 +1,174 @@ -/* - * Copyright 2002-2011 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.listener; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; -import org.springframework.amqp.support.converter.SimpleMessageConverter; - -/** - * @author Mark Fisher - */ -public class MessageListenerContainerMultipleQueueIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerContainerMultipleQueueIntegrationTests.class); - - private static Queue queue1 = new Queue("test.queue.1"); - - private static Queue queue2 = new Queue("test.queue.2"); - - @Rule - public BrokerRunning brokerIsRunningAndQueue1Empty = BrokerRunning.isRunningWithEmptyQueues(queue1); - - @Rule - public BrokerRunning brokerIsRunningAndQueue2Empty = BrokerRunning.isRunningWithEmptyQueues(queue2); - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); - - @Rule - public ExpectedException exception = ExpectedException.none(); - - - @Test - public void testMultipleQueues() { - doTest(1, new ContainerConfigurer() { - public void configure(SimpleMessageListenerContainer container) { - container.setQueues(queue1, queue2); - } - }); - } - - @Test - public void testMultipleQueueNames() { - doTest(1, new ContainerConfigurer() { - public void configure(SimpleMessageListenerContainer container) { - container.setQueueNames(queue1.getName(), queue2.getName()); - } - }); - } - - @Test - public void testMultipleQueuesWithConcurrentConsumers() { - doTest(3, new ContainerConfigurer() { - public void configure(SimpleMessageListenerContainer container) { - container.setQueues(queue1, queue2); - } - }); - } - - @Test - public void testMultipleQueueNamesWithConcurrentConsumers() { - doTest(3, new ContainerConfigurer() { - public void configure(SimpleMessageListenerContainer container) { - container.setQueueNames(queue1.getName(), queue2.getName()); - } - }); - } - - - private void doTest(int concurrentConsumers, ContainerConfigurer configurer) { - int messageCount = 10; - RabbitTemplate template = new RabbitTemplate(); - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - SimpleMessageConverter messageConverter = new SimpleMessageConverter(); - messageConverter.setCreateMessageIds(true); - template.setMessageConverter(messageConverter); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue1.getName(), new Integer(i)); - template.convertAndSend(queue2.getName(), new Integer(i)); - } - final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - final CountDownLatch latch = new CountDownLatch(messageCount * 2); - PojoListener listener = new PojoListener(latch); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setAcknowledgeMode(AcknowledgeMode.AUTO); - container.setChannelTransacted(true); - container.setConcurrentConsumers(concurrentConsumers); - configurer.configure(container); - container.afterPropertiesSet(); - container.start(); - try { - int timeout = Math.min(1 + messageCount / concurrentConsumers, 30); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - logger.info("All messages recovered: " + waited); - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - assertTrue("Timed out waiting for messages", waited); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("unexpected interruption"); - } - finally { - container.shutdown(); - assertEquals(0, container.getActiveConsumerCount()); - } - assertNull(template.receiveAndConvert(queue1.getName())); - assertNull(template.receiveAndConvert(queue2.getName())); - } - - - private interface ContainerConfigurer { - void configure(SimpleMessageListenerContainer container); - } - - - @SuppressWarnings("unused") - private static class PojoListener { - - private AtomicInteger count = new AtomicInteger(); - - private final CountDownLatch latch; - - public PojoListener(CountDownLatch latch) { - this.latch = latch; - } - - public void handleMessage(int value) throws Exception { - logger.debug(value + ":" + count.getAndIncrement()); - latch.countDown(); - } - - public int getCount() { - return count.get(); - } - } - -} +/* + * Copyright 2002-2011 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.listener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; +import org.springframework.amqp.support.converter.SimpleMessageConverter; + +/** + * @author Mark Fisher + */ +public class MessageListenerContainerMultipleQueueIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerContainerMultipleQueueIntegrationTests.class); + + private static Queue queue1 = new Queue("test.queue.1"); + + private static Queue queue2 = new Queue("test.queue.2"); + + @Rule + public BrokerRunning brokerIsRunningAndQueue1Empty = BrokerRunning.isRunningWithEmptyQueues(queue1); + + @Rule + public BrokerRunning brokerIsRunningAndQueue2Empty = BrokerRunning.isRunningWithEmptyQueues(queue2); + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + + @Test + public void testMultipleQueues() { + doTest(1, new ContainerConfigurer() { + public void configure(SimpleMessageListenerContainer container) { + container.setQueues(queue1, queue2); + } + }); + } + + @Test + public void testMultipleQueueNames() { + doTest(1, new ContainerConfigurer() { + public void configure(SimpleMessageListenerContainer container) { + container.setQueueNames(queue1.getName(), queue2.getName()); + } + }); + } + + @Test + public void testMultipleQueuesWithConcurrentConsumers() { + doTest(3, new ContainerConfigurer() { + public void configure(SimpleMessageListenerContainer container) { + container.setQueues(queue1, queue2); + } + }); + } + + @Test + public void testMultipleQueueNamesWithConcurrentConsumers() { + doTest(3, new ContainerConfigurer() { + public void configure(SimpleMessageListenerContainer container) { + container.setQueueNames(queue1.getName(), queue2.getName()); + } + }); + } + + + private void doTest(int concurrentConsumers, ContainerConfigurer configurer) { + int messageCount = 10; + RabbitTemplate template = new RabbitTemplate(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + SimpleMessageConverter messageConverter = new SimpleMessageConverter(); + messageConverter.setCreateMessageIds(true); + template.setMessageConverter(messageConverter); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue1.getName(), new Integer(i)); + template.convertAndSend(queue2.getName(), new Integer(i)); + } + final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); + final CountDownLatch latch = new CountDownLatch(messageCount * 2); + PojoListener listener = new PojoListener(latch); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setAcknowledgeMode(AcknowledgeMode.AUTO); + container.setChannelTransacted(true); + container.setConcurrentConsumers(concurrentConsumers); + configurer.configure(container); + container.afterPropertiesSet(); + container.start(); + try { + int timeout = Math.min(1 + messageCount / concurrentConsumers, 30); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + logger.info("All messages recovered: " + waited); + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + assertTrue("Timed out waiting for messages", waited); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("unexpected interruption"); + } + finally { + container.shutdown(); + assertEquals(0, container.getActiveConsumerCount()); + } + assertNull(template.receiveAndConvert(queue1.getName())); + assertNull(template.receiveAndConvert(queue2.getName())); + } + + + private interface ContainerConfigurer { + void configure(SimpleMessageListenerContainer container); + } + + + @SuppressWarnings("unused") + private static class PojoListener { + + private AtomicInteger count = new AtomicInteger(); + + private final CountDownLatch latch; + + public PojoListener(CountDownLatch latch) { + this.latch = latch; + } + + public void handleMessage(int value) throws Exception { + logger.debug(value + ":" + count.getAndIncrement()); + latch.countDown(); + } + + public int getCount() { + return count.get(); + } + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerRetryIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerRetryIntegrationTests.java index 65d2a8bb95..8ce4779f2b 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerRetryIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerContainerRetryIntegrationTests.java @@ -1,266 +1,266 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.aopalliance.aop.Advice; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.config.AbstractRetryOperationsInterceptorFactoryBean; -import org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean; -import org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.retry.MessageRecoverer; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; -import org.springframework.amqp.rabbit.test.RepeatProcessor; -import org.springframework.amqp.support.converter.MessageConverter; -import org.springframework.amqp.support.converter.SimpleMessageConverter; -import org.springframework.amqp.utils.SerializationUtils; -import org.springframework.retry.policy.MapRetryContextCache; -import org.springframework.retry.support.RetryTemplate; -import org.springframework.test.annotation.Repeat; - -public class MessageListenerContainerRetryIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerContainerRetryIntegrationTests.class); - - private static Queue queue = new Queue("test.queue"); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); - - @Rule - public Log4jLevelAdjuster traceLevels = new Log4jLevelAdjuster(Level.TRACE, StatefulRetryOperationsInterceptorFactoryBean.class, MessageListenerContainerRetryIntegrationTests.class); - - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Rule - public RepeatProcessor repeats = new RepeatProcessor(); - - private RabbitTemplate template; - - private RetryTemplate retryTemplate; - - private MessageConverter messageConverter; - - private RabbitTemplate createTemplate(int concurrentConsumers) { - RabbitTemplate template = new RabbitTemplate(); - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - if (messageConverter == null) { - SimpleMessageConverter messageConverter = new SimpleMessageConverter(); - messageConverter.setCreateMessageIds(true); - this.messageConverter = messageConverter; - } - template.setMessageConverter(messageConverter); - return template; - } - - @Test - public void testStatefulRetryWithAllMessagesFailing() throws Exception { - - int messageCount = 10; - int txSize = 1; - int failFrequency = 1; - int concurrentConsumers = 3; - doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); - - } - - @Test - public void testStatelessRetryWithAllMessagesFailing() throws Exception { - - int messageCount = 10; - int txSize = 1; - int failFrequency = 1; - int concurrentConsumers = 3; - doTestStatelessRetry(messageCount, txSize, failFrequency, concurrentConsumers); - - } - - @Test - public void testStatefulRetryWithNoMessageIds() throws Exception { - - int messageCount = 2; - int txSize = 1; - int failFrequency = 1; - int concurrentConsumers = 1; - SimpleMessageConverter messageConverter = new SimpleMessageConverter(); - // There will be no key for these messages so they cannot be recovered... - messageConverter.setCreateMessageIds(false); - this.messageConverter = messageConverter; - // Beware of context cache busting if retry policy fails... - this.retryTemplate = new RetryTemplate(); - this.retryTemplate.setRetryContextCache(new MapRetryContextCache(1)); - // The container should have shutdown, so there are now no active consumers - exception.expectMessage("expected:<1> but was:<0>"); - doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); - - } - - @Test - @Repeat(10) - public void testStatefulRetryWithTxSizeAndIntermittentFailure() throws Exception { - - int messageCount = 10; - int txSize = 4; - int failFrequency = 3; - int concurrentConsumers = 3; - doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); - - } - - @Test - public void testStatefulRetryWithMoreMessages() throws Exception { - - int messageCount = 200; - int txSize = 10; - int failFrequency = 6; - int concurrentConsumers = 3; - doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); - - } - - private Advice createRetryInterceptor(final CountDownLatch latch, boolean stateful) throws Exception { - AbstractRetryOperationsInterceptorFactoryBean factory; - if (stateful) { - factory = new StatefulRetryOperationsInterceptorFactoryBean(); - } else { - factory = new StatelessRetryOperationsInterceptorFactoryBean(); - } - factory.setMessageRecoverer(new MessageRecoverer() { - public void recover(Message message, Throwable cause) { - logger.info("Recovered: [" + SerializationUtils.deserialize(message.getBody()).toString()+"], message: " +message); - latch.countDown(); - } - }); - if (retryTemplate == null) { - retryTemplate = new RetryTemplate(); - } - factory.setRetryOperations(retryTemplate); - Advice retryInterceptor = factory.getObject(); - return retryInterceptor; - } - - private void doTestStatefulRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers) - throws Exception { - doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, true); - } - - private void doTestStatelessRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers) - throws Exception { - doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, false); - } - - private void doTestRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers, boolean stateful) - throws Exception { - - int failedMessageCount = messageCount / failFrequency + (messageCount % failFrequency == 0 ? 0 : 1); - - template = createTemplate(concurrentConsumers); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), new Integer(i)); - } - - final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer( - template.getConnectionFactory()); - PojoListener listener = new PojoListener(failFrequency); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setAcknowledgeMode(AcknowledgeMode.AUTO); - container.setChannelTransacted(true); - container.setTxSize(txSize); - container.setConcurrentConsumers(concurrentConsumers); - - final CountDownLatch latch = new CountDownLatch(failedMessageCount); - container.setAdviceChain(new Advice[] { createRetryInterceptor(latch, stateful) }); - - container.setQueueNames(queue.getName()); - container.afterPropertiesSet(); - container.start(); - - try { - - int timeout = Math.min(1 + 2 * messageCount / concurrentConsumers, 30); - - final int count = messageCount; - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - Executors.newSingleThreadExecutor().execute(new Runnable() { - public void run() { - while (container.getActiveConsumerCount() > 0) { - try { - Thread.sleep(100L); - } catch (InterruptedException e) { - latch.countDown(); - Thread.currentThread().interrupt(); - return; - } - } - for (int i = 0; i < count; i++) { - latch.countDown(); - } - } - }); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - logger.info("All messages recovered: " + waited); - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - assertTrue("Timed out waiting for messages", waited); - - // Retried each failure 3 times (default retry policy)... - assertEquals(3 * failedMessageCount, listener.getCount()); - - // All failed messages recovered - assertEquals(null, template.receiveAndConvert(queue.getName())); - - } finally { - container.shutdown(); - assertEquals(0, container.getActiveConsumerCount()); - } - - } - - public static class PojoListener { - private AtomicInteger count = new AtomicInteger(); - private final int failFrequency; - - public PojoListener(int failFrequency) { - this.failFrequency = failFrequency; - } - - public void handleMessage(int value) throws Exception { - logger.debug("Handling: ["+value+ "], fails:" + count); - if (value % failFrequency == 0) { - count.getAndIncrement(); - logger.debug("Failing: ["+value+ "], fails:" + count); - throw new RuntimeException("Planned"); - } - } - - public int getCount() { - return count.get(); - } - } - -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.aopalliance.aop.Advice; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.config.AbstractRetryOperationsInterceptorFactoryBean; +import org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean; +import org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.retry.MessageRecoverer; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; +import org.springframework.amqp.rabbit.test.RepeatProcessor; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.amqp.support.converter.SimpleMessageConverter; +import org.springframework.amqp.utils.SerializationUtils; +import org.springframework.retry.policy.MapRetryContextCache; +import org.springframework.retry.support.RetryTemplate; +import org.springframework.test.annotation.Repeat; + +public class MessageListenerContainerRetryIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerContainerRetryIntegrationTests.class); + + private static Queue queue = new Queue("test.queue"); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); + + @Rule + public Log4jLevelAdjuster traceLevels = new Log4jLevelAdjuster(Level.TRACE, StatefulRetryOperationsInterceptorFactoryBean.class, MessageListenerContainerRetryIntegrationTests.class); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Rule + public RepeatProcessor repeats = new RepeatProcessor(); + + private RabbitTemplate template; + + private RetryTemplate retryTemplate; + + private MessageConverter messageConverter; + + private RabbitTemplate createTemplate(int concurrentConsumers) { + RabbitTemplate template = new RabbitTemplate(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + if (messageConverter == null) { + SimpleMessageConverter messageConverter = new SimpleMessageConverter(); + messageConverter.setCreateMessageIds(true); + this.messageConverter = messageConverter; + } + template.setMessageConverter(messageConverter); + return template; + } + + @Test + public void testStatefulRetryWithAllMessagesFailing() throws Exception { + + int messageCount = 10; + int txSize = 1; + int failFrequency = 1; + int concurrentConsumers = 3; + doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); + + } + + @Test + public void testStatelessRetryWithAllMessagesFailing() throws Exception { + + int messageCount = 10; + int txSize = 1; + int failFrequency = 1; + int concurrentConsumers = 3; + doTestStatelessRetry(messageCount, txSize, failFrequency, concurrentConsumers); + + } + + @Test + public void testStatefulRetryWithNoMessageIds() throws Exception { + + int messageCount = 2; + int txSize = 1; + int failFrequency = 1; + int concurrentConsumers = 1; + SimpleMessageConverter messageConverter = new SimpleMessageConverter(); + // There will be no key for these messages so they cannot be recovered... + messageConverter.setCreateMessageIds(false); + this.messageConverter = messageConverter; + // Beware of context cache busting if retry policy fails... + this.retryTemplate = new RetryTemplate(); + this.retryTemplate.setRetryContextCache(new MapRetryContextCache(1)); + // The container should have shutdown, so there are now no active consumers + exception.expectMessage("expected:<1> but was:<0>"); + doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); + + } + + @Test + @Repeat(10) + public void testStatefulRetryWithTxSizeAndIntermittentFailure() throws Exception { + + int messageCount = 10; + int txSize = 4; + int failFrequency = 3; + int concurrentConsumers = 3; + doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); + + } + + @Test + public void testStatefulRetryWithMoreMessages() throws Exception { + + int messageCount = 200; + int txSize = 10; + int failFrequency = 6; + int concurrentConsumers = 3; + doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers); + + } + + private Advice createRetryInterceptor(final CountDownLatch latch, boolean stateful) throws Exception { + AbstractRetryOperationsInterceptorFactoryBean factory; + if (stateful) { + factory = new StatefulRetryOperationsInterceptorFactoryBean(); + } else { + factory = new StatelessRetryOperationsInterceptorFactoryBean(); + } + factory.setMessageRecoverer(new MessageRecoverer() { + public void recover(Message message, Throwable cause) { + logger.info("Recovered: [" + SerializationUtils.deserialize(message.getBody()).toString()+"], message: " +message); + latch.countDown(); + } + }); + if (retryTemplate == null) { + retryTemplate = new RetryTemplate(); + } + factory.setRetryOperations(retryTemplate); + Advice retryInterceptor = factory.getObject(); + return retryInterceptor; + } + + private void doTestStatefulRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers) + throws Exception { + doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, true); + } + + private void doTestStatelessRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers) + throws Exception { + doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, false); + } + + private void doTestRetry(int messageCount, int txSize, int failFrequency, int concurrentConsumers, boolean stateful) + throws Exception { + + int failedMessageCount = messageCount / failFrequency + (messageCount % failFrequency == 0 ? 0 : 1); + + template = createTemplate(concurrentConsumers); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), new Integer(i)); + } + + final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer( + template.getConnectionFactory()); + PojoListener listener = new PojoListener(failFrequency); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setAcknowledgeMode(AcknowledgeMode.AUTO); + container.setChannelTransacted(true); + container.setTxSize(txSize); + container.setConcurrentConsumers(concurrentConsumers); + + final CountDownLatch latch = new CountDownLatch(failedMessageCount); + container.setAdviceChain(new Advice[] { createRetryInterceptor(latch, stateful) }); + + container.setQueueNames(queue.getName()); + container.afterPropertiesSet(); + container.start(); + + try { + + int timeout = Math.min(1 + 2 * messageCount / concurrentConsumers, 30); + + final int count = messageCount; + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + Executors.newSingleThreadExecutor().execute(new Runnable() { + public void run() { + while (container.getActiveConsumerCount() > 0) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + latch.countDown(); + Thread.currentThread().interrupt(); + return; + } + } + for (int i = 0; i < count; i++) { + latch.countDown(); + } + } + }); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + logger.info("All messages recovered: " + waited); + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + assertTrue("Timed out waiting for messages", waited); + + // Retried each failure 3 times (default retry policy)... + assertEquals(3 * failedMessageCount, listener.getCount()); + + // All failed messages recovered + assertEquals(null, template.receiveAndConvert(queue.getName())); + + } finally { + container.shutdown(); + assertEquals(0, container.getActiveConsumerCount()); + } + + } + + public static class PojoListener { + private AtomicInteger count = new AtomicInteger(); + private final int failFrequency; + + public PojoListener(int failFrequency) { + this.failFrequency = failFrequency; + } + + public void handleMessage(int value) throws Exception { + logger.debug("Handling: ["+value+ "], fails:" + count); + if (value % failFrequency == 0) { + count.getAndIncrement(); + logger.debug("Failing: ["+value+ "], fails:" + count); + throw new RuntimeException("Planned"); + } + } + + public int getCount() { + return count.get(); + } + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerManualAckIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerManualAckIntegrationTests.java index 71574c72a3..a00bcc3664 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerManualAckIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerManualAckIntegrationTests.java @@ -1,137 +1,137 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; - -import com.rabbitmq.client.Channel; - -public class MessageListenerManualAckIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerManualAckIntegrationTests.class); - - private Queue queue = new Queue("test.queue"); - - private RabbitTemplate template = new RabbitTemplate(); - - private int concurrentConsumers = 1; - - private int messageCount = 50; - - private int txSize = 1; - - private boolean transactional = false; - - private SimpleMessageListenerContainer container; - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Before - public void createConnectionFactory() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - } - - @After - public void clear() throws Exception { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.debug("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - } - - @Test - public void testListenerWithManualAckNonTransactional() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(new TestListener(latch)); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - assertNull(template.receiveAndConvert(queue.getName())); - } - - @Test - public void testListenerWithManualAckTransactional() throws Exception { - transactional = true; - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(new TestListener(latch)); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - assertNull(template.receiveAndConvert(queue.getName())); - } - - private SimpleMessageListenerContainer createContainer(Object listener) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setQueueNames(queue.getName()); - container.setTxSize(txSize); - container.setPrefetchCount(txSize); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(AcknowledgeMode.MANUAL); - container.afterPropertiesSet(); - container.start(); - return container; - } - - public static class TestListener implements ChannelAwareMessageListener { - - private final CountDownLatch latch; - - public TestListener(CountDownLatch latch) { - this.latch = latch; - } - - public void handleMessage(String value) { - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - try { - logger.debug("Acking: " + value); - channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); - } finally { - latch.countDown(); - } - } - } - -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; + +import com.rabbitmq.client.Channel; + +public class MessageListenerManualAckIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerManualAckIntegrationTests.class); + + private Queue queue = new Queue("test.queue"); + + private RabbitTemplate template = new RabbitTemplate(); + + private int concurrentConsumers = 1; + + private int messageCount = 50; + + private int txSize = 1; + + private boolean transactional = false; + + private SimpleMessageListenerContainer container; + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Before + public void createConnectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + } + + @After + public void clear() throws Exception { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.debug("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + } + + @Test + public void testListenerWithManualAckNonTransactional() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(new TestListener(latch)); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + assertNull(template.receiveAndConvert(queue.getName())); + } + + @Test + public void testListenerWithManualAckTransactional() throws Exception { + transactional = true; + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(new TestListener(latch)); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + assertNull(template.receiveAndConvert(queue.getName())); + } + + private SimpleMessageListenerContainer createContainer(Object listener) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setQueueNames(queue.getName()); + container.setTxSize(txSize); + container.setPrefetchCount(txSize); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(AcknowledgeMode.MANUAL); + container.afterPropertiesSet(); + container.start(); + return container; + } + + public static class TestListener implements ChannelAwareMessageListener { + + private final CountDownLatch latch; + + public TestListener(CountDownLatch latch) { + this.latch = latch; + } + + public void handleMessage(String value) { + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + try { + logger.debug("Acking: " + value); + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + } finally { + latch.countDown(); + } + } + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryCachingConnectionIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryCachingConnectionIntegrationTests.java index 50b939601d..71e2fdcea3 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryCachingConnectionIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryCachingConnectionIntegrationTests.java @@ -1,395 +1,395 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.AmqpIllegalStateException; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.Connection; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionProxy; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.MessageListenerBrokerInterruptionIntegrationTests.VanillaListener; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; - -import com.rabbitmq.client.Channel; - -public class MessageListenerRecoveryCachingConnectionIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerRecoveryCachingConnectionIntegrationTests.class); - - private Queue queue = new Queue("test.queue"); - - private Queue sendQueue = new Queue("test.send"); - - private int concurrentConsumers = 1; - - private int messageCount = 10; - - private boolean transactional = false; - - private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; - - private SimpleMessageListenerContainer container; - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, CachingConnectionFactory.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue, sendQueue); - - protected ConnectionFactory createConnectionFactory() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - return connectionFactory; - } - - @After - public void clear() throws Exception { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.debug("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - } - - @Test - public void testListenerSendsMessageAndThenContainerCommits() throws Exception { - - ConnectionFactory connectionFactory = createConnectionFactory(); - RabbitTemplate template = new RabbitTemplate(connectionFactory); - new RabbitAdmin(connectionFactory).declareQueue(sendQueue); - - acknowledgeMode = AcknowledgeMode.AUTO; - transactional = true; - - CountDownLatch latch = new CountDownLatch(1); - container = createContainer(queue.getName(), new ChannelSenderListener(sendQueue.getName(), latch, false), - connectionFactory); - template.convertAndSend(queue.getName(), "foo"); - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - // Give message time to reach broker (intermittent test failures)! - Thread.sleep(500L); - // All messages committed - byte[] bytes = (byte[]) template.receiveAndConvert(sendQueue.getName()); - assertNotNull(bytes); - assertEquals("bar", new String(bytes)); - assertEquals(null, template.receiveAndConvert(queue.getName())); - - } - - @Test - public void testListenerSendsMessageAndThenRollback() throws Exception { - - ConnectionFactory connectionFactory = createConnectionFactory(); - RabbitTemplate template = new RabbitTemplate(connectionFactory); - new RabbitAdmin(connectionFactory).declareQueue(sendQueue); - - acknowledgeMode = AcknowledgeMode.AUTO; - transactional = true; - - CountDownLatch latch = new CountDownLatch(1); - container = createContainer(queue.getName(), new ChannelSenderListener(sendQueue.getName(), latch, true), - connectionFactory); - template.convertAndSend(queue.getName(), "foo"); - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - container.stop(); - Thread.sleep(200L); - - // Foo message is redelivered - assertEquals("foo", template.receiveAndConvert(queue.getName())); - // Sending of bar message is also rolled back - assertNull(template.receiveAndConvert(sendQueue.getName())); - - } - - @Test - public void testListenerRecoversFromBogusDoubleAck() throws Exception { - - RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); - - acknowledgeMode = AcknowledgeMode.MANUAL; - - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(queue.getName(), new ManualAckListener(latch), createConnectionFactory()); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - @Test - public void testListenerRecoversFromClosedChannel() throws Exception { - - RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); - - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(queue.getName(), new AbortChannelListener(latch), createConnectionFactory()); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - @Test - public void testListenerRecoversFromClosedChannelAndStop() throws Exception { - - RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); - - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(queue.getName(), new AbortChannelListener(latch), createConnectionFactory()); - int n = 0; - while (n++ < 100 && container.getActiveConsumerCount() != concurrentConsumers) { - Thread.sleep(50L); - } - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - assertEquals(concurrentConsumers, container.getActiveConsumerCount()); - container.stop(); - assertEquals(0, container.getActiveConsumerCount()); - - } - - @Test - public void testListenerRecoversFromClosedConnection() throws Exception { - - RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); - - CountDownLatch latch = new CountDownLatch(messageCount); - ConnectionFactory connectionFactory = createConnectionFactory(); - container = createContainer(queue.getName(), - new CloseConnectionListener((ConnectionProxy) connectionFactory.createConnection(), latch), - connectionFactory); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - @Test - public void testListenerRecoversAndTemplateSharesConnectionFactory() throws Exception { - - ConnectionFactory connectionFactory = createConnectionFactory(); - RabbitTemplate template = new RabbitTemplate(connectionFactory); - - acknowledgeMode = AcknowledgeMode.MANUAL; - - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(queue.getName(), new ManualAckListener(latch), connectionFactory); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - // Give the listener container a chance to steal the connection from the template - Thread.sleep(200); - } - - int timeout = getTimeout(); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - @Test(expected = AmqpIllegalStateException.class) - public void testListenerDoesNotRecoverFromMissingQueue() throws Exception { - concurrentConsumers = 3; - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer("nonexistent", new VanillaListener(latch), createConnectionFactory()); - } - - @Test(expected = AmqpIllegalStateException.class) - public void testSingleListenerDoesNotRecoverFromMissingQueue() throws Exception { - /* - * A single listener sometimes doesn't have time to attempt to start before we ask it if it has failed, so this - * is a good test of that potential bug. - */ - concurrentConsumers = 1; - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer("nonexistent", new VanillaListener(latch), createConnectionFactory()); - } - - private int getTimeout() { - return Math.min(1 + messageCount / (concurrentConsumers), 30); - } - - private SimpleMessageListenerContainer createContainer(String queueName, Object listener, - ConnectionFactory connectionFactory) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setQueueNames(queueName); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(acknowledgeMode); - container.afterPropertiesSet(); - container.start(); - return container; - } - - public static class ManualAckListener implements ChannelAwareMessageListener { - - private AtomicBoolean failed = new AtomicBoolean(false); - - private final CountDownLatch latch; - - public ManualAckListener(CountDownLatch latch) { - this.latch = latch; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - try { - logger.debug("Acking: " + value); - channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); - if (failed.compareAndSet(false, true)) { - // intentional error (causes exception on connection thread): - channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); - } - } finally { - latch.countDown(); - } - } - } - - public static class ChannelSenderListener implements ChannelAwareMessageListener { - - private final CountDownLatch latch; - - private final boolean fail; - - private final String queueName; - - public ChannelSenderListener(String queueName, CountDownLatch latch, boolean fail) { - this.queueName = queueName; - this.latch = latch; - this.fail = fail; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - try { - logger.debug("Received: " + value + " Sending: bar"); - channel.basicPublish("", queueName, null, "bar".getBytes()); - if (fail) { - logger.debug("Failing (planned)"); - // intentional error (causes exception on connection thread): - throw new RuntimeException("Planned"); - } - } finally { - latch.countDown(); - } - } - } - - public static class AbortChannelListener implements ChannelAwareMessageListener { - - private AtomicBoolean failed = new AtomicBoolean(false); - - private final CountDownLatch latch; - - public AbortChannelListener(CountDownLatch latch) { - this.latch = latch; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - logger.debug("Receiving: " + value); - if (failed.compareAndSet(false, true)) { - // intentional error (causes exception on connection thread): - channel.abort(); - } else { - latch.countDown(); - } - } - } - - public static class CloseConnectionListener implements ChannelAwareMessageListener { - - private AtomicBoolean failed = new AtomicBoolean(false); - - private final CountDownLatch latch; - - private final Connection connection; - - public CloseConnectionListener(ConnectionProxy connection, CountDownLatch latch) { - this.connection = connection.getTargetConnection(); - this.latch = latch; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - logger.debug("Receiving: " + value); - if (failed.compareAndSet(false, true)) { - // intentional error (causes exception on connection thread): - connection.close(); - } else { - latch.countDown(); - } - } - } -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.AmqpIllegalStateException; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.Connection; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionProxy; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.MessageListenerBrokerInterruptionIntegrationTests.VanillaListener; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; + +import com.rabbitmq.client.Channel; + +public class MessageListenerRecoveryCachingConnectionIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerRecoveryCachingConnectionIntegrationTests.class); + + private Queue queue = new Queue("test.queue"); + + private Queue sendQueue = new Queue("test.send"); + + private int concurrentConsumers = 1; + + private int messageCount = 10; + + private boolean transactional = false; + + private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; + + private SimpleMessageListenerContainer container; + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, CachingConnectionFactory.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue, sendQueue); + + protected ConnectionFactory createConnectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + return connectionFactory; + } + + @After + public void clear() throws Exception { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.debug("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + } + + @Test + public void testListenerSendsMessageAndThenContainerCommits() throws Exception { + + ConnectionFactory connectionFactory = createConnectionFactory(); + RabbitTemplate template = new RabbitTemplate(connectionFactory); + new RabbitAdmin(connectionFactory).declareQueue(sendQueue); + + acknowledgeMode = AcknowledgeMode.AUTO; + transactional = true; + + CountDownLatch latch = new CountDownLatch(1); + container = createContainer(queue.getName(), new ChannelSenderListener(sendQueue.getName(), latch, false), + connectionFactory); + template.convertAndSend(queue.getName(), "foo"); + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + // Give message time to reach broker (intermittent test failures)! + Thread.sleep(500L); + // All messages committed + byte[] bytes = (byte[]) template.receiveAndConvert(sendQueue.getName()); + assertNotNull(bytes); + assertEquals("bar", new String(bytes)); + assertEquals(null, template.receiveAndConvert(queue.getName())); + + } + + @Test + public void testListenerSendsMessageAndThenRollback() throws Exception { + + ConnectionFactory connectionFactory = createConnectionFactory(); + RabbitTemplate template = new RabbitTemplate(connectionFactory); + new RabbitAdmin(connectionFactory).declareQueue(sendQueue); + + acknowledgeMode = AcknowledgeMode.AUTO; + transactional = true; + + CountDownLatch latch = new CountDownLatch(1); + container = createContainer(queue.getName(), new ChannelSenderListener(sendQueue.getName(), latch, true), + connectionFactory); + template.convertAndSend(queue.getName(), "foo"); + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + container.stop(); + Thread.sleep(200L); + + // Foo message is redelivered + assertEquals("foo", template.receiveAndConvert(queue.getName())); + // Sending of bar message is also rolled back + assertNull(template.receiveAndConvert(sendQueue.getName())); + + } + + @Test + public void testListenerRecoversFromBogusDoubleAck() throws Exception { + + RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); + + acknowledgeMode = AcknowledgeMode.MANUAL; + + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(queue.getName(), new ManualAckListener(latch), createConnectionFactory()); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + @Test + public void testListenerRecoversFromClosedChannel() throws Exception { + + RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); + + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(queue.getName(), new AbortChannelListener(latch), createConnectionFactory()); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + @Test + public void testListenerRecoversFromClosedChannelAndStop() throws Exception { + + RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); + + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(queue.getName(), new AbortChannelListener(latch), createConnectionFactory()); + int n = 0; + while (n++ < 100 && container.getActiveConsumerCount() != concurrentConsumers) { + Thread.sleep(50L); + } + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + assertEquals(concurrentConsumers, container.getActiveConsumerCount()); + container.stop(); + assertEquals(0, container.getActiveConsumerCount()); + + } + + @Test + public void testListenerRecoversFromClosedConnection() throws Exception { + + RabbitTemplate template = new RabbitTemplate(createConnectionFactory()); + + CountDownLatch latch = new CountDownLatch(messageCount); + ConnectionFactory connectionFactory = createConnectionFactory(); + container = createContainer(queue.getName(), + new CloseConnectionListener((ConnectionProxy) connectionFactory.createConnection(), latch), + connectionFactory); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + @Test + public void testListenerRecoversAndTemplateSharesConnectionFactory() throws Exception { + + ConnectionFactory connectionFactory = createConnectionFactory(); + RabbitTemplate template = new RabbitTemplate(connectionFactory); + + acknowledgeMode = AcknowledgeMode.MANUAL; + + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(queue.getName(), new ManualAckListener(latch), connectionFactory); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + // Give the listener container a chance to steal the connection from the template + Thread.sleep(200); + } + + int timeout = getTimeout(); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + @Test(expected = AmqpIllegalStateException.class) + public void testListenerDoesNotRecoverFromMissingQueue() throws Exception { + concurrentConsumers = 3; + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer("nonexistent", new VanillaListener(latch), createConnectionFactory()); + } + + @Test(expected = AmqpIllegalStateException.class) + public void testSingleListenerDoesNotRecoverFromMissingQueue() throws Exception { + /* + * A single listener sometimes doesn't have time to attempt to start before we ask it if it has failed, so this + * is a good test of that potential bug. + */ + concurrentConsumers = 1; + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer("nonexistent", new VanillaListener(latch), createConnectionFactory()); + } + + private int getTimeout() { + return Math.min(1 + messageCount / (concurrentConsumers), 30); + } + + private SimpleMessageListenerContainer createContainer(String queueName, Object listener, + ConnectionFactory connectionFactory) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setQueueNames(queueName); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(acknowledgeMode); + container.afterPropertiesSet(); + container.start(); + return container; + } + + public static class ManualAckListener implements ChannelAwareMessageListener { + + private AtomicBoolean failed = new AtomicBoolean(false); + + private final CountDownLatch latch; + + public ManualAckListener(CountDownLatch latch) { + this.latch = latch; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + try { + logger.debug("Acking: " + value); + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + if (failed.compareAndSet(false, true)) { + // intentional error (causes exception on connection thread): + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + } + } finally { + latch.countDown(); + } + } + } + + public static class ChannelSenderListener implements ChannelAwareMessageListener { + + private final CountDownLatch latch; + + private final boolean fail; + + private final String queueName; + + public ChannelSenderListener(String queueName, CountDownLatch latch, boolean fail) { + this.queueName = queueName; + this.latch = latch; + this.fail = fail; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + try { + logger.debug("Received: " + value + " Sending: bar"); + channel.basicPublish("", queueName, null, "bar".getBytes()); + if (fail) { + logger.debug("Failing (planned)"); + // intentional error (causes exception on connection thread): + throw new RuntimeException("Planned"); + } + } finally { + latch.countDown(); + } + } + } + + public static class AbortChannelListener implements ChannelAwareMessageListener { + + private AtomicBoolean failed = new AtomicBoolean(false); + + private final CountDownLatch latch; + + public AbortChannelListener(CountDownLatch latch) { + this.latch = latch; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + logger.debug("Receiving: " + value); + if (failed.compareAndSet(false, true)) { + // intentional error (causes exception on connection thread): + channel.abort(); + } else { + latch.countDown(); + } + } + } + + public static class CloseConnectionListener implements ChannelAwareMessageListener { + + private AtomicBoolean failed = new AtomicBoolean(false); + + private final CountDownLatch latch; + + private final Connection connection; + + public CloseConnectionListener(ConnectionProxy connection, CountDownLatch latch) { + this.connection = connection.getTargetConnection(); + this.latch = latch; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + logger.debug("Receiving: " + value); + if (failed.compareAndSet(false, true)) { + // intentional error (causes exception on connection thread): + connection.close(); + } else { + latch.countDown(); + } + } + } +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryRepeatIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryRepeatIntegrationTests.java index 27afa96d1a..ef7beec755 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryRepeatIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoveryRepeatIntegrationTests.java @@ -1,169 +1,169 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; -import org.springframework.amqp.rabbit.test.RepeatProcessor; -import org.springframework.test.annotation.Repeat; - -import com.rabbitmq.client.Channel; - -/** - * Long-running test created to facilitate profiling of SimpleMessageListenerContainer. - * - * @author Dave Syer - * - */ -@Ignore -public class MessageListenerRecoveryRepeatIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerRecoveryRepeatIntegrationTests.class); - - private Queue queue = new Queue("test.queue"); - - private Queue sendQueue = new Queue("test.send"); - - private int concurrentConsumers = 1; - - private int messageCount = 2; - - private int txSize = 1; - - private boolean transactional = false; - - private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; - - private SimpleMessageListenerContainer container; - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue, sendQueue); - - @Rule - public RepeatProcessor repeatProcessor = new RepeatProcessor(); - - private CloseConnectionListener listener; - - private ConnectionFactory connectionFactory; - - @Before - public void init() { - if (!repeatProcessor.isInitialized()) { - logger.info("Initializing at start of test"); - connectionFactory = createConnectionFactory(); - listener = new CloseConnectionListener(); - container = createContainer(queue.getName(), listener, connectionFactory); - } - } - - @After - public void clear() throws Exception { - if (repeatProcessor.isFinalizing()) { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.info("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - } - } - - @Test - @Repeat(1000) - public void testListenerRecoversFromClosedConnection() throws Exception { - - // logger.info("Testing..."); - - RabbitTemplate template = new RabbitTemplate(connectionFactory); - CountDownLatch latch = new CountDownLatch(messageCount); - listener.setLatch(latch); - - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - - int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - - assertNull(template.receiveAndConvert(queue.getName())); - - } - - private ConnectionFactory createConnectionFactory() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - // connectionFactory.setPort(BrokerTestUtils.getTracerPort()); - connectionFactory.setPort(BrokerTestUtils.getPort()); - return connectionFactory; - } - - private SimpleMessageListenerContainer createContainer(String queueName, Object listener, - ConnectionFactory connectionFactory) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setQueueNames(queueName); - container.setTxSize(txSize); - container.setPrefetchCount(txSize); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(acknowledgeMode); - container.setTaskExecutor(Executors.newFixedThreadPool(concurrentConsumers)); - container.afterPropertiesSet(); - container.start(); - return container; - } - - private static class CloseConnectionListener implements ChannelAwareMessageListener { - - private AtomicBoolean failed = new AtomicBoolean(false); - - private CountDownLatch latch; - - public void setLatch(CountDownLatch latch) { - this.latch = latch; - failed.set(false); - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - logger.info("Receiving: " + value); - if (failed.compareAndSet(false, true)) { - // intentional error (causes exception on connection thread): - // channel.abort(); - // throw new RuntimeException("Planned"); - throw new FatalListenerExecutionException("Planned"); - } else { - latch.countDown(); - } - } - } -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; +import org.springframework.amqp.rabbit.test.RepeatProcessor; +import org.springframework.test.annotation.Repeat; + +import com.rabbitmq.client.Channel; + +/** + * Long-running test created to facilitate profiling of SimpleMessageListenerContainer. + * + * @author Dave Syer + * + */ +@Ignore +public class MessageListenerRecoveryRepeatIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerRecoveryRepeatIntegrationTests.class); + + private Queue queue = new Queue("test.queue"); + + private Queue sendQueue = new Queue("test.send"); + + private int concurrentConsumers = 1; + + private int messageCount = 2; + + private int txSize = 1; + + private boolean transactional = false; + + private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; + + private SimpleMessageListenerContainer container; + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.INFO, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue, sendQueue); + + @Rule + public RepeatProcessor repeatProcessor = new RepeatProcessor(); + + private CloseConnectionListener listener; + + private ConnectionFactory connectionFactory; + + @Before + public void init() { + if (!repeatProcessor.isInitialized()) { + logger.info("Initializing at start of test"); + connectionFactory = createConnectionFactory(); + listener = new CloseConnectionListener(); + container = createContainer(queue.getName(), listener, connectionFactory); + } + } + + @After + public void clear() throws Exception { + if (repeatProcessor.isFinalizing()) { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.info("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + } + } + + @Test + @Repeat(1000) + public void testListenerRecoversFromClosedConnection() throws Exception { + + // logger.info("Testing..."); + + RabbitTemplate template = new RabbitTemplate(connectionFactory); + CountDownLatch latch = new CountDownLatch(messageCount); + listener.setLatch(latch); + + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + + int timeout = Math.min(4 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + + assertNull(template.receiveAndConvert(queue.getName())); + + } + + private ConnectionFactory createConnectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + // connectionFactory.setPort(BrokerTestUtils.getTracerPort()); + connectionFactory.setPort(BrokerTestUtils.getPort()); + return connectionFactory; + } + + private SimpleMessageListenerContainer createContainer(String queueName, Object listener, + ConnectionFactory connectionFactory) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setQueueNames(queueName); + container.setTxSize(txSize); + container.setPrefetchCount(txSize); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(acknowledgeMode); + container.setTaskExecutor(Executors.newFixedThreadPool(concurrentConsumers)); + container.afterPropertiesSet(); + container.start(); + return container; + } + + private static class CloseConnectionListener implements ChannelAwareMessageListener { + + private AtomicBoolean failed = new AtomicBoolean(false); + + private CountDownLatch latch; + + public void setLatch(CountDownLatch latch) { + this.latch = latch; + failed.set(false); + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + logger.info("Receiving: " + value); + if (failed.compareAndSet(false, true)) { + // intentional error (causes exception on connection thread): + // channel.abort(); + // throw new RuntimeException("Planned"); + throw new FatalListenerExecutionException("Planned"); + } else { + latch.countDown(); + } + } + } +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoverySingleConnectionIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoverySingleConnectionIntegrationTests.java index c6a97716cb..1c24c794a0 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoverySingleConnectionIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerRecoverySingleConnectionIntegrationTests.java @@ -1,15 +1,15 @@ -package org.springframework.amqp.rabbit.listener; - -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.SingleConnectionFactory; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; - -public class MessageListenerRecoverySingleConnectionIntegrationTests extends MessageListenerRecoveryCachingConnectionIntegrationTests { - - protected ConnectionFactory createConnectionFactory() { - SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); - connectionFactory.setPort(BrokerTestUtils.getPort()); - return connectionFactory; - } - -} +package org.springframework.amqp.rabbit.listener; + +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.SingleConnectionFactory; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; + +public class MessageListenerRecoverySingleConnectionIntegrationTests extends MessageListenerRecoveryCachingConnectionIntegrationTests { + + protected ConnectionFactory createConnectionFactory() { + SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); + connectionFactory.setPort(BrokerTestUtils.getPort()); + return connectionFactory; + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerTxSizeIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerTxSizeIntegrationTests.java index 3a0d63ec7e..b0f23ff551 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerTxSizeIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/MessageListenerTxSizeIntegrationTests.java @@ -1,152 +1,152 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; - -import com.rabbitmq.client.Channel; - -public class MessageListenerTxSizeIntegrationTests { - - private static Log logger = LogFactory.getLog(MessageListenerTxSizeIntegrationTests.class); - - private Queue queue = new Queue("test.queue"); - - private RabbitTemplate template = new RabbitTemplate(); - - private int concurrentConsumers = 1; - - private int messageCount = 12; - - private int txSize = 4; - - private boolean transactional = true; - - private SimpleMessageListenerContainer container; - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - @Before - public void createConnectionFactory() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - } - - @After - public void clear() throws Exception { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.debug("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - } - - @Test - public void testListenerTransactionalSunnyDay() throws Exception { - transactional = true; - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(new TestListener(latch, false)); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - assertNull(template.receiveAndConvert(queue.getName())); - } - - @Test - public void testListenerTransactionalFails() throws Exception { - transactional = true; - CountDownLatch latch = new CountDownLatch(messageCount); - container = createContainer(new TestListener(latch, true)); - for (int i = 0; i < txSize; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); - logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); - boolean waited = latch.await(timeout, TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - assertNull(template.receiveAndConvert(queue.getName())); - } - - private SimpleMessageListenerContainer createContainer(Object listener) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); - container.setMessageListener(new MessageListenerAdapter(listener)); - container.setQueueNames(queue.getName()); - container.setTxSize(txSize); - container.setPrefetchCount(txSize); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(AcknowledgeMode.AUTO); - container.afterPropertiesSet(); - container.start(); - return container; - } - - public class TestListener implements ChannelAwareMessageListener { - - private ThreadLocal count = new ThreadLocal(); - - private final CountDownLatch latch; - - private final boolean fail; - - public TestListener(CountDownLatch latch, boolean fail) { - this.latch = latch; - this.fail = fail; - } - - public void handleMessage(String value) { - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - try { - logger.debug("Received: " + value); - if (count.get()==null) { - count.set(1); - } else { - count.set(count.get()+1); - } - if (count.get()==txSize && fail) { - logger.debug("Failing: " + value); - count.set(0); - throw new RuntimeException("Planned"); - } - } finally { - latch.countDown(); - } - } - } - -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; + +import com.rabbitmq.client.Channel; + +public class MessageListenerTxSizeIntegrationTests { + + private static Log logger = LogFactory.getLog(MessageListenerTxSizeIntegrationTests.class); + + private Queue queue = new Queue("test.queue"); + + private RabbitTemplate template = new RabbitTemplate(); + + private int concurrentConsumers = 1; + + private int messageCount = 12; + + private int txSize = 4; + + private boolean transactional = true; + + private SimpleMessageListenerContainer container; + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.DEBUG, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + @Before + public void createConnectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + } + + @After + public void clear() throws Exception { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.debug("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + } + + @Test + public void testListenerTransactionalSunnyDay() throws Exception { + transactional = true; + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(new TestListener(latch, false)); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + assertNull(template.receiveAndConvert(queue.getName())); + } + + @Test + public void testListenerTransactionalFails() throws Exception { + transactional = true; + CountDownLatch latch = new CountDownLatch(messageCount); + container = createContainer(new TestListener(latch, true)); + for (int i = 0; i < txSize; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + int timeout = Math.min(1 + messageCount / (4 * concurrentConsumers), 30); + logger.debug("Waiting for messages with timeout = " + timeout + " (s)"); + boolean waited = latch.await(timeout, TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + assertNull(template.receiveAndConvert(queue.getName())); + } + + private SimpleMessageListenerContainer createContainer(Object listener) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); + container.setMessageListener(new MessageListenerAdapter(listener)); + container.setQueueNames(queue.getName()); + container.setTxSize(txSize); + container.setPrefetchCount(txSize); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(AcknowledgeMode.AUTO); + container.afterPropertiesSet(); + container.start(); + return container; + } + + public class TestListener implements ChannelAwareMessageListener { + + private ThreadLocal count = new ThreadLocal(); + + private final CountDownLatch latch; + + private final boolean fail; + + public TestListener(CountDownLatch latch, boolean fail) { + this.latch = latch; + this.fail = fail; + } + + public void handleMessage(String value) { + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + try { + logger.debug("Received: " + value); + if (count.get()==null) { + count.set(1); + } else { + count.set(count.get()+1); + } + if (count.get()==txSize && fail) { + logger.debug("Failing: " + value); + count.set(0); + throw new RuntimeException("Planned"); + } + } finally { + latch.countDown(); + } + } + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerIntegrationTests.java index 33970999ad..cf72fdbbeb 100755 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainerIntegrationTests.java @@ -1,354 +1,354 @@ -package org.springframework.amqp.rabbit.listener; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageListener; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.rabbit.test.BrokerRunning; -import org.springframework.amqp.rabbit.test.BrokerTestUtils; -import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionException; -import org.springframework.transaction.support.AbstractPlatformTransactionManager; -import org.springframework.transaction.support.DefaultTransactionStatus; - -import com.rabbitmq.client.Channel; - -@RunWith(Parameterized.class) -public class SimpleMessageListenerContainerIntegrationTests { - - private static Log logger = LogFactory.getLog(SimpleMessageListenerContainerIntegrationTests.class); - - private Queue queue = new Queue("test.queue"); - - private RabbitTemplate template = new RabbitTemplate(); - - private final int concurrentConsumers; - - private final AcknowledgeMode acknowledgeMode; - - @Rule - public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.OFF, RabbitTemplate.class, - SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, CachingConnectionFactory.class); - - @Rule - public Log4jLevelAdjuster testLogLevels = new Log4jLevelAdjuster(Level.DEBUG, - SimpleMessageListenerContainerIntegrationTests.class); - - @Rule - public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); - - private final int messageCount; - - private SimpleMessageListenerContainer container; - - private final int txSize; - - private final boolean externalTransaction; - - private final boolean transactional; - - public SimpleMessageListenerContainerIntegrationTests(int messageCount, int concurrency, - AcknowledgeMode acknowledgeMode, boolean transactional, int txSize, boolean externalTransaction) { - this.messageCount = messageCount; - this.concurrentConsumers = concurrency; - this.acknowledgeMode = acknowledgeMode; - this.transactional = transactional; - this.txSize = txSize; - this.externalTransaction = externalTransaction; - } - - @Parameters - public static List getParameters() { - return Arrays.asList( // - params(0, 1, 1, AcknowledgeMode.AUTO), // - params(1, 1, 1, AcknowledgeMode.NONE), // - params(2, 4, 1, AcknowledgeMode.AUTO), // - extern(3, 4, 1, AcknowledgeMode.AUTO), // - params(4, 4, 1, AcknowledgeMode.AUTO, false), // - params(5, 2, 2, AcknowledgeMode.AUTO), // - params(6, 2, 2, AcknowledgeMode.NONE), // - params(7, 20, 4, AcknowledgeMode.AUTO), // - params(8, 20, 4, AcknowledgeMode.NONE), // - params(9, 300, 4, AcknowledgeMode.AUTO), // - params(10, 300, 4, AcknowledgeMode.NONE), // - params(11, 300, 4, AcknowledgeMode.AUTO, 10) // - ); - } - - private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, - boolean transactional, int txSize) { - // "i" is just a counter to make it easier to identify the test in the log - return new Object[] { messageCount, concurrency, acknowledgeMode, transactional, txSize, false }; - } - - private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, int txSize) { - // For this test always us a transaction if it makes sense... - return params(i, messageCount, concurrency, acknowledgeMode, acknowledgeMode.isTransactionAllowed(), txSize); - } - - private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, - boolean transactional) { - return params(i, messageCount, concurrency, acknowledgeMode, transactional, 1); - } - - private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode) { - return params(i, messageCount, concurrency, acknowledgeMode, 1); - } - - private static Object[] extern(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode) { - return new Object[] { messageCount, concurrency, acknowledgeMode, true, 1, true }; - } - - @Before - public void declareQueue() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setChannelCacheSize(concurrentConsumers); - connectionFactory.setPort(BrokerTestUtils.getPort()); - template.setConnectionFactory(connectionFactory); - } - - @After - public void clear() throws Exception { - // Wait for broker communication to finish before trying to stop container - Thread.sleep(300L); - logger.debug("Shutting down at end of test"); - if (container != null) { - container.shutdown(); - } - } - - @Test - public void testPojoListenerSunnyDay() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doSunnyDayTest(latch, new MessageListenerAdapter(new PojoListener(latch))); - } - - @Test - public void testListenerSunnyDay() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doSunnyDayTest(latch, new Listener(latch)); - } - - @Test - public void testChannelAwareListenerSunnyDay() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doSunnyDayTest(latch, new ChannelAwareListener(latch)); - } - - @Test - public void testPojoListenerWithException() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doListenerWithExceptionTest(latch, new MessageListenerAdapter(new PojoListener(latch, true))); - } - - @Test - public void testListenerWithException() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doListenerWithExceptionTest(latch, new Listener(latch, true)); - } - - @Test - public void testChannelAwareListenerWithException() throws Exception { - CountDownLatch latch = new CountDownLatch(messageCount); - doListenerWithExceptionTest(latch, new ChannelAwareListener(latch, true)); - } - - private void doSunnyDayTest(CountDownLatch latch, Object listener) throws Exception { - container = createContainer(listener); - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - boolean waited = latch.await(Math.max(2, messageCount / 20), TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - assertNull(template.receiveAndConvert(queue.getName())); - } - - private void doListenerWithExceptionTest(CountDownLatch latch, Object listener) throws Exception { - container = createContainer(listener); - if (acknowledgeMode.isTransactionAllowed()) { - // Should only need one message if it is going to fail - for (int i = 0; i < concurrentConsumers; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - } else { - for (int i = 0; i < messageCount; i++) { - template.convertAndSend(queue.getName(), i + "foo"); - } - } - try { - boolean waited = latch.await(5 + Math.max(1, messageCount / 10), TimeUnit.SECONDS); - assertTrue("Timed out waiting for message", waited); - } finally { - // Wait for broker communication to finish before trying to stop - // container - Thread.sleep(300L); - container.shutdown(); - Thread.sleep(300L); - } - if (acknowledgeMode.isTransactionAllowed()) { - assertNotNull(template.receiveAndConvert(queue.getName())); - } else { - assertNull(template.receiveAndConvert(queue.getName())); - } - } - - private SimpleMessageListenerContainer createContainer(Object listener) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); - container.setMessageListener(listener); - container.setQueueNames(queue.getName()); - container.setTxSize(txSize); - container.setPrefetchCount(txSize); - container.setConcurrentConsumers(concurrentConsumers); - container.setChannelTransacted(transactional); - container.setAcknowledgeMode(acknowledgeMode); - if (externalTransaction) { - container.setTransactionManager(new TestTransactionManager()); - } - container.afterPropertiesSet(); - container.start(); - return container; - } - - public static class PojoListener { - private AtomicInteger count = new AtomicInteger(); - - private final CountDownLatch latch; - - private final boolean fail; - - public PojoListener(CountDownLatch latch) { - this(latch, false); - } - - public PojoListener(CountDownLatch latch, boolean fail) { - this.latch = latch; - this.fail = fail; - } - - public void handleMessage(String value) { - try { - int counter = count.getAndIncrement(); - if (logger.isDebugEnabled() && counter % 100 == 0) { - logger.debug("Handling: " + value + ":" + counter + " - " + latch); - } - if (fail) { - throw new RuntimeException("Planned failure"); - } - } finally { - latch.countDown(); - } - } - } - - public static class Listener implements MessageListener { - private AtomicInteger count = new AtomicInteger(); - - private final CountDownLatch latch; - - private final boolean fail; - - public Listener(CountDownLatch latch) { - this(latch, false); - } - - public Listener(CountDownLatch latch, boolean fail) { - this.latch = latch; - this.fail = fail; - } - - public void onMessage(Message message) { - String value = new String(message.getBody()); - try { - int counter = count.getAndIncrement(); - if (logger.isDebugEnabled() && counter % 100 == 0) { - logger.debug(value + counter); - } - if (fail) { - throw new RuntimeException("Planned failure"); - } - } finally { - latch.countDown(); - } - } - } - - public static class ChannelAwareListener implements ChannelAwareMessageListener { - private AtomicInteger count = new AtomicInteger(); - - private final CountDownLatch latch; - - private final boolean fail; - - public ChannelAwareListener(CountDownLatch latch) { - this(latch, false); - } - - public ChannelAwareListener(CountDownLatch latch, boolean fail) { - this.latch = latch; - this.fail = fail; - } - - public void onMessage(Message message, Channel channel) throws Exception { - String value = new String(message.getBody()); - try { - int counter = count.getAndIncrement(); - if (logger.isDebugEnabled() && counter % 100 == 0) { - logger.debug(value + counter); - } - if (fail) { - throw new RuntimeException("Planned failure"); - } - } finally { - latch.countDown(); - } - } - - } - - @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 { - } - - } -} +package org.springframework.amqp.rabbit.listener; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.springframework.amqp.core.AcknowledgeMode; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.amqp.rabbit.test.BrokerRunning; +import org.springframework.amqp.rabbit.test.BrokerTestUtils; +import org.springframework.amqp.rabbit.test.Log4jLevelAdjuster; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.AbstractPlatformTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; + +import com.rabbitmq.client.Channel; + +@RunWith(Parameterized.class) +public class SimpleMessageListenerContainerIntegrationTests { + + private static Log logger = LogFactory.getLog(SimpleMessageListenerContainerIntegrationTests.class); + + private Queue queue = new Queue("test.queue"); + + private RabbitTemplate template = new RabbitTemplate(); + + private final int concurrentConsumers; + + private final AcknowledgeMode acknowledgeMode; + + @Rule + public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster(Level.OFF, RabbitTemplate.class, + SimpleMessageListenerContainer.class, BlockingQueueConsumer.class, CachingConnectionFactory.class); + + @Rule + public Log4jLevelAdjuster testLogLevels = new Log4jLevelAdjuster(Level.DEBUG, + SimpleMessageListenerContainerIntegrationTests.class); + + @Rule + public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue); + + private final int messageCount; + + private SimpleMessageListenerContainer container; + + private final int txSize; + + private final boolean externalTransaction; + + private final boolean transactional; + + public SimpleMessageListenerContainerIntegrationTests(int messageCount, int concurrency, + AcknowledgeMode acknowledgeMode, boolean transactional, int txSize, boolean externalTransaction) { + this.messageCount = messageCount; + this.concurrentConsumers = concurrency; + this.acknowledgeMode = acknowledgeMode; + this.transactional = transactional; + this.txSize = txSize; + this.externalTransaction = externalTransaction; + } + + @Parameters + public static List getParameters() { + return Arrays.asList( // + params(0, 1, 1, AcknowledgeMode.AUTO), // + params(1, 1, 1, AcknowledgeMode.NONE), // + params(2, 4, 1, AcknowledgeMode.AUTO), // + extern(3, 4, 1, AcknowledgeMode.AUTO), // + params(4, 4, 1, AcknowledgeMode.AUTO, false), // + params(5, 2, 2, AcknowledgeMode.AUTO), // + params(6, 2, 2, AcknowledgeMode.NONE), // + params(7, 20, 4, AcknowledgeMode.AUTO), // + params(8, 20, 4, AcknowledgeMode.NONE), // + params(9, 300, 4, AcknowledgeMode.AUTO), // + params(10, 300, 4, AcknowledgeMode.NONE), // + params(11, 300, 4, AcknowledgeMode.AUTO, 10) // + ); + } + + private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, + boolean transactional, int txSize) { + // "i" is just a counter to make it easier to identify the test in the log + return new Object[] { messageCount, concurrency, acknowledgeMode, transactional, txSize, false }; + } + + private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, int txSize) { + // For this test always us a transaction if it makes sense... + return params(i, messageCount, concurrency, acknowledgeMode, acknowledgeMode.isTransactionAllowed(), txSize); + } + + private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode, + boolean transactional) { + return params(i, messageCount, concurrency, acknowledgeMode, transactional, 1); + } + + private static Object[] params(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode) { + return params(i, messageCount, concurrency, acknowledgeMode, 1); + } + + private static Object[] extern(int i, int messageCount, int concurrency, AcknowledgeMode acknowledgeMode) { + return new Object[] { messageCount, concurrency, acknowledgeMode, true, 1, true }; + } + + @Before + public void declareQueue() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setChannelCacheSize(concurrentConsumers); + connectionFactory.setPort(BrokerTestUtils.getPort()); + template.setConnectionFactory(connectionFactory); + } + + @After + public void clear() throws Exception { + // Wait for broker communication to finish before trying to stop container + Thread.sleep(300L); + logger.debug("Shutting down at end of test"); + if (container != null) { + container.shutdown(); + } + } + + @Test + public void testPojoListenerSunnyDay() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doSunnyDayTest(latch, new MessageListenerAdapter(new PojoListener(latch))); + } + + @Test + public void testListenerSunnyDay() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doSunnyDayTest(latch, new Listener(latch)); + } + + @Test + public void testChannelAwareListenerSunnyDay() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doSunnyDayTest(latch, new ChannelAwareListener(latch)); + } + + @Test + public void testPojoListenerWithException() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doListenerWithExceptionTest(latch, new MessageListenerAdapter(new PojoListener(latch, true))); + } + + @Test + public void testListenerWithException() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doListenerWithExceptionTest(latch, new Listener(latch, true)); + } + + @Test + public void testChannelAwareListenerWithException() throws Exception { + CountDownLatch latch = new CountDownLatch(messageCount); + doListenerWithExceptionTest(latch, new ChannelAwareListener(latch, true)); + } + + private void doSunnyDayTest(CountDownLatch latch, Object listener) throws Exception { + container = createContainer(listener); + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + boolean waited = latch.await(Math.max(2, messageCount / 20), TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + assertNull(template.receiveAndConvert(queue.getName())); + } + + private void doListenerWithExceptionTest(CountDownLatch latch, Object listener) throws Exception { + container = createContainer(listener); + if (acknowledgeMode.isTransactionAllowed()) { + // Should only need one message if it is going to fail + for (int i = 0; i < concurrentConsumers; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + } else { + for (int i = 0; i < messageCount; i++) { + template.convertAndSend(queue.getName(), i + "foo"); + } + } + try { + boolean waited = latch.await(5 + Math.max(1, messageCount / 10), TimeUnit.SECONDS); + assertTrue("Timed out waiting for message", waited); + } finally { + // Wait for broker communication to finish before trying to stop + // container + Thread.sleep(300L); + container.shutdown(); + Thread.sleep(300L); + } + if (acknowledgeMode.isTransactionAllowed()) { + assertNotNull(template.receiveAndConvert(queue.getName())); + } else { + assertNull(template.receiveAndConvert(queue.getName())); + } + } + + private SimpleMessageListenerContainer createContainer(Object listener) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); + container.setMessageListener(listener); + container.setQueueNames(queue.getName()); + container.setTxSize(txSize); + container.setPrefetchCount(txSize); + container.setConcurrentConsumers(concurrentConsumers); + container.setChannelTransacted(transactional); + container.setAcknowledgeMode(acknowledgeMode); + if (externalTransaction) { + container.setTransactionManager(new TestTransactionManager()); + } + container.afterPropertiesSet(); + container.start(); + return container; + } + + public static class PojoListener { + private AtomicInteger count = new AtomicInteger(); + + private final CountDownLatch latch; + + private final boolean fail; + + public PojoListener(CountDownLatch latch) { + this(latch, false); + } + + public PojoListener(CountDownLatch latch, boolean fail) { + this.latch = latch; + this.fail = fail; + } + + public void handleMessage(String value) { + try { + int counter = count.getAndIncrement(); + if (logger.isDebugEnabled() && counter % 100 == 0) { + logger.debug("Handling: " + value + ":" + counter + " - " + latch); + } + if (fail) { + throw new RuntimeException("Planned failure"); + } + } finally { + latch.countDown(); + } + } + } + + public static class Listener implements MessageListener { + private AtomicInteger count = new AtomicInteger(); + + private final CountDownLatch latch; + + private final boolean fail; + + public Listener(CountDownLatch latch) { + this(latch, false); + } + + public Listener(CountDownLatch latch, boolean fail) { + this.latch = latch; + this.fail = fail; + } + + public void onMessage(Message message) { + String value = new String(message.getBody()); + try { + int counter = count.getAndIncrement(); + if (logger.isDebugEnabled() && counter % 100 == 0) { + logger.debug(value + counter); + } + if (fail) { + throw new RuntimeException("Planned failure"); + } + } finally { + latch.countDown(); + } + } + } + + public static class ChannelAwareListener implements ChannelAwareMessageListener { + private AtomicInteger count = new AtomicInteger(); + + private final CountDownLatch latch; + + private final boolean fail; + + public ChannelAwareListener(CountDownLatch latch) { + this(latch, false); + } + + public ChannelAwareListener(CountDownLatch latch, boolean fail) { + this.latch = latch; + this.fail = fail; + } + + public void onMessage(Message message, Channel channel) throws Exception { + String value = new String(message.getBody()); + try { + int counter = count.getAndIncrement(); + if (logger.isDebugEnabled() && counter % 100 == 0) { + logger.debug(value + counter); + } + if (fail) { + throw new RuntimeException("Planned failure"); + } + } finally { + latch.countDown(); + } + } + + } + + @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 { + } + + } +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/BrokerPanic.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/BrokerPanic.java index 823ca501bb..a281aa3c29 100755 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/BrokerPanic.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/BrokerPanic.java @@ -1,51 +1,51 @@ -/* - * Copyright 2002-2011 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.test; - -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; -import org.springframework.amqp.rabbit.admin.RabbitBrokerAdmin; - -public class BrokerPanic implements MethodRule { - - private RabbitBrokerAdmin brokerAdmin; - - /** - * @param brokerAdmin the brokerAdmin to set - */ - public void setBrokerAdmin(RabbitBrokerAdmin brokerAdmin) { - this.brokerAdmin = brokerAdmin; - } - - public Statement apply(final Statement base, final FrameworkMethod method, Object target) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - base.evaluate(); - } catch (Throwable t) { - if (brokerAdmin != null) { - try { - brokerAdmin.stopNode(); - } catch (Throwable e) { - // don't hide original error (so ignored) - } - } - throw t; - } - } - }; - } - -} +/* + * Copyright 2002-2011 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.test; + +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; +import org.springframework.amqp.rabbit.admin.RabbitBrokerAdmin; + +public class BrokerPanic implements MethodRule { + + private RabbitBrokerAdmin brokerAdmin; + + /** + * @param brokerAdmin the brokerAdmin to set + */ + public void setBrokerAdmin(RabbitBrokerAdmin brokerAdmin) { + this.brokerAdmin = brokerAdmin; + } + + public Statement apply(final Statement base, final FrameworkMethod method, Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } catch (Throwable t) { + if (brokerAdmin != null) { + try { + brokerAdmin.stopNode(); + } catch (Throwable e) { + // don't hide original error (so ignored) + } + } + throw t; + } + } + }; + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/EnvironmentAvailable.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/EnvironmentAvailable.java index 1aa91b0a2b..e9f16541f7 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/EnvironmentAvailable.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/EnvironmentAvailable.java @@ -1,53 +1,53 @@ -/* - * Copyright 2002-2011 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.test; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.Assume; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -public class EnvironmentAvailable implements MethodRule { - - private static Log logger = LogFactory.getLog(EnvironmentAvailable.class); - - private static final String DEFAULT_ENVIRONMENT_KEY = "ENVIRONMENT"; - - private final String key; - - public EnvironmentAvailable(String key) { - this.key = key; - } - - public EnvironmentAvailable() { - this(DEFAULT_ENVIRONMENT_KEY); - } - - public Statement apply(final Statement base, final FrameworkMethod method, Object target) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - logger.info("Evironment: " + key + " active=" + isActive()); - Assume.assumeTrue(isActive()); - base.evaluate(); - } - }; - } - - public boolean isActive() { - return System.getProperty(key) != null; - } - -} +/* + * Copyright 2002-2011 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.test; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assume; +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +public class EnvironmentAvailable implements MethodRule { + + private static Log logger = LogFactory.getLog(EnvironmentAvailable.class); + + private static final String DEFAULT_ENVIRONMENT_KEY = "ENVIRONMENT"; + + private final String key; + + public EnvironmentAvailable(String key) { + this.key = key; + } + + public EnvironmentAvailable() { + this(DEFAULT_ENVIRONMENT_KEY); + } + + public Statement apply(final Statement base, final FrameworkMethod method, Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + logger.info("Evironment: " + key + " active=" + isActive()); + Assume.assumeTrue(isActive()); + base.evaluate(); + } + }; + } + + public boolean isActive() { + return System.getProperty(key) != null; + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/Log4jLevelAdjuster.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/Log4jLevelAdjuster.java index 4658cc668b..076e693ab2 100755 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/Log4jLevelAdjuster.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/Log4jLevelAdjuster.java @@ -1,59 +1,59 @@ -package org.springframework.amqp.rabbit.test; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -/** - * A JUnit method @Rule that changes the logger level for a set of classes - * while a test method is running. Useful for performance or scalability tests - * where we don't want to generate a large log in a tight inner loop. - * - * @author Dave Syer - * - */ -public class Log4jLevelAdjuster implements MethodRule { - - private static final Log logger = LogFactory.getLog(Log4jLevelAdjuster.class); - - private final Class[] classes; - - private final Level level; - - public Log4jLevelAdjuster(Level level, Class... classes) { - this.level = level; - this.classes = classes; - } - - public Statement apply(final Statement base, FrameworkMethod method, Object target) { - return new Statement() { - public void evaluate() throws Throwable { - logger.debug("Overriding log level setting for: " + Arrays.asList(classes)); - Map, Level> oldLevels = new HashMap, Level>(); - for (Class cls : classes) { - oldLevels.put(cls, LogManager.getLogger(cls).getLevel()); - LogManager.getLogger(cls).setLevel(level); - } - try { - base.evaluate(); - } - finally { - logger.debug("Restoring log level setting for: " + Arrays.asList(classes)); - // raw Class type used to avoid http://bugs.sun.com/view_bug.do?bug_id=6682380 - for (@SuppressWarnings("rawtypes") Class cls : classes) { - LogManager.getLogger(cls).setLevel(oldLevels.get(cls)); - } - } - } - }; - } - -} +package org.springframework.amqp.rabbit.test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +/** + * A JUnit method @Rule that changes the logger level for a set of classes + * while a test method is running. Useful for performance or scalability tests + * where we don't want to generate a large log in a tight inner loop. + * + * @author Dave Syer + * + */ +public class Log4jLevelAdjuster implements MethodRule { + + private static final Log logger = LogFactory.getLog(Log4jLevelAdjuster.class); + + private final Class[] classes; + + private final Level level; + + public Log4jLevelAdjuster(Level level, Class... classes) { + this.level = level; + this.classes = classes; + } + + public Statement apply(final Statement base, FrameworkMethod method, Object target) { + return new Statement() { + public void evaluate() throws Throwable { + logger.debug("Overriding log level setting for: " + Arrays.asList(classes)); + Map, Level> oldLevels = new HashMap, Level>(); + for (Class cls : classes) { + oldLevels.put(cls, LogManager.getLogger(cls).getLevel()); + LogManager.getLogger(cls).setLevel(level); + } + try { + base.evaluate(); + } + finally { + logger.debug("Restoring log level setting for: " + Arrays.asList(classes)); + // raw Class type used to avoid http://bugs.sun.com/view_bug.do?bug_id=6682380 + for (@SuppressWarnings("rawtypes") Class cls : classes) { + LogManager.getLogger(cls).setLevel(oldLevels.get(cls)); + } + } + } + }; + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/RepeatProcessor.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/RepeatProcessor.java index b86fe26e78..f3b88afbbf 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/RepeatProcessor.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/test/RepeatProcessor.java @@ -1,184 +1,184 @@ -/* - * Copyright 2002-2010 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.test; - -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletionService; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.hamcrest.CoreMatchers; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.internal.runners.statements.RunAfters; -import org.junit.internal.runners.statements.RunBefores; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; -import org.junit.runners.model.TestClass; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.test.annotation.Repeat; - -/** - * A JUnit method @Rule that looks at Spring repeat annotations on methods and executes the test multiple times - * (without re-initializing the test case if necessary). To avoid re-initializing use the {@link #isInitialized()} - * method to protect the @Before and @After methods. - * - * @author Dave Syer - * @since 2.0 - * - */ -public class RepeatProcessor implements MethodRule { - - private static final Log logger = LogFactory.getLog(RepeatProcessor.class); - - private final int concurrency; - - private volatile boolean initialized = false; - - private volatile boolean finalizing = false; - - public RepeatProcessor() { - this(0); - } - - public RepeatProcessor(int concurrency) { - this.concurrency = concurrency < 0 ? 0 : concurrency; - } - - public Statement apply(final Statement base, FrameworkMethod method, final Object target) { - - Repeat repeat = AnnotationUtils.findAnnotation(method.getMethod(), Repeat.class); - if (repeat == null) { - return base; - } - - final int repeats = repeat.value(); - if (repeats <= 1) { - return base; - } - - initializeIfNecessary(target); - - if (concurrency <= 0) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - for (int i = 0; i < repeats; i++) { - try { - base.evaluate(); - } catch (Throwable t) { - throw new IllegalStateException("Failed on iteration: " + i + " of " + repeats + " (started at 0)", t); - } - } - } finally { - finalizeIfNecessary(target); - } - } - }; - } - return new Statement() { - @Override - public void evaluate() throws Throwable { - List> results = new ArrayList>(); - ExecutorService executor = Executors.newFixedThreadPool(concurrency); - CompletionService completionService = new ExecutorCompletionService(executor); - try { - for (int i = 0; i < repeats; i++) { - final int count = i; - results.add(completionService.submit(new Callable() { - public Boolean call() { - try { - base.evaluate(); - } catch (Throwable t) { - throw new IllegalStateException("Failed on iteration: " + count, t); - } - return true; - } - })); - } - for (int i = 0; i < repeats; i++) { - Future future = completionService.take(); - assertTrue("Null result from completer", future.get()); - } - } finally { - executor.shutdownNow(); - finalizeIfNecessary(target); - } - } - }; - } - - private void finalizeIfNecessary(Object target) { - finalizing = true; - List afters = new TestClass(target.getClass()).getAnnotatedMethods(After.class); - try { - if (!afters.isEmpty()) { - logger.debug("Running @After methods"); - try { - new RunAfters(new Statement() { - public void evaluate() { - } - }, afters, target).evaluate(); - } catch (Throwable e) { - Assert.assertThat(e, CoreMatchers.not(CoreMatchers.anything())); - } - } - } finally { - finalizing = false; - } - } - - private void initializeIfNecessary(Object target) { - TestClass testClass = new TestClass(target.getClass()); - List befores = testClass.getAnnotatedMethods(Before.class); - if (!befores.isEmpty()) { - logger.debug("Running @Before methods"); - try { - new RunBefores(new Statement() { - public void evaluate() { - } - }, befores, target).evaluate(); - } catch (Throwable e) { - Assert.assertThat(e, CoreMatchers.not(CoreMatchers.anything())); - } - initialized = true; - } - if (!testClass.getAnnotatedMethods(After.class).isEmpty()) { - initialized = true; - } - } - - public boolean isInitialized() { - return initialized; - } - - public boolean isFinalizing() { - return finalizing; - } - - public int getConcurrency() { - return concurrency > 0 ? concurrency : 1; - } +/* + * Copyright 2002-2010 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.test; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.internal.runners.statements.RunAfters; +import org.junit.internal.runners.statements.RunBefores; +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.test.annotation.Repeat; + +/** + * A JUnit method @Rule that looks at Spring repeat annotations on methods and executes the test multiple times + * (without re-initializing the test case if necessary). To avoid re-initializing use the {@link #isInitialized()} + * method to protect the @Before and @After methods. + * + * @author Dave Syer + * @since 2.0 + * + */ +public class RepeatProcessor implements MethodRule { + + private static final Log logger = LogFactory.getLog(RepeatProcessor.class); + + private final int concurrency; + + private volatile boolean initialized = false; + + private volatile boolean finalizing = false; + + public RepeatProcessor() { + this(0); + } + + public RepeatProcessor(int concurrency) { + this.concurrency = concurrency < 0 ? 0 : concurrency; + } + + public Statement apply(final Statement base, FrameworkMethod method, final Object target) { + + Repeat repeat = AnnotationUtils.findAnnotation(method.getMethod(), Repeat.class); + if (repeat == null) { + return base; + } + + final int repeats = repeat.value(); + if (repeats <= 1) { + return base; + } + + initializeIfNecessary(target); + + if (concurrency <= 0) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + for (int i = 0; i < repeats; i++) { + try { + base.evaluate(); + } catch (Throwable t) { + throw new IllegalStateException("Failed on iteration: " + i + " of " + repeats + " (started at 0)", t); + } + } + } finally { + finalizeIfNecessary(target); + } + } + }; + } + return new Statement() { + @Override + public void evaluate() throws Throwable { + List> results = new ArrayList>(); + ExecutorService executor = Executors.newFixedThreadPool(concurrency); + CompletionService completionService = new ExecutorCompletionService(executor); + try { + for (int i = 0; i < repeats; i++) { + final int count = i; + results.add(completionService.submit(new Callable() { + public Boolean call() { + try { + base.evaluate(); + } catch (Throwable t) { + throw new IllegalStateException("Failed on iteration: " + count, t); + } + return true; + } + })); + } + for (int i = 0; i < repeats; i++) { + Future future = completionService.take(); + assertTrue("Null result from completer", future.get()); + } + } finally { + executor.shutdownNow(); + finalizeIfNecessary(target); + } + } + }; + } + + private void finalizeIfNecessary(Object target) { + finalizing = true; + List afters = new TestClass(target.getClass()).getAnnotatedMethods(After.class); + try { + if (!afters.isEmpty()) { + logger.debug("Running @After methods"); + try { + new RunAfters(new Statement() { + public void evaluate() { + } + }, afters, target).evaluate(); + } catch (Throwable e) { + Assert.assertThat(e, CoreMatchers.not(CoreMatchers.anything())); + } + } + } finally { + finalizing = false; + } + } + + private void initializeIfNecessary(Object target) { + TestClass testClass = new TestClass(target.getClass()); + List befores = testClass.getAnnotatedMethods(Before.class); + if (!befores.isEmpty()) { + logger.debug("Running @Before methods"); + try { + new RunBefores(new Statement() { + public void evaluate() { + } + }, befores, target).evaluate(); + } catch (Throwable e) { + Assert.assertThat(e, CoreMatchers.not(CoreMatchers.anything())); + } + initialized = true; + } + if (!testClass.getAnnotatedMethods(After.class).isEmpty()) { + initialized = true; + } + } + + public boolean isInitialized() { + return initialized; + } + + public boolean isFinalizing() { + return finalizing; + } + + public int getConcurrency() { + return concurrency > 0 ? concurrency : 1; + } } \ No newline at end of file