@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");
  }
  @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");
  }
  @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 testProducerProperties() throws Exception {
    MessageBus bus = getMessageBus();
    bus.bindProducer("props.0", new DirectChannel(), null);
    @SuppressWarnings("unchecked")
    List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
    assertEquals(1, bindings.size());
    AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
    assertEquals(
        "xdbus.props.0", TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKey"));
    MessageDeliveryMode mode =
        TestUtils.getPropertyValue(
            endpoint, "handler.delegate.defaultDeliveryMode", MessageDeliveryMode.class);
    assertEquals(MessageDeliveryMode.PERSISTENT, mode);
    List<?> requestHeaders =
        TestUtils.getPropertyValue(
            endpoint, "handler.delegate.headerMapper.requestHeaderMatcher.strategies", List.class);
    assertEquals(2, requestHeaders.size());
    bus.unbindProducers("props.0");
    assertEquals(0, bindings.size());

    Properties properties = new Properties();
    properties.put("prefix", "foo.");
    properties.put("deliveryMode", "NON_PERSISTENT");
    properties.put("requestHeaderPatterns", "foo");
    properties.put("partitionKeyExpression", "'foo'");
    properties.put("partitionKeyExtractorClass", "foo");
    properties.put("partitionSelectorExpression", "0");
    properties.put("partitionSelectorClass", "foo");
    properties.put(BusProperties.NEXT_MODULE_COUNT, "2");

    bus.bindProducer("props.0", new DirectChannel(), properties);
    assertEquals(1, bindings.size());
    endpoint = bindings.get(0).getEndpoint();
    assertEquals(
        "'foo.props.0-' + headers['partition']",
        TestUtils.getPropertyValue(
                endpoint, "handler.delegate.routingKeyExpression", SpelExpression.class)
            .getExpressionString());
    mode =
        TestUtils.getPropertyValue(
            endpoint, "handler.delegate.defaultDeliveryMode", MessageDeliveryMode.class);
    assertEquals(MessageDeliveryMode.NON_PERSISTENT, mode);
    verifyFooRequestProducer(endpoint);

    try {
      bus.bindPubSubProducer("dummy", new DirectChannel(), properties);
      fail("Expected exception");
    } catch (IllegalArgumentException e) {
      assertThat(
          e.getMessage(),
          allOf(
              containsString("RabbitMessageBus does not support producer properties: "),
              containsString("partitionSelectorExpression"),
              containsString("partitionKeyExtractorClass"),
              containsString("partitionKeyExpression"),
              containsString("partitionSelectorClass")));
      assertThat(e.getMessage(), containsString("for dummy."));
    }
    try {
      bus.bindProducer("queue:dummy", new DirectChannel(), properties);
      fail("Expected exception");
    } catch (IllegalArgumentException e) {
      assertThat(
          e.getMessage(),
          allOf(
              containsString("RabbitMessageBus does not support producer properties: "),
              containsString("partitionSelectorExpression"),
              containsString("partitionKeyExtractorClass"),
              containsString("partitionKeyExpression"),
              containsString("partitionSelectorClass")));
      assertThat(e.getMessage(), containsString("for queue:dummy."));
    }

    bus.unbindProducers("props.0");
    assertEquals(0, bindings.size());
  }