Merge pull request #39 from garyrussell/AMQP-236
* AMQP-236: AMQP-236 Remove DOS Newlines
This commit is contained in:
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
package org.springframework.amqp.rabbit.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.junit.rules.MethodRule;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
/**
|
||||
* A JUnit method @Rule that changes the logger level for a set of classes
|
||||
* while a test method is running. Useful for performance or scalability tests
|
||||
* where we don't want to generate a large log in a tight inner loop.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class Log4jLevelAdjuster implements MethodRule {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(Log4jLevelAdjuster.class);
|
||||
|
||||
private final Class<?>[] classes;
|
||||
|
||||
private final Level level;
|
||||
|
||||
public Log4jLevelAdjuster(Level level, Class<?>... classes) {
|
||||
this.level = level;
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
public Statement apply(final Statement base, FrameworkMethod method, Object target) {
|
||||
return new Statement() {
|
||||
public void evaluate() throws Throwable {
|
||||
logger.debug("Overriding log level setting for: " + Arrays.asList(classes));
|
||||
Map<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 @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));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,184 +1,184 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.amqp.rabbit.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.internal.runners.statements.RunAfters;
|
||||
import org.junit.internal.runners.statements.RunBefores;
|
||||
import org.junit.rules.MethodRule;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.runners.model.TestClass;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
|
||||
/**
|
||||
* A JUnit method @Rule that looks at Spring repeat annotations on methods and executes the test multiple times
|
||||
* (without re-initializing the test case if necessary). To avoid re-initializing use the {@link #isInitialized()}
|
||||
* method to protect the @Before and @After methods.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public class RepeatProcessor implements MethodRule {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RepeatProcessor.class);
|
||||
|
||||
private final int concurrency;
|
||||
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
private volatile boolean finalizing = false;
|
||||
|
||||
public RepeatProcessor() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public RepeatProcessor(int concurrency) {
|
||||
this.concurrency = concurrency < 0 ? 0 : concurrency;
|
||||
}
|
||||
|
||||
public Statement apply(final Statement base, FrameworkMethod method, final Object target) {
|
||||
|
||||
Repeat repeat = AnnotationUtils.findAnnotation(method.getMethod(), Repeat.class);
|
||||
if (repeat == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
final int repeats = repeat.value();
|
||||
if (repeats <= 1) {
|
||||
return base;
|
||||
}
|
||||
|
||||
initializeIfNecessary(target);
|
||||
|
||||
if (concurrency <= 0) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
try {
|
||||
base.evaluate();
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalStateException("Failed on iteration: " + i + " of " + repeats + " (started at 0)", t);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
finalizeIfNecessary(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
List<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 @Rule that looks at Spring repeat annotations on methods and executes the test multiple times
|
||||
* (without re-initializing the test case if necessary). To avoid re-initializing use the {@link #isInitialized()}
|
||||
* method to protect the @Before and @After methods.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public class RepeatProcessor implements MethodRule {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RepeatProcessor.class);
|
||||
|
||||
private final int concurrency;
|
||||
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
private volatile boolean finalizing = false;
|
||||
|
||||
public RepeatProcessor() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public RepeatProcessor(int concurrency) {
|
||||
this.concurrency = concurrency < 0 ? 0 : concurrency;
|
||||
}
|
||||
|
||||
public Statement apply(final Statement base, FrameworkMethod method, final Object target) {
|
||||
|
||||
Repeat repeat = AnnotationUtils.findAnnotation(method.getMethod(), Repeat.class);
|
||||
if (repeat == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
final int repeats = repeat.value();
|
||||
if (repeats <= 1) {
|
||||
return base;
|
||||
}
|
||||
|
||||
initializeIfNecessary(target);
|
||||
|
||||
if (concurrency <= 0) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
try {
|
||||
base.evaluate();
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalStateException("Failed on iteration: " + i + " of " + repeats + " (started at 0)", t);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
finalizeIfNecessary(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
List<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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user