/*
* Copyright 2016-2017 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.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.LogFactory;
import org.apache.logging.log4j.Level;
import org.junit.AfterClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.mockito.ArgumentCaptor;
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.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunning;
import org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer.ChannelHolder;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.rabbit.listener.adapter.ReplyingMessageListener;
import org.springframework.amqp.rabbit.support.ArgumentBuilder;
import org.springframework.amqp.rabbit.test.LogLevelAdjuster;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.utils.test.TestUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.MultiValueMap;
import org.springframework.util.backoff.FixedBackOff;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
/**
* @author Gary Russell
* @author Artem Bilan
* @author Alex Panchenko
*
* @since 2.0
*
*/
public class DirectMessageListenerContainerIntegrationTests {
private static final String Q1 = "testQ1";
private static final String Q2 = "testQ2";
private static final String EQ1 = "eventTestQ1";
private static final String EQ2 = "eventTestQ2";
private static final String DLQ1 = "testDLQ1";
@ClassRule
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(Q1, Q2, EQ1, EQ2, DLQ1);
private static CachingConnectionFactory adminCf =
new CachingConnectionFactory(brokerRunning.getConnectionFactory());
private static RabbitAdmin admin = new RabbitAdmin(adminCf);
@Rule
public LogLevelAdjuster adjuster = new LogLevelAdjuster(Level.DEBUG,
CachingConnectionFactory.class, DirectReplyToMessageListenerContainer.class,
DirectMessageListenerContainer.class, DirectMessageListenerContainerIntegrationTests.class,
BrokerRunning.class);
@Rule
public TestName testName = new TestName();
@AfterClass
public static void tearDown() {
brokerRunning.removeTestQueues();
adminCf.destroy();
}
@Test
public void testSimple() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("client-");
executor.afterPropertiesSet();
cf.setExecutor(executor);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setQueueNames(Q1, Q2);
container.setConsumersPerQueue(2);
container.setMessageListener(new MessageListenerAdapter((ReplyingMessageListener<String, String>) in -> {
if ("foo".equals(in) || "bar".equals(in)) {
return in.toUpperCase();
}
else {
return null;
}
}));
container.setBeanName("simple");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
RabbitTemplate template = new RabbitTemplate(cf);
assertEquals("FOO", template.convertSendAndReceive(Q1, "foo"));
assertEquals("BAR", template.convertSendAndReceive(Q2, "bar"));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
template.stop();
cf.destroy();
}
@Test
public void testAdvice() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setQueueNames(Q1, Q2);
container.setConsumersPerQueue(2);
final CountDownLatch latch = new CountDownLatch(2);
container.setMessageListener(m -> latch.countDown());
final CountDownLatch adviceLatch = new CountDownLatch(2);
MethodInterceptor advice = i -> {
adviceLatch.countDown();
return i.proceed();
};
container.setAdviceChain(advice);
container.setBeanName("advice");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
RabbitTemplate template = new RabbitTemplate(cf);
template.convertAndSend(Q1, "foo");
template.convertAndSend(Q1, "bar");
assertTrue(latch.await(10, TimeUnit.SECONDS));
assertTrue(adviceLatch.await(10, TimeUnit.SECONDS));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
cf.destroy();
}
@Test
public void testQueueManagement() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("client-");
executor.afterPropertiesSet();
cf.setExecutor(executor);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setConsumersPerQueue(2);
container.setMessageListener(new MessageListenerAdapter((ReplyingMessageListener<String, String>) in -> {
if ("foo".equals(in) || "bar".equals(in)) {
return in.toUpperCase();
}
else {
return null;
}
}));
container.setBeanName("qManage");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
container.addQueueNames(Q1, Q2);
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
RabbitTemplate template = new RabbitTemplate(cf);
assertEquals("FOO", template.convertSendAndReceive(Q1, "foo"));
assertEquals("BAR", template.convertSendAndReceive(Q2, "bar"));
container.removeQueueNames(Q1, Q2, "junk");
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
template.stop();
cf.destroy();
}
@Test
public void testQueueManagementQueueInstances() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("client-");
executor.afterPropertiesSet();
cf.setExecutor(executor);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setConsumersPerQueue(2);
container.setMessageListener(new MessageListenerAdapter((ReplyingMessageListener<String, String>) in -> {
if ("foo".equals(in) || "bar".equals(in)) {
return in.toUpperCase();
}
else {
return null;
}
}));
container.setBeanName("qManage");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.setQueues(new Queue(Q1));
assertArrayEquals(new String[] { Q1 }, container.getQueueNames());
container.start();
container.addQueues(new Queue(Q2));
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
RabbitTemplate template = new RabbitTemplate(cf);
assertEquals("FOO", template.convertSendAndReceive(Q1, "foo"));
assertEquals("BAR", template.convertSendAndReceive(Q2, "bar"));
container.removeQueues(new Queue(Q1), new Queue(Q2), new Queue("junk"));
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
template.stop();
cf.destroy();
}
@Test
public void testAddRemoveConsumers() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("client-");
executor.afterPropertiesSet();
cf.setExecutor(executor);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setQueueNames(Q1, Q2);
container.setConsumersPerQueue(2);
container.setMessageListener(new MessageListenerAdapter((ReplyingMessageListener<String, String>) in -> {
if ("foo".equals(in) || "bar".equals(in)) {
return in.toUpperCase();
}
else {
return null;
}
}));
container.setBeanName("qAddRemove");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
RabbitTemplate template = new RabbitTemplate(cf);
assertEquals("FOO", template.convertSendAndReceive(Q1, "foo"));
assertEquals("BAR", template.convertSendAndReceive(Q2, "bar"));
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
container.setConsumersPerQueue(1);
assertTrue(consumersOnQueue(Q1, 1));
assertTrue(consumersOnQueue(Q2, 1));
container.setConsumersPerQueue(2);
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
template.stop();
cf.destroy();
}
@Test
public void testEvents() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setQueueNames(EQ1, EQ2);
final List<Long> times = new ArrayList<>();
final CountDownLatch latch1 = new CountDownLatch(2);
final AtomicReference<ApplicationEvent> failEvent = new AtomicReference<>();
final CountDownLatch latch2 = new CountDownLatch(2);
container.setApplicationEventPublisher(event -> {
if (event instanceof ListenerContainerIdleEvent) {
times.add(System.currentTimeMillis());
latch1.countDown();
}
else {
failEvent.set((ApplicationEvent) event);
latch2.countDown();
}
});
container.setMessageListener(m -> { });
container.setIdleEventInterval(50L);
container.setBeanName("events");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
assertTrue(latch1.await(10, TimeUnit.SECONDS));
assertThat(times.get(1) - times.get(0), greaterThanOrEqualTo(50L));
brokerRunning.deleteQueues(EQ1, EQ2);
assertTrue(latch2.await(10, TimeUnit.SECONDS));
assertNotNull(failEvent.get());
assertThat(failEvent.get(), instanceOf(ListenerContainerConsumerTerminatedEvent.class));
container.stop();
cf.destroy();
}
@Test
public void testErrorHandler() throws Exception {
brokerRunning.deleteQueues(Q1);
Queue q1 = new Queue(Q1, true, false, false, new ArgumentBuilder()
.put("x-dead-letter-exchange", "")
.put("x-dead-letter-routing-key", DLQ1)
.get());
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
RabbitAdmin admin = new RabbitAdmin(cf);
admin.declareQueue(q1);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setQueueNames(Q1);
container.setConsumersPerQueue(2);
final AtomicReference<Channel> channel = new AtomicReference<>();
container.setMessageListener((ChannelAwareMessageListener) (m, c) -> {
channel.set(c);
throw new MessageConversionException("intended - should be wrapped in an AmqpRejectAndDontRequeueException");
});
container.setBeanName("errorHandler");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
RabbitTemplate template = new RabbitTemplate(cf);
template.convertAndSend(Q1, "foo");
assertNotNull(template.receive(DLQ1, 10000));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
assertFalse(channel.get().isOpen());
cf.destroy();
}
@Test
public void testContainerNotRecoveredAfterExhaustingRecoveryBackOff() throws Exception {
ConnectionFactory mockCF = mock(ConnectionFactory.class);
given(mockCF.createConnection()).willThrow(new RuntimeException("intended - backOff test"));
DirectMessageListenerContainer container = new DirectMessageListenerContainer(mockCF);
container.setQueueNames("foo");
container.setRecoveryBackOff(new FixedBackOff(100, 3));
container.setMissingQueuesFatal(false);
container.setBeanName("backOff");
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
// Since backOff exhausting makes listenerContainer as invalid (calls stop()),
// it is enough to check the listenerContainer activity
int n = 0;
while (container.isActive() && n++ < 100) {
Thread.sleep(100);
}
assertFalse(container.isActive());
}
@Test
public void testRecoverBrokerLoss() throws Exception {
ConnectionFactory mockCF = mock(ConnectionFactory.class);
Connection connection = mock(Connection.class);
Channel channel = mock(Channel.class);
given(connection.isOpen()).willReturn(true);
given(mockCF.createConnection()).willReturn(connection);
given(connection.createChannel(false)).willReturn(channel);
given(channel.isOpen()).willReturn(true);
final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(2);
ArgumentCaptor<Consumer> consumerCaptor = ArgumentCaptor.forClass(Consumer.class);
final String tag = "tag";
willAnswer(i -> {
latch1.countDown();
latch2.countDown();
return tag;
}).given(channel).basicConsume(eq("foo"), anyBoolean(), anyString(), anyBoolean(), anyBoolean(),
anyMap(), consumerCaptor.capture());
DirectMessageListenerContainer container = new DirectMessageListenerContainer(mockCF);
container.setQueueNames("foo");
container.setBeanName("brokerLost");
container.setConsumerTagStrategy(q -> "tag");
container.setShutdownTimeout(1);
container.setMonitorInterval(200);
container.setFailedDeclarationRetryInterval(200);
container.afterPropertiesSet();
container.start();
assertTrue(latch1.await(10, TimeUnit.SECONDS));
given(channel.isOpen()).willReturn(false);
assertTrue(latch2.await(10, TimeUnit.SECONDS));
container.setShutdownTimeout(1);
container.stop();
}
@Test
public void testCancelConsumerBeforeConsumeOk() throws Exception {
ConnectionFactory mockCF = mock(ConnectionFactory.class);
Connection connection = mock(Connection.class);
Channel channel = mock(Channel.class);
given(connection.isOpen()).willReturn(true);
given(mockCF.createConnection()).willReturn(connection);
given(connection.createChannel(false)).willReturn(channel);
given(channel.isOpen()).willReturn(true);
final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
ArgumentCaptor<Consumer> consumerCaptor = ArgumentCaptor.forClass(Consumer.class);
final String tag = "tag";
willAnswer(i -> {
latch1.countDown();
return tag;
}).given(channel).basicConsume(eq("foo"), anyBoolean(), anyString(), anyBoolean(), anyBoolean(),
anyMap(), consumerCaptor.capture());
DirectMessageListenerContainer container = new DirectMessageListenerContainer(mockCF);
container.setQueueNames("foo");
container.setBeanName("backOff");
container.setConsumerTagStrategy(q -> "tag");
container.setShutdownTimeout(1);
container.afterPropertiesSet();
container.start();
assertTrue(latch1.await(10, TimeUnit.SECONDS));
Consumer consumer = consumerCaptor.getValue();
Executors.newSingleThreadExecutor().execute(() -> {
container.stop();
latch2.countDown();
});
assertTrue(latch2.await(10, TimeUnit.SECONDS));
verify(channel).basicCancel(tag); // canceled properly even without consumeOk
consumer.handleCancelOk(tag);
}
@Test
public void testRecoverDeletedQueueAutoDeclare() throws Exception {
testRecoverDeletedQueueGuts(true);
}
@Test
public void testRecoverDeletedQueueNoAutoDeclare() throws Exception {
testRecoverDeletedQueueGuts(false);
}
private void testRecoverDeletedQueueGuts(boolean autoDeclare) throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
if (autoDeclare) {
GenericApplicationContext context = new GenericApplicationContext();
context.getBeanFactory().registerSingleton("foo", new Queue(Q1));
RabbitAdmin admin = new RabbitAdmin(cf);
admin.setApplicationContext(context);
context.getBeanFactory().registerSingleton("admin", admin);
context.refresh();
container.setApplicationContext(context);
}
container.setAutoDeclare(autoDeclare);
container.setQueueNames(Q1, Q2);
container.setConsumersPerQueue(2);
container.setConsumersPerQueue(2);
container.setMessageListener(m -> { });
container.setFailedDeclarationRetryInterval(500);
container.setBeanName("deleteQauto=" + autoDeclare);
container.setConsumerTagStrategy(new Tag());
container.afterPropertiesSet();
container.start();
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
assertTrue(activeConsumerCount(container, 4));
brokerRunning.deleteQueues(Q1);
assertTrue(consumersOnQueue(Q2, 2));
assertTrue(activeConsumerCount(container, 2));
assertTrue(restartConsumerCount(container, 2));
RabbitAdmin admin = new RabbitAdmin(cf);
if (!autoDeclare) {
Thread.sleep(2000);
admin.declareQueue(new Queue(Q1));
}
assertTrue(consumersOnQueue(Q1, 2));
assertTrue(consumersOnQueue(Q2, 2));
assertTrue(activeConsumerCount(container, 4));
container.stop();
assertTrue(consumersOnQueue(Q1, 0));
assertTrue(consumersOnQueue(Q2, 0));
assertTrue(activeConsumerCount(container, 0));
assertEquals(0, TestUtils.getPropertyValue(container, "consumersByQueue", MultiValueMap.class).size());
cf.destroy();
}
@Test
public void testReplyToReleaseWithCancel() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(cf);
container.setBeanName("releaseCancel");
container.afterPropertiesSet();
container.start();
ChannelHolder channelHolder = container.getChannelHolder();
final CountDownLatch latch = new CountDownLatch(1);
container.setApplicationEventPublisher(e -> {
if (e instanceof ListenerContainerConsumerTerminatedEvent) {
latch.countDown();
}
});
container.releaseConsumerFor(channelHolder, true, "foo");
assertTrue(latch.await(10, TimeUnit.SECONDS));
container.stop();
cf.destroy();
}
@Test
public void testReplyToConsumersReduced() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(cf);
container.setBeanName("reducing");
container.setIdleEventInterval(500);
container.afterPropertiesSet();
container.start();
ChannelHolder channelHolder = container.getChannelHolder();
assertTrue(activeConsumerCount(container, 1));
container.releaseConsumerFor(channelHolder, false, null);
assertTrue(activeConsumerCount(container, 0));
container.stop();
cf.destroy();
}
@Test
public void testNonManagedContainerDoesntStartWhenConnectionFactoryDestroyed() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ApplicationContext context = mock(ApplicationContext.class);
cf.setApplicationContext(context);
cf.addConnectionListener(connection -> {
cf.onApplicationEvent(new ContextClosedEvent(context));
cf.stop();
cf.destroy();
});
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
container.setMessageListener(m -> { });
container.setQueueNames(Q1);
container.setBeanName("stopAfterDestroyBeforeStart");
container.afterPropertiesSet();
container.start();
int n = 0;
while (n++ < 100 && container.isRunning()) {
Thread.sleep(100);
}
assertFalse(container.isRunning());
}
@Test
public void testNonManagedContainerStopsWhenConnectionFactoryDestroyed() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
ApplicationContext context = mock(ApplicationContext.class);
cf.setApplicationContext(context);
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
final CountDownLatch latch = new CountDownLatch(1);
container.setMessageListener(m -> {
latch.countDown();
});
container.setQueueNames(Q1);
container.setBeanName("stopAfterDestroy");
container.setIdleEventInterval(500);
container.setFailedDeclarationRetryInterval(500);
container.afterPropertiesSet();
container.start();
new RabbitTemplate(cf).convertAndSend(Q1, "foo");
assertTrue(latch.await(10, TimeUnit.SECONDS));
cf.onApplicationEvent(new ContextClosedEvent(context));
cf.stop();
cf.destroy();
int n = 0;
while (n++ < 100 && container.isRunning()) {
Thread.sleep(100);
}
assertFalse(container.isRunning());
}
@Test
public void testDeferredAcks() throws Exception {
CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
DirectMessageListenerContainer container = new DirectMessageListenerContainer(cf);
final CountDownLatch latch = new CountDownLatch(2);
container.setMessageListener(m -> {
latch.countDown();
});
container.setQueueNames(Q1);
container.setBeanName("deferredAcks");
container.setMessagesPerAck(2);
container.afterPropertiesSet();
container.start();
RabbitTemplate rabbitTemplate = new RabbitTemplate(cf);
rabbitTemplate.convertAndSend(Q1, "foo");
rabbitTemplate.convertAndSend(Q1, "bar");
assertTrue(latch.await(10, TimeUnit.SECONDS));
container.stop();
cf.stop();
cf.destroy();
}
private boolean consumersOnQueue(String queue, int expected) throws Exception {
int n = 0;
Properties queueProperties = admin.getQueueProperties(queue);
LogFactory.getLog(getClass()).debug(queue + " waiting for " + expected + " : " + queueProperties);
while (n++ < 600
&& (queueProperties == null || !queueProperties.get(RabbitAdmin.QUEUE_CONSUMER_COUNT).equals(expected))) {
Thread.sleep(100);
queueProperties = admin.getQueueProperties(queue);
LogFactory.getLog(getClass()).debug(queue + " waiting for " + expected + " : " + queueProperties);
}
return queueProperties.get(RabbitAdmin.QUEUE_CONSUMER_COUNT).equals(expected);
}
private boolean activeConsumerCount(AbstractMessageListenerContainer container, int expected) throws Exception {
int n = 0;
List<?> consumers = TestUtils.getPropertyValue(container, "consumers", List.class);
while (n++ < 600 && consumers.size() != expected) {
Thread.sleep(100);
}
return consumers.size() == expected;
}
private boolean restartConsumerCount(AbstractMessageListenerContainer container, int expected) throws Exception {
int n = 0;
List<?> consumers = TestUtils.getPropertyValue(container, "consumersToRestart", List.class);
while (n++ < 600 && consumers.size() != expected) {
Thread.sleep(100);
}
return consumers.size() == expected;
}
public class Tag implements ConsumerTagStrategy {
private volatile int n;
@Override
public String createConsumerTag(String queue) {
return queue + "/" + DirectMessageListenerContainerIntegrationTests.this.testName.getMethodName() + n++;
}
}
}