예제 #1
0
  @Bean
  SimpleMessageListenerContainer container(
      ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.setQueueNames(MAIL_MESSAGE_CMD_QUEUE);
    container.setMessageListener(listenerAdapter);

    return container;
  }
 @Bean
 public SimpleMessageListenerContainer messageListenerContainer(AmqpAdmin amqpAdmin) {
   SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
   container.setConnectionFactory(rabbitConnectionFactory());
   container.setQueueNames(clientQueue(amqpAdmin).getName());
   container.setMessageListener(
       new MessageListenerAdapter(serviceMessageReceiver, messageConverter));
   // No acks will be sent (incompatible with channelTransacted=true). RabbitMQ calls this
   // "autoack" because the broker assumes all messages are acked without any action from the
   // consumer.
   container.setAcknowledgeMode(AcknowledgeMode.NONE);
   return container;
 }
 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;
 }
 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 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());
  }
  /** Verifies that an up-stack RabbitTemplate uses the listener's channel (MessageListener). */
  @SuppressWarnings("unchecked")
  @Test
  public void testMessageListener() throws Exception {
    ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class);
    Connection mockConnection = mock(Connection.class);
    final Channel onlyChannel = mock(Channel.class);
    when(onlyChannel.isOpen()).thenReturn(true);

    final CachingConnectionFactory cachingConnectionFactory =
        new CachingConnectionFactory(mockConnectionFactory);

    when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection);
    when(mockConnection.isOpen()).thenReturn(true);

    final AtomicReference<Exception> tooManyChannels = new AtomicReference<Exception>();

    doAnswer(
            new Answer<Channel>() {
              boolean done;

              @Override
              public Channel answer(InvocationOnMock invocation) throws Throwable {
                if (!done) {
                  done = true;
                  return onlyChannel;
                }
                tooManyChannels.set(new Exception("More than one channel requested"));
                Channel channel = mock(Channel.class);
                when(channel.isOpen()).thenReturn(true);
                return channel;
              }
            })
        .when(mockConnection)
        .createChannel();

    final AtomicReference<Consumer> consumer = new AtomicReference<Consumer>();

    doAnswer(
            new Answer<String>() {

              @Override
              public String answer(InvocationOnMock invocation) throws Throwable {
                consumer.set((Consumer) invocation.getArguments()[6]);
                return null;
              }
            })
        .when(onlyChannel)
        .basicConsume(
            anyString(),
            anyBoolean(),
            anyString(),
            anyBoolean(),
            anyBoolean(),
            anyMap(),
            any(Consumer.class));

    final CountDownLatch commitLatch = new CountDownLatch(1);
    doAnswer(
            new Answer<String>() {

              @Override
              public String answer(InvocationOnMock invocation) throws Throwable {
                commitLatch.countDown();
                return null;
              }
            })
        .when(onlyChannel)
        .txCommit();

    final CountDownLatch latch = new CountDownLatch(1);
    SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(cachingConnectionFactory);
    container.setMessageListener(
        new MessageListener() {
          @Override
          public void onMessage(Message message) {
            RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
            rabbitTemplate.setChannelTransacted(true);
            // should use same channel as container
            rabbitTemplate.convertAndSend("foo", "bar", "baz");
            latch.countDown();
          }
        });
    container.setQueueNames("queue");
    container.setChannelTransacted(true);
    container.setShutdownTimeout(100);
    container.afterPropertiesSet();
    container.start();

    consumer
        .get()
        .handleDelivery(
            "qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] {0});

    assertTrue(latch.await(10, TimeUnit.SECONDS));

    Exception e = tooManyChannels.get();
    if (e != null) {
      throw e;
    }

    verify(mockConnection, Mockito.times(1)).createChannel();
    assertTrue(commitLatch.await(10, TimeUnit.SECONDS));
    verify(onlyChannel).txCommit();
    verify(onlyChannel)
        .basicPublish(
            Mockito.anyString(),
            Mockito.anyString(),
            Mockito.anyBoolean(),
            Mockito.any(BasicProperties.class),
            Mockito.any(byte[].class));

    // verify close() was never called on the channel
    DirectFieldAccessor dfa = new DirectFieldAccessor(cachingConnectionFactory);
    List<?> channels = (List<?>) dfa.getPropertyValue("cachedChannelsTransactional");
    assertEquals(0, channels.size());

    container.stop();
  }
  /**
   * Verifies that the listener channel is not exposed when so configured and up-stack
   * RabbitTemplate uses the additional channel. created when exposeListenerChannel is false
   * (ChannelAwareMessageListener).
   */
  @SuppressWarnings("unchecked")
  @Test
  public void testChannelAwareMessageListenerDontExpose() throws Exception {
    ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class);
    Connection mockConnection = mock(Connection.class);
    final Channel firstChannel = mock(Channel.class);
    when(firstChannel.isOpen()).thenReturn(true);
    final Channel secondChannel = mock(Channel.class);
    when(secondChannel.isOpen()).thenReturn(true);

    final SingleConnectionFactory singleConnectionFactory =
        new SingleConnectionFactory(mockConnectionFactory);

    when(mockConnectionFactory.newConnection((ExecutorService) null)).thenReturn(mockConnection);
    when(mockConnection.isOpen()).thenReturn(true);

    final AtomicReference<Exception> tooManyChannels = new AtomicReference<Exception>();

    doAnswer(
            new Answer<Channel>() {
              boolean done;

              @Override
              public Channel answer(InvocationOnMock invocation) throws Throwable {
                if (!done) {
                  done = true;
                  return firstChannel;
                }
                return secondChannel;
              }
            })
        .when(mockConnection)
        .createChannel();

    final AtomicReference<Consumer> consumer = new AtomicReference<Consumer>();

    doAnswer(
            new Answer<String>() {

              @Override
              public String answer(InvocationOnMock invocation) throws Throwable {
                consumer.set((Consumer) invocation.getArguments()[6]);
                return null;
              }
            })
        .when(firstChannel)
        .basicConsume(
            anyString(),
            anyBoolean(),
            anyString(),
            anyBoolean(),
            anyBoolean(),
            anyMap(),
            any(Consumer.class));

    final CountDownLatch commitLatch = new CountDownLatch(1);
    doAnswer(
            new Answer<String>() {

              @Override
              public String answer(InvocationOnMock invocation) throws Throwable {
                commitLatch.countDown();
                return null;
              }
            })
        .when(firstChannel)
        .txCommit();

    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicReference<Channel> exposed = new AtomicReference<Channel>();
    SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(singleConnectionFactory);
    container.setMessageListener(
        new ChannelAwareMessageListener() {
          @Override
          public void onMessage(Message message, Channel channel) {
            exposed.set(channel);
            RabbitTemplate rabbitTemplate = new RabbitTemplate(singleConnectionFactory);
            rabbitTemplate.setChannelTransacted(true);
            // should use same channel as container
            rabbitTemplate.convertAndSend("foo", "bar", "baz");
            latch.countDown();
          }
        });
    container.setQueueNames("queue");
    container.setChannelTransacted(true);
    container.setExposeListenerChannel(false);
    container.setShutdownTimeout(100);
    container.afterPropertiesSet();
    container.start();

    consumer
        .get()
        .handleDelivery(
            "qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] {0});

    assertTrue(latch.await(10, TimeUnit.SECONDS));

    Exception e = tooManyChannels.get();
    if (e != null) {
      throw e;
    }

    // once for listener, once for exposed + 0 for template (used bound)
    verify(mockConnection, Mockito.times(2)).createChannel();
    assertTrue(commitLatch.await(10, TimeUnit.SECONDS));
    verify(firstChannel).txCommit();
    verify(secondChannel).txCommit();
    verify(secondChannel)
        .basicPublish(
            Mockito.anyString(),
            Mockito.anyString(),
            Mockito.anyBoolean(),
            Mockito.any(BasicProperties.class),
            Mockito.any(byte[].class));

    assertSame(secondChannel, exposed.get());

    verify(firstChannel, Mockito.never()).close();
    verify(secondChannel, Mockito.times(1)).close();
    container.stop();
  }
  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());
    }
  }