Spring JMSTemplate在一个事务中接收所有消息

时间:2013-07-30 11:08:40

标签: spring transactions jms jmstemplate

我试图使用Spring JMSTemplate.receive(String)方法以同步模式从队列中获取所有消息。

问题是我总是只收到一条消息。这是代码:

@Transactional
public List<Message> receiveAllFromQueue(String destination) {
  List<Message> messages = new ArrayList<Message>();
  Message message;
  while ((message = queueJmsTemplate.receive(destination)) != null) {
    messages.add(message);
  }
  return messages;
}

如果我删除了@Transactional注释,我会收到所有消息,但所有消息都是在事务中完成的,所以如果稍后在处理这些消息时会有一个例外,消息将会丢失。

这是我的JMSTemplate bean的定义。

<bean id="queueJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="pubSubDomain" value="false" />
    <property name="receiveTimeout" value="1" />
   <property name="sessionTransacted" value="true" />
</bean>

我想要实现的是拥有一个事务,并且在此事务中我希望获得所有待处理的消息。

2 个答案:

答案 0 :(得分:4)

我会回复自己。看起来JMSTemplate不支持它。暂时解决它的唯一方法是扩展JMSTemplate并添加使用JMSTemplate部分的新方法。不幸的是,有些方法是私有的,因此需要复制......

public class CustomQueueJmsTemplate extends JmsTemplateDelegate {

  public List<Message> receiveAll(String destinationName) {
    return receiveAll(destinationName, null);
  }

  public List<Message> receiveAll(final String destinationName, final String messageSelector) {
    return execute(new SessionCallback<List<Message>>() {
      @Override
      public List<Message> doInJms(Session session) throws JMSException {
        Destination destination = resolveDestinationName(session, destinationName);
        return doReceiveAll(session, destination, messageSelector);
      }
    }, true);
  }

  private List<Message> doReceiveAll(Session session, Destination destination, String messageSelector)
      throws JMSException
  {
    return doReceiveAll(session, createConsumer(session, destination, messageSelector));
  }

  private List<Message> doReceiveAll(Session session, MessageConsumer consumer) throws JMSException {
    try {
      // Use transaction timeout (if available).
      long timeout = getReceiveTimeout();
      JmsResourceHolder resourceHolder = (JmsResourceHolder) TransactionSynchronizationManager
          .getResource(getConnectionFactory());
      if (resourceHolder != null && resourceHolder.hasTimeout()) {
        timeout = resourceHolder.getTimeToLiveInMillis();
      }

      // START OF MODIFIED CODE
      List<Message> messages = new ArrayList<>();
      Message message;
      while ((message = doReceive(consumer, timeout)) != null) {
        messages.add(message);
      }
      // END OF MODIFIED CODE

      if (session.getTransacted()) {
        // Commit necessary - but avoid commit call within a JTA transaction.
        if (isSessionLocallyTransacted(session)) {
          // Transacted session created by this template -> commit.
          JmsUtils.commitIfNecessary(session);
        }
      } else if (isClientAcknowledge(session)) {
        // Manually acknowledge message, if any.
        for (Message retrievedMessages : messages) {
          retrievedMessages.acknowledge();
        }
      }
      return messages;
    }
    finally {
      JmsUtils.closeMessageConsumer(consumer);
    }
  }

  private Message doReceive(MessageConsumer consumer, long timeout) throws JMSException {
    if (timeout == RECEIVE_TIMEOUT_NO_WAIT) {
      return consumer.receiveNoWait();
    } else if (timeout > 0) {
      return consumer.receive(timeout);
    } else {
      return consumer.receive();
    }
  }

}

答案 1 :(得分:1)

JmsTemplate的receive方法每次都会创建一个新的MessageConsumer。 第二次,您的事务尚未提交,Spring会在第一次接收时预取许多消息。 那时没有消息可提取,导致您的接听电话为空。

Spring中的

JmsTemplate有一个execute方法,该方法将SessionCallback作为参数。这使您可以针对JmsTemplate的基础会话运行自己的代码。 仅创建一个MessageConsumer应该可以解决您的问题。

$(...)