public final class QueueParserIntegrationTests { @Rule public BrokerRunning brokerIsRunning = BrokerRunning.isRunning(); private XmlBeanFactory beanFactory; @Before public void setUpDefaultBeanFactory() throws Exception { beanFactory = new XmlBeanFactory( new ClassPathResource(getClass().getSimpleName() + "-context.xml", getClass())); } @Test public void testArgumentsQueue() throws Exception { Queue queue = beanFactory.getBean("arguments", Queue.class); assertNotNull(queue); RabbitTemplate template = new RabbitTemplate(new CachingConnectionFactory(BrokerTestUtils.getPort())); RabbitAdmin rabbitAdmin = new RabbitAdmin(template.getConnectionFactory()); rabbitAdmin.deleteQueue(queue.getName()); rabbitAdmin.declareQueue(queue); assertEquals(100L, queue.getArguments().get("x-message-ttl")); template.convertAndSend(queue.getName(), "message"); Thread.sleep(200); String result = (String) template.receiveAndConvert(queue.getName()); assertEquals(null, result); } }
/** * @author Gary Russell * @author Gunar Hillert * @author Artem Bilan * @since 1.1 */ public class RabbitTemplatePublisherCallbacksIntegrationTests { private static final String ROUTE = "test.queue"; private CachingConnectionFactory connectionFactory; private CachingConnectionFactory connectionFactoryWithConfirmsEnabled; private CachingConnectionFactory connectionFactoryWithReturnsEnabled; private RabbitTemplate templateWithConfirmsEnabled; private RabbitTemplate templateWithReturnsEnabled; @Before public void create() { connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setChannelCacheSize(10); connectionFactory.setPort(BrokerTestUtils.getPort()); connectionFactoryWithConfirmsEnabled = new CachingConnectionFactory(); connectionFactoryWithConfirmsEnabled.setHost("localhost"); // When using publisher confirms, the cache size needs to be large enough // otherwise channels can be closed before confirms are received. connectionFactoryWithConfirmsEnabled.setChannelCacheSize(100); connectionFactoryWithConfirmsEnabled.setPort(BrokerTestUtils.getPort()); connectionFactoryWithConfirmsEnabled.setPublisherConfirms(true); templateWithConfirmsEnabled = new RabbitTemplate(connectionFactoryWithConfirmsEnabled); connectionFactoryWithReturnsEnabled = new CachingConnectionFactory(); connectionFactoryWithReturnsEnabled.setHost("localhost"); connectionFactoryWithReturnsEnabled.setChannelCacheSize(1); connectionFactoryWithReturnsEnabled.setPort(BrokerTestUtils.getPort()); connectionFactoryWithReturnsEnabled.setPublisherReturns(true); templateWithReturnsEnabled = new RabbitTemplate(connectionFactoryWithReturnsEnabled); } @After public void cleanUp() { if (connectionFactory != null) { connectionFactory.destroy(); } if (connectionFactoryWithConfirmsEnabled != null) { connectionFactoryWithConfirmsEnabled.destroy(); } if (connectionFactoryWithReturnsEnabled != null) { connectionFactoryWithReturnsEnabled.destroy(); } this.brokerIsRunning.removeTestQueues(); } @Rule public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(ROUTE); @Test public void testPublisherConfirmReceived() throws Exception { final CountDownLatch latch = new CountDownLatch(10000); final AtomicInteger acks = new AtomicInteger(); templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { acks.incrementAndGet(); latch.countDown(); } }); ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { exec.submit( new Runnable() { @Override public void run() { try { for (int i = 0; i < 100; i++) { templateWithConfirmsEnabled.convertAndSend( ROUTE, (Object) "message", new CorrelationData("abc")); } } catch (Throwable t) { t.printStackTrace(); } } }); } exec.shutdown(); assertTrue(exec.awaitTermination(300, TimeUnit.SECONDS)); assertTrue("" + latch.getCount(), latch.await(60, TimeUnit.SECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); this.templateWithConfirmsEnabled.execute( new ChannelCallback<Void>() { @Override public Void doInRabbit(Channel channel) throws Exception { Map<?, ?> listenerMap = TestUtils.getPropertyValue( ((ChannelProxy) channel).getTargetChannel(), "listenerForSeq", Map.class); int n = 0; while (n++ < 100 && listenerMap.size() > 0) { Thread.sleep(100); } assertEquals(0, listenerMap.size()); return null; } }); Log logger = spy(TestUtils.getPropertyValue(connectionFactoryWithConfirmsEnabled, "logger", Log.class)); new DirectFieldAccessor(connectionFactoryWithConfirmsEnabled) .setPropertyValue("logger", logger); cleanUp(); verify(logger, never()).error(any()); } @Test public void testPublisherConfirmWithSendAndReceive() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<CorrelationData> confirmCD = new AtomicReference<CorrelationData>(); templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { confirmCD.set(correlationData); latch.countDown(); } }); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.connectionFactoryWithConfirmsEnabled); container.setQueueNames(ROUTE); container.setMessageListener( new MessageListenerAdapter( new Object() { @SuppressWarnings("unused") public String handleMessage(String in) { return in.toUpperCase(); } })); container.start(); CorrelationData correlationData = new CorrelationData("abc"); String result = (String) this.templateWithConfirmsEnabled.convertSendAndReceive( ROUTE, (Object) "message", correlationData); container.stop(); assertEquals("MESSAGE", result); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals(correlationData, confirmCD.get()); } @Test public void testPublisherConfirmReceivedConcurrentThreads() throws Exception { final CountDownLatch latch = new CountDownLatch(2); templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { latch.countDown(); } }); // Hold up the first thread so we get two channels final CountDownLatch threadLatch = new CountDownLatch(1); // Thread 1 Executors.newSingleThreadExecutor() .execute( new Runnable() { @Override public void run() { templateWithConfirmsEnabled.execute( new ChannelCallback<Object>() { @Override public Object doInRabbit(Channel channel) throws Exception { try { threadLatch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } templateWithConfirmsEnabled.doSend( channel, "", ROUTE, new SimpleMessageConverter() .toMessage("message", new MessageProperties()), false, new CorrelationData("def")); return null; } }); } }); // Thread 2 templateWithConfirmsEnabled.convertAndSend( ROUTE, (Object) "message", new CorrelationData("abc")); threadLatch.countDown(); assertTrue(latch.await(5000, TimeUnit.MILLISECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); } @Test public void testPublisherConfirmReceivedTwoTemplates() throws Exception { final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1); templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { latch1.countDown(); } }); templateWithConfirmsEnabled.convertAndSend( ROUTE, (Object) "message", new CorrelationData("abc")); RabbitTemplate secondTemplate = new RabbitTemplate(connectionFactoryWithConfirmsEnabled); secondTemplate.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { latch2.countDown(); } }); secondTemplate.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); assertTrue(latch1.await(10, TimeUnit.SECONDS)); assertTrue(latch2.await(10, TimeUnit.SECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); assertNull(secondTemplate.getUnconfirmed(-1)); } @Test public void testPublisherReturns() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final List<Message> returns = new ArrayList<Message>(); templateWithReturnsEnabled.setReturnCallback( new ReturnCallback() { @Override public void returnedMessage( Message message, int replyCode, String replyText, String exchange, String routingKey) { returns.add(message); latch.countDown(); } }); templateWithReturnsEnabled.setMandatory(true); templateWithReturnsEnabled.convertAndSend( ROUTE + "junk", (Object) "message", new CorrelationData("abc")); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); assertEquals(1, returns.size()); Message message = returns.get(0); assertEquals("message", new String(message.getBody(), "utf-8")); } @Test public void testPublisherReturnsWithMandatoryExpression() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final List<Message> returns = new ArrayList<Message>(); templateWithReturnsEnabled.setReturnCallback( new ReturnCallback() { @Override public void returnedMessage( Message message, int replyCode, String replyText, String exchange, String routingKey) { returns.add(message); latch.countDown(); } }); Expression mandatoryExpression = new SpelExpressionParser().parseExpression("'message'.bytes == body"); templateWithReturnsEnabled.setMandatoryExpression(mandatoryExpression); templateWithReturnsEnabled.convertAndSend( ROUTE + "junk", (Object) "message", new CorrelationData("abc")); templateWithReturnsEnabled.convertAndSend( ROUTE + "junk", (Object) "foo", new CorrelationData("abc")); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); assertEquals(1, returns.size()); Message message = returns.get(0); assertEquals("message", new String(message.getBody(), "utf-8")); } @Test public void testPublisherConfirmNotReceived() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); doReturn(new PublisherCallbackChannelImpl(mockChannel)).when(mockConnection).createChannel(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final AtomicBoolean confirmed = new AtomicBoolean(); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { confirmed.set(true); } }); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); Thread.sleep(5); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); assertEquals(1, unconfirmed.size()); assertEquals("abc", unconfirmed.iterator().next().getId()); assertFalse(confirmed.get()); } @Test public void testPublisherConfirmNotReceivedMultiThreads() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel1 = mock(Channel.class); Channel mockChannel2 = mock(Channel.class); when(mockChannel1.isOpen()).thenReturn(true); when(mockChannel2.isOpen()).thenReturn(true); when(mockChannel1.getNextPublishSeqNo()).thenReturn(1L, 2L, 3L, 4L); when(mockChannel2.getNextPublishSeqNo()).thenReturn(1L, 2L, 3L, 4L); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); PublisherCallbackChannelImpl channel1 = new PublisherCallbackChannelImpl(mockChannel1); PublisherCallbackChannelImpl channel2 = new PublisherCallbackChannelImpl(mockChannel2); when(mockConnection.createChannel()).thenReturn(channel1).thenReturn(channel2); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); ccf.setChannelCacheSize(3); final RabbitTemplate template = new RabbitTemplate(ccf); final AtomicBoolean confirmed = new AtomicBoolean(); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { confirmed.set(true); } }); // Hold up the first thread so we get two channels final CountDownLatch threadLatch = new CountDownLatch(1); final CountDownLatch threadSentLatch = new CountDownLatch(1); // Thread 1 ExecutorService exec = Executors.newSingleThreadExecutor(); exec.execute( new Runnable() { @Override public void run() { template.execute( new ChannelCallback<Object>() { @Override public Object doInRabbit(Channel channel) throws Exception { try { threadLatch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } template.doSend( channel, "", ROUTE, new SimpleMessageConverter().toMessage("message", new MessageProperties()), false, new CorrelationData("def")); threadSentLatch.countDown(); return null; } }); } }); // Thread 2 template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); // channel y threadLatch.countDown(); assertTrue(threadSentLatch.await(5, TimeUnit.SECONDS)); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); assertEquals(2, unconfirmed.size()); Set<String> ids = new HashSet<String>(); Iterator<CorrelationData> iterator = unconfirmed.iterator(); ids.add(iterator.next().getId()); ids.add(iterator.next().getId()); assertTrue(ids.remove("abc")); assertTrue(ids.remove("def")); assertFalse(confirmed.get()); DirectFieldAccessor dfa = new DirectFieldAccessor(template); Map<?, ?> pendingConfirms = (Map<?, ?>) dfa.getPropertyValue("pendingConfirms"); assertThat(pendingConfirms.size(), greaterThan(0)); // might use 2 or only 1 channel exec.shutdown(); assertTrue(exec.awaitTermination(10, TimeUnit.SECONDS)); ccf.destroy(); assertEquals(0, pendingConfirms.size()); } @Test public void testPublisherConfirmNotReceivedAged() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); doReturn(new PublisherCallbackChannelImpl(mockChannel)).when(mockConnection).createChannel(); final AtomicInteger count = new AtomicInteger(); doAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return count.incrementAndGet(); } }) .when(mockChannel) .getNextPublishSeqNo(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final AtomicBoolean confirmed = new AtomicBoolean(); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { confirmed.set(true); } }); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); Thread.sleep(100); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(50); assertEquals(1, unconfirmed.size()); assertEquals("abc", unconfirmed.iterator().next().getId()); assertFalse(confirmed.get()); Thread.sleep(100); unconfirmed = template.getUnconfirmed(50); assertEquals(1, unconfirmed.size()); assertEquals("def", unconfirmed.iterator().next().getId()); assertFalse(confirmed.get()); } @Test public void testPublisherConfirmMultiple() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); PublisherCallbackChannelImpl callbackChannel = new PublisherCallbackChannelImpl(mockChannel); when(mockConnection.createChannel()).thenReturn(callbackChannel); final AtomicInteger count = new AtomicInteger(); doAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return count.incrementAndGet(); } }) .when(mockChannel) .getNextPublishSeqNo(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final CountDownLatch latch = new CountDownLatch(2); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { latch.countDown(); } } }); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); callbackChannel.handleAck(2, true); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); assertNull(unconfirmed); } /** * Tests that piggy-backed confirms (multiple=true) are distributed to the proper template. * * @throws Exception */ @Test public void testPublisherConfirmMultipleWithTwoListeners() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); PublisherCallbackChannelImpl callbackChannel = new PublisherCallbackChannelImpl(mockChannel); when(mockConnection.createChannel()).thenReturn(callbackChannel); final AtomicInteger count = new AtomicInteger(); doAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return count.incrementAndGet(); } }) .when(mockChannel) .getNextPublishSeqNo(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); final RabbitTemplate template1 = new RabbitTemplate(ccf); final Set<String> confirms = new HashSet<String>(); final CountDownLatch latch1 = new CountDownLatch(1); template1.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { confirms.add(correlationData.getId() + "1"); latch1.countDown(); } } }); final RabbitTemplate template2 = new RabbitTemplate(ccf); final CountDownLatch latch2 = new CountDownLatch(1); template2.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { confirms.add(correlationData.getId() + "2"); latch2.countDown(); } } }); template1.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); template2.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); template2.convertAndSend(ROUTE, (Object) "message", new CorrelationData("ghi")); callbackChannel.handleAck(3, true); assertTrue(latch1.await(1000, TimeUnit.MILLISECONDS)); assertTrue(latch2.await(1000, TimeUnit.MILLISECONDS)); Collection<CorrelationData> unconfirmed1 = template1.getUnconfirmed(-1); assertNull(unconfirmed1); Collection<CorrelationData> unconfirmed2 = template2.getUnconfirmed(-1); assertNull(unconfirmed2); assertTrue(confirms.contains("abc1")); assertTrue(confirms.contains("def2")); assertTrue(confirms.contains("ghi2")); assertEquals(3, confirms.size()); } /** * AMQP-262 Sets up a situation where we are processing 'multi' acks at the same time as adding a * new pending ack to the map. Test verifies we don't get a {@link * ConcurrentModificationException}. */ @SuppressWarnings({"rawtypes", "unchecked", "deprecation"}) @Test public void testConcurrentConfirms() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockChannel.getNextPublishSeqNo()).thenReturn(1L, 2L, 3L, 4L); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); final PublisherCallbackChannelImpl channel = new PublisherCallbackChannelImpl(mockChannel); when(mockConnection.createChannel()).thenReturn(channel); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); ccf.setChannelCacheSize(3); final RabbitTemplate template = new RabbitTemplate(ccf); final CountDownLatch first2SentOnThread1Latch = new CountDownLatch(1); final CountDownLatch delayAckProcessingLatch = new CountDownLatch(1); final CountDownLatch startedProcessingMultiAcksLatch = new CountDownLatch(1); final CountDownLatch waitForAll3AcksLatch = new CountDownLatch(3); final CountDownLatch allSentLatch = new CountDownLatch(1); final AtomicInteger acks = new AtomicInteger(); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { try { startedProcessingMultiAcksLatch.countDown(); // delay processing here; ensures thread 2 put would be concurrent delayAckProcessingLatch.await(2, TimeUnit.SECONDS); // only delay first time through delayAckProcessingLatch.countDown(); waitForAll3AcksLatch.countDown(); acks.incrementAndGet(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Executors.newSingleThreadExecutor() .execute( new Runnable() { @Override public void run() { template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); first2SentOnThread1Latch.countDown(); } }); Executors.newSingleThreadExecutor() .execute( new Runnable() { @Override public void run() { try { startedProcessingMultiAcksLatch.await(); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("ghi")); allSentLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); assertTrue(first2SentOnThread1Latch.await(10, TimeUnit.SECONDS)); // there should be no concurrent execution exception here channel.handleAck(2, true); assertTrue(allSentLatch.await(10, TimeUnit.SECONDS)); channel.handleAck(3, false); assertTrue(waitForAll3AcksLatch.await(10, TimeUnit.SECONDS)); assertEquals(3, acks.get()); // 3.3.1 client channel.basicConsume("foo", false, (Map) null, null); verify(mockChannel).basicConsume("foo", false, (Map) null, null); channel.basicQos(3, false); verify(mockChannel).basicQos(3, false); doReturn(true).when(mockChannel).flowBlocked(); assertTrue(channel.flowBlocked()); try { channel.flow(true); fail("Expected exception"); } catch (UnsupportedOperationException e) { } try { channel.getFlow(); fail("Expected exception"); } catch (UnsupportedOperationException e) { } // 3.2.4 client /* try { channel.basicConsume("foo", false, (Map) null, (Consumer) null); fail("Expected exception"); } catch (UnsupportedOperationException e) {} try { channel.basicQos(3, false); fail("Expected exception"); } catch (UnsupportedOperationException e) {} try { channel.flowBlocked(); fail("Expected exception"); } catch (UnsupportedOperationException e) {} channel.flow(true); verify(mockChannel).flow(true); channel.getFlow(); verify(mockChannel).getFlow(); */ } @Test public void testNackForBadExchange() throws Exception { final AtomicBoolean nack = new AtomicBoolean(true); final AtomicReference<CorrelationData> correlation = new AtomicReference<CorrelationData>(); final AtomicReference<String> reason = new AtomicReference<String>(); final CountDownLatch latch = new CountDownLatch(2); this.templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { nack.set(ack); correlation.set(correlationData); reason.set(cause); latch.countDown(); } }); Log logger = spy(TestUtils.getPropertyValue(connectionFactoryWithConfirmsEnabled, "logger", Log.class)); new DirectFieldAccessor(connectionFactoryWithConfirmsEnabled) .setPropertyValue("logger", logger); final AtomicReference<String> log = new AtomicReference<String>(); doAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { log.set((String) invocation.getArguments()[0]); invocation.callRealMethod(); latch.countDown(); return null; } }) .when(logger) .error(any()); CorrelationData correlationData = new CorrelationData("bar"); String exchange = UUID.randomUUID().toString(); this.templateWithConfirmsEnabled.convertAndSend(exchange, "key", "foo", correlationData); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(nack.get()); assertEquals(correlationData.toString(), correlation.get().toString()); assertThat(reason.get(), containsString("NOT_FOUND - no exchange '" + exchange)); assertThat(log.get(), containsString("NOT_FOUND - no exchange '" + exchange)); } @Test public void testConfirmReceivedAfterPublisherCallbackChannelScheduleClose() throws Exception { final CountDownLatch latch = new CountDownLatch(40); templateWithConfirmsEnabled.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { latch.countDown(); } }); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 20; i++) { executorService.execute( new Runnable() { @Override public void run() { templateWithConfirmsEnabled.convertAndSend( ROUTE, (Object) "message", new CorrelationData("abc")); templateWithConfirmsEnabled.convertAndSend( "BAD_ROUTE", (Object) "bad", new CorrelationData("cba")); } }); } assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); } // AMQP-506 ConcurrentModificationException @Test public void testPublisherConfirmGetUnconfirmedConcurrency() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockChannel.isOpen()).thenReturn(true); final AtomicLong seq = new AtomicLong(); doAnswer( new Answer<Long>() { @Override public Long answer(InvocationOnMock invocation) throws Throwable { return seq.incrementAndGet(); } }) .when(mockChannel) .getNextPublishSeqNo(); when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); doReturn(mockChannel).when(mockConnection).createChannel(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final AtomicBoolean confirmed = new AtomicBoolean(); template.setConfirmCallback( new ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { confirmed.set(true); } }); ExecutorService exec = Executors.newSingleThreadExecutor(); final AtomicBoolean sentAll = new AtomicBoolean(); exec.execute( new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) { template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); } sentAll.set(true); } }); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); long t1 = System.currentTimeMillis(); while (!sentAll.get() && System.currentTimeMillis() < t1 + 20000) { unconfirmed = template.getUnconfirmed(-1); } assertTrue(sentAll.get()); assertFalse(confirmed.get()); } }
@RunWith(Parameterized.class) public class MessageListenerContainerLifecycleIntegrationTests { private static Log logger = LogFactory.getLog(MessageListenerContainerLifecycleIntegrationTests.class); private static Queue queue = new Queue("test.queue"); private enum TransactionMode { ON, OFF; public boolean isTransactional() { return this == ON; } } private enum Concurrency { LOW(1); private final int value; private Concurrency(int value) { this.value = value; } public int value() { return this.value; } } private enum MessageCount { LOW(1), HIGH(200); private final int value; private MessageCount(int value) { this.value = value; } public int value() { return this.value; } } private RabbitTemplate template = new RabbitTemplate(); private final int concurrentConsumers; private final boolean transactional; @Rule public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueue(queue); private final int messageCount; public MessageListenerContainerLifecycleIntegrationTests( MessageCount messageCount, Concurrency concurrency, TransactionMode transacted) { this.messageCount = messageCount.value(); this.concurrentConsumers = concurrency.value(); this.transactional = transacted.isTransactional(); } @Parameters public static List<Object[]> getParameters() { @SuppressWarnings("unused") Object[] debug = new Object[] {MessageCount.LOW, Concurrency.LOW, TransactionMode.OFF}; // return Collections.singletonList(debug); return Arrays.asList( // new Object[] {MessageCount.HIGH, Concurrency.LOW, TransactionMode.ON}, // new Object[] {MessageCount.HIGH, Concurrency.LOW, TransactionMode.OFF}); } @Before public void declareQueue() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setChannelCacheSize(concurrentConsumers); // connectionFactory.setPort(5673); template.setConnectionFactory(connectionFactory); } @Test public void testListenerSunnyDay() throws Exception { CountDownLatch latch = new CountDownLatch(messageCount); for (int i = 0; i < messageCount; i++) { template.convertAndSend(queue.getName(), i + "foo"); } SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(template.getConnectionFactory()); container.setMessageListener(new MessageListenerAdapter(new PojoListener(latch))); container.setChannelTransacted(transactional); container.setConcurrentConsumers(concurrentConsumers); container.setQueueName(queue.getName()); container.afterPropertiesSet(); container.start(); try { boolean waited = latch.await(50, TimeUnit.MILLISECONDS); assertFalse("Expected time out waiting for message", waited); container.stop(); Thread.sleep(500L); container.start(); if (transactional) { waited = latch.await(5, TimeUnit.SECONDS); assertTrue("Timed out waiting for message", waited); } else { waited = latch.await(500, TimeUnit.MILLISECONDS); // If non-transactional we half expect to lose messages assertFalse("Expected time out waiting for message", waited); } } finally { // Wait for broker communication to finish before trying to stop // container Thread.sleep(300L); container.shutdown(); } assertNull(template.receiveAndConvert(queue.getName())); } 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 { logger.debug(value + count.getAndIncrement()); if (fail) { throw new RuntimeException("Planned failure"); } } finally { latch.countDown(); } } } }
/** * @author Dave Syer * @author Gunnar Hillert * @author Gary Russell * @since 1.0 */ public class MessageListenerTxSizeIntegrationTests { private static Log logger = LogFactory.getLog(MessageListenerTxSizeIntegrationTests.class); private final Queue queue = new Queue("test.queue"); private final RabbitTemplate template = new RabbitTemplate(); private final int concurrentConsumers = 1; private final int messageCount = 12; private final int txSize = 4; private boolean transactional = true; private SimpleMessageListenerContainer container; @Rule public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster( Level.ERROR, 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(); } ((DisposableBean) template.getConnectionFactory()).destroy(); } @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 final 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) {} @Override 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(); } } } }
/** * @author Dave Syer * @author Gunnar Hillert * @author Gary Russell * @since 1.0 */ @RunWith(Parameterized.class) public class SimpleMessageListenerContainerIntegrationTests { private static Log logger = LogFactory.getLog(SimpleMessageListenerContainerIntegrationTests.class); private final Queue queue = new Queue("test.queue"); private final RabbitTemplate template = new RabbitTemplate(); private final int concurrentConsumers; private final AcknowledgeMode acknowledgeMode; @Rule public LongRunningIntegrationTest longTests = new LongRunningIntegrationTest(); @Rule public Log4jLevelAdjuster logLevels = new Log4jLevelAdjuster( Level.OFF, RabbitTemplate.class, ConditionalRejectingErrorHandler.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.setHost("localhost"); 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(); } ((DisposableBean) template.getConnectionFactory()).destroy(); this.brokerIsRunning.removeTestQueues(); } @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(10, 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(10 + 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); // requires RabbitMQ 3.2.x // container.setConsumerArguments(Collections. <String, Object> singletonMap("x-priority", // Integer.valueOf(10))); if (externalTransaction) { container.setTransactionManager(new TestTransactionManager()); } container.afterPropertiesSet(); container.start(); return container; } public static class PojoListener { private final 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 final 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; } @Override 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 final 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; } @Override 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 {} } }