Package org.springframework.amqp.rabbit.core

Source Code of org.springframework.amqp.rabbit.core.RabbitTemplatePublisherCallbacksIntegrationTests

/*
* Copyright 2010-2014 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.core;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ChannelProxy;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl;
import org.springframework.amqp.rabbit.test.BrokerRunning;
import org.springframework.amqp.rabbit.test.BrokerTestUtils;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.amqp.utils.test.TestUtils;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
* @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(1);
    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(10);
    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();
    }
  }

  @Rule
  public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(ROUTE);

  @Test
  public void testPublisherConfirmReceived() throws Exception {
    final CountDownLatch latch = new CountDownLatch(10);
    templateWithConfirmsEnabled.setConfirmCallback(new ConfirmCallback() {

      @Override
      public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        latch.countDown();
      }
    });
    for (int i = 0; i < 10; i++) {
      templateWithConfirmsEnabled.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc"));
    }
    assertTrue(latch.await(10, TimeUnit.SECONDS));
    assertNull(templateWithConfirmsEnabled.getUnconfirmed(0));
    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 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()),
                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(0));
  }

  @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(0));
    assertNull(secondTemplate.getUnconfirmed(0));
  }

  @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(0);
    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()),
              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(0);
    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(0);
    assertNull(unconfirmed1);
    Collection<CorrelationData> unconfirmed2 = template2.getUnconfirmed(0);
    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(0));
  }

}
TOP

Related Classes of org.springframework.amqp.rabbit.core.RabbitTemplatePublisherCallbacksIntegrationTests

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.