@Test
  public void testAutoBindDLQwithRepublish() throws Exception {
    // pre-declare the queue with dead-lettering, users can also use a policy
    RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());
    Map<String, Object> args = new HashMap<String, Object>();
    args.put("x-dead-letter-exchange", "xdbustest.DLX");
    Queue queue = new Queue("xdbustest.dlqpubtest", true, false, false, args);
    admin.declareQueue(queue);

    MessageBus bus = getMessageBus();
    Properties properties = new Properties();
    properties.put("prefix", "xdbustest.");
    properties.put("autoBindDLQ", "true");
    properties.put("republishToDLQ", "true");
    properties.put("maxAttempts", "1"); // disable retry
    properties.put("requeue", "false");
    DirectChannel moduleInputChannel = new DirectChannel();
    moduleInputChannel.setBeanName("dlqPubTest");
    moduleInputChannel.subscribe(
        new MessageHandler() {

          @Override
          public void handleMessage(Message<?> message) throws MessagingException {
            throw new RuntimeException("foo");
          }
        });
    bus.bindConsumer("dlqpubtest", moduleInputChannel, properties);

    RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
    template.convertAndSend("", "xdbustest.dlqpubtest", "foo");

    int n = 0;
    while (n++ < 100) {
      org.springframework.amqp.core.Message deadLetter =
          template.receive("xdbustest.dlqpubtest.dlq");
      if (deadLetter != null) {
        assertEquals("foo", new String(deadLetter.getBody()));
        assertNotNull(deadLetter.getMessageProperties().getHeaders().get("x-exception-stacktrace"));
        break;
      }
      Thread.sleep(100);
    }
    assertTrue(n < 100);

    bus.unbindConsumer("dlqpubtest", moduleInputChannel);
    admin.deleteQueue("xdbustest.dlqpubtest.dlq");
    admin.deleteQueue("xdbustest.dlqpubtest");
    admin.deleteExchange("xdbustest.DLX");
  }
  @Test
  public void testAutoBindDLQ() throws Exception {
    RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());

    MessageBus bus = getMessageBus();
    Properties properties = new Properties();
    properties.put("prefix", "xdbustest.");
    properties.put("autoBindDLQ", "true");
    properties.put("maxAttempts", "1"); // disable retry
    properties.put("requeue", "false");
    DirectChannel moduleInputChannel = new DirectChannel();
    moduleInputChannel.setBeanName("dlqTest");
    moduleInputChannel.subscribe(
        new MessageHandler() {

          @Override
          public void handleMessage(Message<?> message) throws MessagingException {
            throw new RuntimeException("foo");
          }
        });
    bus.bindConsumer("dlqtest", moduleInputChannel, properties);

    RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
    template.convertAndSend("", "xdbustest.dlqtest", "foo");

    int n = 0;
    while (n++ < 100) {
      Object deadLetter = template.receiveAndConvert("xdbustest.dlqtest.dlq");
      if (deadLetter != null) {
        assertEquals("foo", deadLetter);
        break;
      }
      Thread.sleep(100);
    }
    assertTrue(n < 100);

    bus.unbindConsumer("dlqtest", moduleInputChannel);
    admin.deleteQueue("xdbustest.dlqtest.dlq");
    admin.deleteQueue("xdbustest.dlqtest");
    admin.deleteExchange("xdbustest.DLX");
  }
  @Test
  public void testSendAndReceiveBad() throws Exception {
    MessageBus messageBus = getMessageBus();
    DirectChannel moduleOutputChannel = new DirectChannel();
    DirectChannel moduleInputChannel = new DirectChannel();
    messageBus.bindProducer("bad.0", moduleOutputChannel, null);
    messageBus.bindConsumer("bad.0", moduleInputChannel, null);
    Message<?> message =
        MessageBuilder.withPayload("bad").setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar").build();
    final CountDownLatch latch = new CountDownLatch(3);
    moduleInputChannel.subscribe(
        new MessageHandler() {

          @Override
          public void handleMessage(Message<?> message) throws MessagingException {
            latch.countDown();
            throw new RuntimeException("bad");
          }
        });
    moduleOutputChannel.send(message);
    assertTrue(latch.await(10, TimeUnit.SECONDS));
    messageBus.unbindConsumers("bad.0");
    messageBus.unbindProducers("bad.0");
  }
  @SuppressWarnings("unchecked")
  @Test
  public void testBatchingAndCompression() throws Exception {
    RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
    MessageBus bus = getMessageBus();
    Properties properties = new Properties();
    properties.put("deliveryMode", "NON_PERSISTENT");
    properties.put("batchingEnabled", "true");
    properties.put("batchSize", "2");
    properties.put("batchBufferLimit", "100000");
    properties.put("batchTimeout", "30000");
    properties.put("compress", "true");

    DirectChannel output = new DirectChannel();
    output.setBeanName("batchingProducer");
    bus.bindProducer("batching.0", output, properties);

    while (template.receive("xdbus.batching.0") != null) {}

    Log logger =
        spy(
            TestUtils.getPropertyValue(
                bus, "messageBus.compressingPostProcessor.logger", Log.class));
    new DirectFieldAccessor(TestUtils.getPropertyValue(bus, "messageBus.compressingPostProcessor"))
        .setPropertyValue("logger", logger);
    when(logger.isTraceEnabled()).thenReturn(true);

    assertEquals(
        Deflater.BEST_SPEED,
        TestUtils.getPropertyValue(bus, "messageBus.compressingPostProcessor.level"));

    output.send(new GenericMessage<>("foo".getBytes()));
    output.send(new GenericMessage<>("bar".getBytes()));

    Object out = spyOn("batching.0").receive(false);
    assertThat(out, instanceOf(byte[].class));
    assertEquals(
        "\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", new String((byte[]) out));

    ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
    verify(logger).trace(captor.capture());
    assertThat(captor.getValue().toString(), containsString("Compressed 14 to "));

    QueueChannel input = new QueueChannel();
    input.setBeanName("batchingConsumer");
    bus.bindConsumer("batching.0", input, null);

    output.send(new GenericMessage<>("foo".getBytes()));
    output.send(new GenericMessage<>("bar".getBytes()));

    Message<byte[]> in = (Message<byte[]>) input.receive(10000);
    assertNotNull(in);
    assertEquals("foo", new String(in.getPayload()));
    in = (Message<byte[]>) input.receive(10000);
    assertNotNull(in);
    assertEquals("bar", new String(in.getPayload()));
    assertNull(in.getHeaders().get(AmqpHeaders.DELIVERY_MODE));

    bus.unbindProducers("batching.0");
    bus.unbindConsumers("batching.0");
  }
  @Test
  public void testAutoBindDLQPartioned() throws Exception {
    RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource());

    MessageBus bus = getMessageBus();
    Properties properties = new Properties();
    properties.put("prefix", "xdbustest.");
    properties.put("autoBindDLQ", "true");
    properties.put("maxAttempts", "1"); // disable retry
    properties.put("requeue", "false");
    properties.put("partitionIndex", "0");
    DirectChannel input0 = new DirectChannel();
    input0.setBeanName("test.input0DLQ");
    bus.bindConsumer("partDLQ.0", input0, properties);
    properties.put("partitionIndex", "1");
    DirectChannel input1 = new DirectChannel();
    input1.setBeanName("test.input1DLQ");
    bus.bindConsumer("partDLQ.0", input1, properties);

    properties.clear();
    properties.put("prefix", "xdbustest.");
    properties.put(
        "partitionKeyExtractorClass",
        "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
    properties.put(
        "partitionSelectorClass",
        "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
    properties.put(BusProperties.NEXT_MODULE_COUNT, "2");
    DirectChannel output = new DirectChannel();
    output.setBeanName("test.output");
    bus.bindProducer("partDLQ.0", output, properties);

    final CountDownLatch latch0 = new CountDownLatch(1);
    input0.subscribe(
        new MessageHandler() {

          @Override
          public void handleMessage(Message<?> message) throws MessagingException {
            if (latch0.getCount() <= 0) {
              throw new RuntimeException("dlq");
            }
            latch0.countDown();
          }
        });

    final CountDownLatch latch1 = new CountDownLatch(1);
    input1.subscribe(
        new MessageHandler() {

          @Override
          public void handleMessage(Message<?> message) throws MessagingException {
            if (latch1.getCount() <= 0) {
              throw new RuntimeException("dlq");
            }
            latch1.countDown();
          }
        });

    output.send(new GenericMessage<Integer>(1));
    assertTrue(latch1.await(10, TimeUnit.SECONDS));

    output.send(new GenericMessage<Integer>(0));
    assertTrue(latch0.await(10, TimeUnit.SECONDS));

    output.send(new GenericMessage<Integer>(1));

    RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource());
    template.setReceiveTimeout(10000);

    String streamDLQName = "xdbustest.partDLQ.0.dlq";

    org.springframework.amqp.core.Message received = template.receive(streamDLQName);
    assertNotNull(received);
    assertEquals(1, received.getMessageProperties().getHeaders().get("partition"));

    output.send(new GenericMessage<Integer>(0));
    received = template.receive(streamDLQName);
    assertNotNull(received);
    assertEquals(0, received.getMessageProperties().getHeaders().get("partition"));

    admin.deleteQueue(streamDLQName);
    admin.deleteQueue("xdbustest.partDLQ.0-0");
    admin.deleteQueue("xdbustest.partDLQ.0-1");
    admin.deleteExchange("xdbustest.DLX");
  }
  @Test
  public void testConsumerProperties() throws Exception {
    MessageBus bus = getMessageBus();
    Properties properties = new Properties();
    properties.put(
        "transacted", "true"); // test transacted with defaults; not allowed with ackmode NONE
    bus.bindConsumer("props.0", new DirectChannel(), properties);
    @SuppressWarnings("unchecked")
    List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
    assertEquals(1, bindings.size());
    AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
    SimpleMessageListenerContainer container =
        TestUtils.getPropertyValue(
            endpoint, "messageListenerContainer", SimpleMessageListenerContainer.class);
    assertEquals(AcknowledgeMode.AUTO, container.getAcknowledgeMode());
    assertEquals("xdbus.props.0", container.getQueueNames()[0]);
    assertTrue(TestUtils.getPropertyValue(container, "transactional", Boolean.class));
    assertEquals(1, TestUtils.getPropertyValue(container, "concurrentConsumers"));
    assertNull(TestUtils.getPropertyValue(container, "maxConcurrentConsumers"));
    assertTrue(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class));
    assertEquals(1, TestUtils.getPropertyValue(container, "prefetchCount"));
    assertEquals(1, TestUtils.getPropertyValue(container, "txSize"));
    Advice retry = TestUtils.getPropertyValue(container, "adviceChain", Advice[].class)[0];
    assertEquals(3, TestUtils.getPropertyValue(retry, "retryOperations.retryPolicy.maxAttempts"));
    assertEquals(
        1000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.initialInterval"));
    assertEquals(
        10000L, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.maxInterval"));
    assertEquals(
        2.0, TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.multiplier"));
    bus.unbindConsumers("props.0");
    assertEquals(0, bindings.size());

    properties = new Properties();
    properties.put("ackMode", "NONE");
    properties.put("backOffInitialInterval", "2000");
    properties.put("backOffMaxInterval", "20000");
    properties.put("backOffMultiplier", "5.0");
    properties.put("concurrency", "2");
    properties.put("maxAttempts", "23");
    properties.put("maxConcurrency", "3");
    properties.put("prefix", "foo.");
    properties.put("prefetch", "20");
    properties.put("requestHeaderPatterns", "foo");
    properties.put("requeue", "false");
    properties.put("txSize", "10");
    properties.put("partitionIndex", 0);
    bus.bindConsumer("props.0", new DirectChannel(), properties);

    @SuppressWarnings("unchecked")
    List<Binding> bindingsNow = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
    assertEquals(1, bindingsNow.size());
    endpoint = bindingsNow.get(0).getEndpoint();
    container = verifyContainer(endpoint);

    assertEquals("foo.props.0", container.getQueueNames()[0]);

    try {
      bus.bindPubSubConsumer("dummy", null, properties);
      fail("Expected exception");
    } catch (IllegalArgumentException e) {
      assertThat(
          e.getMessage(),
          allOf(
              containsString("RabbitMessageBus does not support consumer properties: "),
              containsString("partitionIndex"),
              containsString("concurrency"),
              containsString(" for dummy.")));
    }
    try {
      bus.bindConsumer("queue:dummy", null, properties);
      fail("Expected exception");
    } catch (IllegalArgumentException e) {
      assertEquals(
          "RabbitMessageBus does not support consumer property: partitionIndex for queue:dummy.",
          e.getMessage());
    }

    bus.unbindConsumers("props.0");
    assertEquals(0, bindingsNow.size());
  }