Merge pull request #39 from garyrussell/AMQP-236

* AMQP-236:
  AMQP-236 Remove DOS Newlines
This commit is contained in:
Oleg Zhurakousky
2012-05-24 10:48:09 -04:00
18 changed files with 2819 additions and 2819 deletions

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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<BlockingQueueConsumer>(),
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<BlockingQueueConsumer>(),
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()));
}
}

View File

@@ -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<QueueInfo> 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<QueueInfo> 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();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<Integer> count = new ThreadLocal<Integer>();
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<Integer> count = new ThreadLocal<Integer>();
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();
}
}
}
}

View File

@@ -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<Object[]> 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<Object[]> 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 {
}
}
}

View File

@@ -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;
}
}
};
}
}

View File

@@ -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;
}
}

View File

@@ -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 &#064;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<Class<?>, Level> oldLevels = new HashMap<Class<?>, 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 &#064;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<Class<?>, Level> oldLevels = new HashMap<Class<?>, 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));
}
}
}
};
}
}

View File

@@ -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 &#064;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 &#64;Before and &#64;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<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
ExecutorService executor = Executors.newFixedThreadPool(concurrency);
CompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(executor);
try {
for (int i = 0; i < repeats; i++) {
final int count = i;
results.add(completionService.submit(new Callable<Boolean>() {
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<Boolean> future = completionService.take();
assertTrue("Null result from completer", future.get());
}
} finally {
executor.shutdownNow();
finalizeIfNecessary(target);
}
}
};
}
private void finalizeIfNecessary(Object target) {
finalizing = true;
List<FrameworkMethod> 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<FrameworkMethod> 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 &#064;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 &#64;Before and &#64;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<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
ExecutorService executor = Executors.newFixedThreadPool(concurrency);
CompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(executor);
try {
for (int i = 0; i < repeats; i++) {
final int count = i;
results.add(completionService.submit(new Callable<Boolean>() {
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<Boolean> future = completionService.take();
assertTrue("Null result from completer", future.get());
}
} finally {
executor.shutdownNow();
finalizeIfNecessary(target);
}
}
};
}
private void finalizeIfNecessary(Object target) {
finalizing = true;
List<FrameworkMethod> 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<FrameworkMethod> 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;
}
}