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 Queue generateQueue(String name) {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);

    Queue queue = new Queue(name);
    queue.setAdminsThatShouldDeclare(rabbitAdmin);

    return queue;
  }
 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()));
 }
 public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
   Queue queue = rabbitAdmin.declareQueue();
   rabbitAdmin.declareBinding(
       new Binding(queue.getName(), Binding.DestinationType.QUEUE, "crudEvents", "#", null));
   SimpleRabbitListenerEndpoint endpoint = new SimpleRabbitListenerEndpoint();
   endpoint.setId("testMessageListener");
   endpoint.setQueueNames(queue.getName());
   endpoint.setMessageListener(testMessageListener(null));
   registrar.registerEndpoint(endpoint);
 }
 @Test
 public void testParseWithQueues() throws Exception {
   SimpleMessageListenerContainer container =
       beanFactory.getBean("container2", SimpleMessageListenerContainer.class);
   Queue queue = beanFactory.getBean("bar", Queue.class);
   assertEquals(
       "[foo, " + queue.getName() + "]", Arrays.asList(container.getQueueNames()).toString());
   assertTrue(TestUtils.getPropertyValue(container, "missingQueuesFatal", Boolean.class));
   assertFalse(TestUtils.getPropertyValue(container, "autoDeclare", Boolean.class));
 }
 @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 testParseWithQueueNames() throws Exception {
   SimpleMessageListenerContainer container =
       beanFactory.getBean("container1", SimpleMessageListenerContainer.class);
   assertEquals(AcknowledgeMode.MANUAL, container.getAcknowledgeMode());
   assertEquals(beanFactory.getBean(ConnectionFactory.class), container.getConnectionFactory());
   assertEquals(MessageListenerAdapter.class, container.getMessageListener().getClass());
   DirectFieldAccessor listenerAccessor = new DirectFieldAccessor(container.getMessageListener());
   assertEquals(
       beanFactory.getBean(TestBean.class), listenerAccessor.getPropertyValue("delegate"));
   assertEquals("handle", listenerAccessor.getPropertyValue("defaultListenerMethod"));
   Queue queue = beanFactory.getBean("bar", Queue.class);
   assertEquals(
       "[foo, " + queue.getName() + "]", Arrays.asList(container.getQueueNames()).toString());
   assertEquals(5, ReflectionTestUtils.getField(container, "concurrentConsumers"));
   assertEquals(6, ReflectionTestUtils.getField(container, "maxConcurrentConsumers"));
   assertEquals(1234L, ReflectionTestUtils.getField(container, "startConsumerMinInterval"));
   assertEquals(2345L, ReflectionTestUtils.getField(container, "stopConsumerMinInterval"));
   assertEquals(12, ReflectionTestUtils.getField(container, "consecutiveActiveTrigger"));
   assertEquals(34, ReflectionTestUtils.getField(container, "consecutiveIdleTrigger"));
   assertEquals(9876L, ReflectionTestUtils.getField(container, "receiveTimeout"));
   Map<?, ?> consumerArgs = TestUtils.getPropertyValue(container, "consumerArgs", Map.class);
   assertEquals(1, consumerArgs.size());
   Object xPriority = consumerArgs.get("x-priority");
   assertNotNull(xPriority);
   assertEquals(10, xPriority);
   assertEquals(
       Long.valueOf(5555),
       TestUtils.getPropertyValue(container, "recoveryBackOff.interval", Long.class));
   assertFalse(TestUtils.getPropertyValue(container, "exclusive", Boolean.class));
   assertFalse(TestUtils.getPropertyValue(container, "missingQueuesFatal", Boolean.class));
   assertTrue(TestUtils.getPropertyValue(container, "autoDeclare", Boolean.class));
   assertEquals(5, TestUtils.getPropertyValue(container, "declarationRetries"));
   assertEquals(1000L, TestUtils.getPropertyValue(container, "failedDeclarationRetryInterval"));
   assertEquals(30000L, TestUtils.getPropertyValue(container, "retryDeclarationInterval"));
   assertEquals(
       beanFactory.getBean("tagger"),
       TestUtils.getPropertyValue(container, "consumerTagStrategy"));
   Collection<?> group = beanFactory.getBean("containerGroup", Collection.class);
   assertEquals(3, group.size());
   assertThat(
       group,
       Matchers.contains(
           beanFactory.getBean("container1"),
           beanFactory.getBean("testListener1"),
           beanFactory.getBean("testListener2")));
   assertEquals(1235L, ReflectionTestUtils.getField(container, "idleEventInterval"));
   assertEquals("container1", container.getListenerId());
 }
 @Test
 public void testParseWithQueueNames() throws Exception {
   SimpleMessageListenerContainer container =
       this.context.getBean("testListener", SimpleMessageListenerContainer.class);
   assertEquals(AcknowledgeMode.MANUAL, container.getAcknowledgeMode());
   assertEquals(this.context.getBean(ConnectionFactory.class), container.getConnectionFactory());
   assertEquals(MessageListenerAdapter.class, container.getMessageListener().getClass());
   DirectFieldAccessor listenerAccessor = new DirectFieldAccessor(container.getMessageListener());
   assertEquals(
       this.context.getBean(TestBean.class), listenerAccessor.getPropertyValue("delegate"));
   assertEquals("handle", listenerAccessor.getPropertyValue("defaultListenerMethod"));
   Queue queue = this.context.getBean("bar", Queue.class);
   assertEquals(
       "[foo, " + queue.getName() + "]", Arrays.asList(container.getQueueNames()).toString());
 }
  private void doTest(int concurrentConsumers, ContainerConfigurer configurer) {
    int messageCount = 10;
    RabbitTemplate template = new RabbitTemplate();
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setChannelCacheSize(concurrentConsumers);
    connectionFactory.setPort(BrokerTestUtils.getPort());
    template.setConnectionFactory(connectionFactory);
    SimpleMessageConverter messageConverter = new SimpleMessageConverter();
    messageConverter.setCreateMessageIds(true);
    template.setMessageConverter(messageConverter);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue1.getName(), new Integer(i));
      template.convertAndSend(queue2.getName(), new Integer(i));
    }
    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(connectionFactory);
    final CountDownLatch latch = new CountDownLatch(messageCount * 2);
    PojoListener listener = new PojoListener(latch);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setConcurrentConsumers(concurrentConsumers);
    configurer.configure(container);
    container.afterPropertiesSet();
    container.start();
    try {
      int timeout = Math.min(1 + messageCount / concurrentConsumers, 30);
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException("unexpected interruption");
    } finally {
      container.shutdown();
      assertEquals(0, container.getActiveConsumerCount());
    }
    assertNull(template.receiveAndConvert(queue1.getName()));
    assertNull(template.receiveAndConvert(queue2.getName()));

    connectionFactory.destroy();
  }
  @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);
  }
 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;
 }
 @Override
 public void bindPubSubConsumer(
     String name, MessageChannel moduleInputChannel, Properties properties) {
   String exchangeName = BinderUtils.removeGroupFromPubSub(name);
   if (logger.isInfoEnabled()) {
     logger.info("declaring pubsub for inbound: " + name + ", bound to: " + exchangeName);
   }
   RabbitPropertiesAccessor accessor = new RabbitPropertiesAccessor(properties);
   validateConsumerProperties(name, properties, SUPPORTED_PUBSUB_CONSUMER_PROPERTIES);
   String prefix = accessor.getPrefix(this.defaultPrefix);
   FanoutExchange exchange = new FanoutExchange(applyPrefix(prefix, applyPubSub(exchangeName)));
   declareExchangeIfNotPresent(exchange);
   Queue queue;
   boolean durable = accessor.isDurable(this.defaultDurableSubscription);
   String queueName = applyPrefix(prefix, name);
   if (durable) {
     Map<String, Object> args = queueArgs(accessor, queueName);
     queue = new Queue(queueName, true, false, false, args);
   } else {
     queue = new Queue(queueName, false, false, true);
   }
   declareQueueIfNotPresent(queue);
   org.springframework.amqp.core.Binding binding = BindingBuilder.bind(queue).to(exchange);
   this.rabbitAdmin.declareBinding(binding);
   // register with context so they will be redeclared after a connection failure
   if (!this.autoDeclareContext.containsBean(applyPubSub(name))) {
     this.autoDeclareContext.getBeanFactory().registerSingleton(applyPubSub(name), queue);
   }
   String bindingBeanName = exchange.getName() + "." + queue.getName() + ".binding";
   if (!this.autoDeclareContext.containsBean(bindingBeanName)) {
     this.autoDeclareContext.getBeanFactory().registerSingleton(bindingBeanName, binding);
   }
   doRegisterConsumer(name, moduleInputChannel, queue, accessor, true);
   if (durable) {
     autoBindDLQ(name, accessor);
   }
 }
 @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()));
 }
 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;
 }
 @Test
 public void testMultipleQueueNamesWithConcurrentConsumers() {
   doTest(3, container -> container.setQueueNames(queue1.getName(), queue2.getName()));
 }
 @Test
 public void testMultipleQueueNames() {
   doTest(1, container -> container.setQueueNames(queue1.getName(), queue2.getName()));
 }
/**
 * @author Mark Fisher
 * @author Gunnar Hillert
 * @author Gary Russell
 */
public class MessageListenerContainerMultipleQueueIntegrationTests {

  private static Log logger =
      LogFactory.getLog(MessageListenerContainerMultipleQueueIntegrationTests.class);

  private static Queue queue1 = new Queue("test.queue.1");

  private static Queue queue2 = new Queue("test.queue.2");

  @Rule
  public BrokerRunning brokerIsRunning =
      BrokerRunning.isRunningWithEmptyQueues(queue1.getName(), queue2.getName());

  @Rule
  public LogLevelAdjuster logLevels =
      new LogLevelAdjuster(
          Level.INFO,
          RabbitTemplate.class,
          SimpleMessageListenerContainer.class,
          BlockingQueueConsumer.class);

  @Rule public ExpectedException exception = ExpectedException.none();

  @After
  public void tearDown() {
    this.brokerIsRunning.removeTestQueues();
  }

  @Test
  public void testMultipleQueues() {
    doTest(1, container -> container.setQueues(queue1, queue2));
  }

  @Test
  public void testMultipleQueueNames() {
    doTest(1, container -> container.setQueueNames(queue1.getName(), queue2.getName()));
  }

  @Test
  public void testMultipleQueuesWithConcurrentConsumers() {
    doTest(3, container -> container.setQueues(queue1, queue2));
  }

  @Test
  public void testMultipleQueueNamesWithConcurrentConsumers() {
    doTest(3, container -> container.setQueueNames(queue1.getName(), queue2.getName()));
  }

  private void doTest(int concurrentConsumers, ContainerConfigurer configurer) {
    int messageCount = 10;
    RabbitTemplate template = new RabbitTemplate();
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setChannelCacheSize(concurrentConsumers);
    connectionFactory.setPort(BrokerTestUtils.getPort());
    template.setConnectionFactory(connectionFactory);
    SimpleMessageConverter messageConverter = new SimpleMessageConverter();
    messageConverter.setCreateMessageIds(true);
    template.setMessageConverter(messageConverter);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue1.getName(), new Integer(i));
      template.convertAndSend(queue2.getName(), new Integer(i));
    }
    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(connectionFactory);
    final CountDownLatch latch = new CountDownLatch(messageCount * 2);
    PojoListener listener = new PojoListener(latch);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setConcurrentConsumers(concurrentConsumers);
    configurer.configure(container);
    container.afterPropertiesSet();
    container.start();
    try {
      int timeout = Math.min(1 + messageCount / concurrentConsumers, 30);
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException("unexpected interruption");
    } finally {
      container.shutdown();
      assertEquals(0, container.getActiveConsumerCount());
    }
    assertNull(template.receiveAndConvert(queue1.getName()));
    assertNull(template.receiveAndConvert(queue2.getName()));

    connectionFactory.destroy();
  }

  @FunctionalInterface
  private interface ContainerConfigurer {
    void configure(SimpleMessageListenerContainer container);
  }

  @SuppressWarnings("unused")
  private static class PojoListener {

    private final AtomicInteger count = new AtomicInteger();

    private final CountDownLatch latch;

    PojoListener(CountDownLatch latch) {
      this.latch = latch;
    }

    public void handleMessage(int value) throws Exception {
      logger.debug(value + ":" + count.getAndIncrement());
      latch.countDown();
    }

    public int getCount() {
      return count.get();
    }
  }
}
/**
 * @author Dave Syer
 * @author Gary Russell
 * @author Gunnar Hillert
 * @author Artem Bilan
 * @since 1.0
 */
public class MessageListenerContainerRetryIntegrationTests {

  private static Log logger =
      LogFactory.getLog(MessageListenerContainerRetryIntegrationTests.class);

  private static Queue queue = new Queue("test.queue");

  @Rule
  public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(queue.getName());

  @Rule
  public LogLevelAdjuster logLevels =
      new LogLevelAdjuster(
          Level.ERROR,
          RabbitTemplate.class,
          SimpleMessageListenerContainer.class,
          BlockingQueueConsumer.class);

  @Rule
  public LogLevelAdjuster traceLevels =
      new LogLevelAdjuster(
          Level.ERROR,
          StatefulRetryOperationsInterceptorFactoryBean.class,
          MessageListenerContainerRetryIntegrationTests.class);

  @Rule public ExpectedException exception = ExpectedException.none();

  @Rule public RepeatProcessor repeats = new RepeatProcessor();

  private RetryTemplate retryTemplate;

  private MessageConverter messageConverter;

  private RabbitTemplate createTemplate(int concurrentConsumers) {
    RabbitTemplate template = new RabbitTemplate();
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setChannelCacheSize(concurrentConsumers);
    connectionFactory.setPort(BrokerTestUtils.getPort());
    template.setConnectionFactory(connectionFactory);
    if (messageConverter == null) {
      SimpleMessageConverter messageConverter = new SimpleMessageConverter();
      messageConverter.setCreateMessageIds(true);
      this.messageConverter = messageConverter;
    }
    template.setMessageConverter(messageConverter);
    return template;
  }

  @After
  public void tearDown() {
    if (this.repeats.isFinalizing()) {
      this.brokerIsRunning.removeTestQueues();
    }
  }

  @Test
  public void testStatefulRetryWithAllMessagesFailing() throws Exception {

    int messageCount = 10;
    int txSize = 1;
    int failFrequency = 1;
    int concurrentConsumers = 3;
    doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers);
  }

  @Test
  public void testStatelessRetryWithAllMessagesFailing() throws Exception {

    int messageCount = 10;
    int txSize = 1;
    int failFrequency = 1;
    int concurrentConsumers = 3;
    doTestStatelessRetry(messageCount, txSize, failFrequency, concurrentConsumers);
  }

  @Test
  public void testStatefulRetryWithNoMessageIds() throws Exception {

    int messageCount = 2;
    int txSize = 1;
    int failFrequency = 1;
    int concurrentConsumers = 1;
    SimpleMessageConverter messageConverter = new SimpleMessageConverter();
    // There will be no key for these messages so they cannot be recovered...
    messageConverter.setCreateMessageIds(false);
    this.messageConverter = messageConverter;
    // Beware of context cache busting if retry policy fails...
    this.retryTemplate = new RetryTemplate();
    this.retryTemplate.setRetryContextCache(new MapRetryContextCache(1));
    // The container should have shutdown, so there are now no active consumers
    exception.expectMessage("expected:<1> but was:<0>");
    doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers);
  }

  @Test
  @Repeat(10)
  public void testStatefulRetryWithTxSizeAndIntermittentFailure() throws Exception {

    int messageCount = 10;
    int txSize = 4;
    int failFrequency = 3;
    int concurrentConsumers = 3;
    doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers);
  }

  @Test
  public void testStatefulRetryWithMoreMessages() throws Exception {

    int messageCount = 200;
    int txSize = 10;
    int failFrequency = 6;
    int concurrentConsumers = 3;
    doTestStatefulRetry(messageCount, txSize, failFrequency, concurrentConsumers);
  }

  private Advice createRetryInterceptor(final CountDownLatch latch, boolean stateful)
      throws Exception {
    AbstractRetryOperationsInterceptorFactoryBean factory;
    if (stateful) {
      factory = new StatefulRetryOperationsInterceptorFactoryBean();
    } else {
      factory = new StatelessRetryOperationsInterceptorFactoryBean();
    }
    factory.setMessageRecoverer(
        (message, cause) -> {
          logger.warn(
              "Recovered: ["
                  + SerializationUtils.deserialize(message.getBody()).toString()
                  + "], message: "
                  + message);
          latch.countDown();
        });
    if (retryTemplate == null) {
      retryTemplate = new RetryTemplate();
    }
    factory.setRetryOperations(retryTemplate);
    return factory.getObject();
  }

  private void doTestStatefulRetry(
      int messageCount, int txSize, int failFrequency, int concurrentConsumers) throws Exception {
    doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, true);
  }

  private void doTestStatelessRetry(
      int messageCount, int txSize, int failFrequency, int concurrentConsumers) throws Exception {
    doTestRetry(messageCount, txSize, failFrequency, concurrentConsumers, false);
  }

  private void doTestRetry(
      int messageCount, int txSize, int failFrequency, int concurrentConsumers, boolean stateful)
      throws Exception {

    int failedMessageCount =
        messageCount / failFrequency + (messageCount % failFrequency == 0 ? 0 : 1);

    RabbitTemplate template = createTemplate(concurrentConsumers);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue.getName(), i);
    }

    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(template.getConnectionFactory());
    PojoListener listener = new PojoListener(failFrequency);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setTxSize(txSize);
    container.setConcurrentConsumers(concurrentConsumers);

    final CountDownLatch latch = new CountDownLatch(failedMessageCount);
    container.setAdviceChain(new Advice[] {createRetryInterceptor(latch, stateful)});

    container.setQueueNames(queue.getName());
    container.afterPropertiesSet();
    container.start();

    try {

      int timeout = Math.min(1 + 2 * messageCount / concurrentConsumers, 30);

      final int count = messageCount;
      logger.debug("Waiting for messages with timeout = " + timeout + " (s)");
      Executors.newSingleThreadExecutor()
          .execute(
              () -> {
                while (container.getActiveConsumerCount() > 0) {
                  try {
                    Thread.sleep(100L);
                  } catch (InterruptedException e) {
                    latch.countDown();
                    Thread.currentThread().interrupt();
                    return;
                  }
                }
                for (int i = 0; i < count; i++) {
                  latch.countDown();
                }
              });
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);

      // Retried each failure 3 times (default retry policy)...
      assertEquals(3 * failedMessageCount, listener.getCount());

      // All failed messages recovered
      assertEquals(null, template.receiveAndConvert(queue.getName()));

    } finally {
      container.shutdown();
      ((DisposableBean) template.getConnectionFactory()).destroy();

      assertEquals(0, container.getActiveConsumerCount());
    }
  }

  private static class PojoListener {

    private final AtomicInteger count = new AtomicInteger();

    private final int failFrequency;

    PojoListener(int failFrequency) {
      this.failFrequency = failFrequency;
    }

    @SuppressWarnings("unused")
    public void handleMessage(int value) throws Exception {
      logger.debug("Handling: [" + value + "], fails:" + count);
      if (value % failFrequency == 0) {
        count.getAndIncrement();
        logger.debug("Failing: [" + value + "], fails:" + count);
        throw new RuntimeException("Planned");
      }
    }

    public int getCount() {
      return count.get();
    }
  }
}
  private void doTestRetry(
      int messageCount, int txSize, int failFrequency, int concurrentConsumers, boolean stateful)
      throws Exception {

    int failedMessageCount =
        messageCount / failFrequency + (messageCount % failFrequency == 0 ? 0 : 1);

    RabbitTemplate template = createTemplate(concurrentConsumers);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue.getName(), i);
    }

    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(template.getConnectionFactory());
    PojoListener listener = new PojoListener(failFrequency);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setTxSize(txSize);
    container.setConcurrentConsumers(concurrentConsumers);

    final CountDownLatch latch = new CountDownLatch(failedMessageCount);
    container.setAdviceChain(new Advice[] {createRetryInterceptor(latch, stateful)});

    container.setQueueNames(queue.getName());
    container.afterPropertiesSet();
    container.start();

    try {

      int timeout = Math.min(1 + 2 * messageCount / concurrentConsumers, 30);

      final int count = messageCount;
      logger.debug("Waiting for messages with timeout = " + timeout + " (s)");
      Executors.newSingleThreadExecutor()
          .execute(
              () -> {
                while (container.getActiveConsumerCount() > 0) {
                  try {
                    Thread.sleep(100L);
                  } catch (InterruptedException e) {
                    latch.countDown();
                    Thread.currentThread().interrupt();
                    return;
                  }
                }
                for (int i = 0; i < count; i++) {
                  latch.countDown();
                }
              });
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);

      // Retried each failure 3 times (default retry policy)...
      assertEquals(3 * failedMessageCount, listener.getCount());

      // All failed messages recovered
      assertEquals(null, template.receiveAndConvert(queue.getName()));

    } finally {
      container.shutdown();
      ((DisposableBean) template.getConnectionFactory()).destroy();

      assertEquals(0, container.getActiveConsumerCount());
    }
  }
 private void doRegisterConsumer(
     String name,
     MessageChannel moduleInputChannel,
     Queue queue,
     RabbitPropertiesAccessor properties,
     boolean isPubSub) {
   // Fix for XD-2503
   // Temporarily overrides the thread context classloader with the one where the
   // SimpleMessageListenerContainer
   // is defined
   // This allows for the proxying that happens while initializing the
   // SimpleMessageListenerContainer to work
   // correctly
   ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
   try {
     ClassUtils.overrideThreadContextClassLoader(
         SimpleMessageListenerContainer.class.getClassLoader());
     SimpleMessageListenerContainer listenerContainer =
         new SimpleMessageListenerContainer(this.connectionFactory);
     listenerContainer.setAcknowledgeMode(
         properties.getAcknowledgeMode(this.defaultAcknowledgeMode));
     listenerContainer.setChannelTransacted(
         properties.getTransacted(this.defaultChannelTransacted));
     listenerContainer.setDefaultRequeueRejected(
         properties.getRequeueRejected(this.defaultDefaultRequeueRejected));
     if (!isPubSub) {
       int concurrency = properties.getConcurrency(this.defaultConcurrency);
       concurrency = concurrency > 0 ? concurrency : 1;
       listenerContainer.setConcurrentConsumers(concurrency);
       int maxConcurrency = properties.getMaxConcurrency(this.defaultMaxConcurrency);
       if (maxConcurrency > concurrency) {
         listenerContainer.setMaxConcurrentConsumers(maxConcurrency);
       }
     }
     listenerContainer.setPrefetchCount(properties.getPrefetchCount(this.defaultPrefetchCount));
     listenerContainer.setTxSize(properties.getTxSize(this.defaultTxSize));
     listenerContainer.setTaskExecutor(new SimpleAsyncTaskExecutor(queue.getName() + "-"));
     listenerContainer.setQueues(queue);
     int maxAttempts = properties.getMaxAttempts(this.defaultMaxAttempts);
     if (maxAttempts > 1 || properties.getRepublishToDLQ(this.defaultRepublishToDLQ)) {
       RetryOperationsInterceptor retryInterceptor =
           RetryInterceptorBuilder.stateless()
               .maxAttempts(maxAttempts)
               .backOffOptions(
                   properties.getBackOffInitialInterval(this.defaultBackOffInitialInterval),
                   properties.getBackOffMultiplier(this.defaultBackOffMultiplier),
                   properties.getBackOffMaxInterval(this.defaultBackOffMaxInterval))
               .recoverer(determineRecoverer(name, properties))
               .build();
       listenerContainer.setAdviceChain(new Advice[] {retryInterceptor});
     }
     listenerContainer.setAfterReceivePostProcessors(this.decompressingPostProcessor);
     listenerContainer.setMessagePropertiesConverter(
         RabbitMessageChannelBinder.inboundMessagePropertiesConverter);
     listenerContainer.afterPropertiesSet();
     AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer);
     adapter.setBeanFactory(this.getBeanFactory());
     DirectChannel bridgeToModuleChannel = new DirectChannel();
     bridgeToModuleChannel.setBeanFactory(this.getBeanFactory());
     bridgeToModuleChannel.setBeanName(name + ".bridge");
     adapter.setOutputChannel(bridgeToModuleChannel);
     adapter.setBeanName("inbound." + name);
     DefaultAmqpHeaderMapper mapper = new DefaultAmqpHeaderMapper();
     mapper.setRequestHeaderNames(
         properties.getRequestHeaderPattens(this.defaultRequestHeaderPatterns));
     mapper.setReplyHeaderNames(properties.getReplyHeaderPattens(this.defaultReplyHeaderPatterns));
     adapter.setHeaderMapper(mapper);
     adapter.afterPropertiesSet();
     Binding consumerBinding = Binding.forConsumer(name, adapter, moduleInputChannel, properties);
     addBinding(consumerBinding);
     ReceivingHandler convertingBridge = new ReceivingHandler();
     convertingBridge.setOutputChannel(moduleInputChannel);
     convertingBridge.setBeanName(name + ".convert.bridge");
     convertingBridge.afterPropertiesSet();
     bridgeToModuleChannel.subscribe(convertingBridge);
     consumerBinding.start();
   } finally {
     Thread.currentThread().setContextClassLoader(originalClassloader);
   }
 }
 /**
  * Try passive declaration first, in case the user has pre-configured the queue with incompatible
  * arguments.
  *
  * @param queue The queue.
  */
 private void declareQueueIfNotPresent(Queue queue) {
   if (this.rabbitAdmin.getQueueProperties(queue.getName()) == null) {
     this.rabbitAdmin.declareQueue(queue);
   }
 }