Spring - JMS

How JMS works

JMS works like this: Producer produce messages, send to Destination. Consumer receives messages, from Destination. The information exchange happens through Connection and Session.

Key components

Spring Implementation

To implement the JMS function, Spring has several components:

We can think of the first two components as supporting modules. The actual work is done with JmsTemplate.

Example of JmsTempalte

// send
public class JmsQueueSender {
    private JmsTemplate jmsTemplate;
    private Queue queue;

    public void setConnectionFactory(ConnectionFactory cf) {
        this.jmsTemplate = new JmsTemplate(cf);
    }
    public void setQueue(Queue queue) {
        this.queue = queue;
    }
    public void simpleSend() {
        this.jmsTemplate.send(this.queue, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
              return session.createTextMessage("hello queue world");
            }
        });
    }
}

// receive
// use JmsTemplate.doReceive(Session session, Destination destination, java.lang.String messageSelector)

// browse
// use JmsTemplate.browse(...)

Example of Receive Convention

In practice, we usually use Message-Driven POJOs to handle/receive messages.

public class ExampleListener implements MessageListener {
    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                System.out.println(((TextMessage) message).getText());
            }
            catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        }
        else {
            throw new IllegalArgumentException("Message must be of type TextMessage");
        }
    }
}

Spring offers a solution to create message-driven POJOs in a way that does not tie a user to an EJB container using Message Listener Containers.

A Message Listener Container is the intermediary between an MDP and a messaging provider, and takes care of registering to receive messages, participating in transactions, resource acquisition and release, exception conversion and suchlike.

So in real usage, we create a MessageListenerContainer, create a MessageListener (which is the MDP) and tie these two together.

<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />

<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener" />
</bean>

Persistence and Durability

首先需要声明一点,Destination(Queue/Topic)收到消息后,(笔者猜测)应该是有实现Serilization的功能保证消息的持久性,但是这个和下面要讨论的Persistence又是不同的。

在JMS的整个流程中,我们可以分为两个阶段来考虑,如果在某个阶段挂了会发生什么:

example of Durable Subscriber

  <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
      <property name="connectionFactory" ref="jmsConnectionFactory"/>
      <property name="messageListener" ref="messageListener" />
      <property name="destination" ref="datasetTopicUMMY"/>
      <property name="sessionTransacted" value="true" /><!-- whether JMS Sessions are transacted-->
      <property name="pubSubDomain" value="true"/><!--use Publish/Subscribe domain (Topics)-->
      <property name="subscriptionDurable" value="true" /><!--make the subscription durable-->
      <property name="durableSubscriptionName" value="${static.durableSubscriptionName}" /><!--subscriber's name-->
      <property name="autoStartup" value="false" />
  </bean>
Fork me on GitHub