Receiving messages in a JMS application

An application uses a message consumer to receive messages. A durable topic subscriber is a message consumer that receives all messages sent to a destination, including those sent while the consumer is inactive. An application can select which messages it wants to receive by using a message selector, and can receive messages asynchronously by using a message listener.

An application uses a MessageConsumer object to receive messages. An application creates a MessageConsumer object for a specific destination, which can be a queue or a topic, so that all messages received using the message consumer are received from the same destination. Therefore, before an application can create a MessageConsumer object, it must first create a Queue or Topic object. For information about how to create a Queue or Topic object, see the following topics:
To create a MessageConsumer object, an application uses the createConsumer() method of a Session object, as shown in the following example:

MessageConsumer consumer = session.createConsumer(destination);
The parameter destination is a Queue or Topic object that the application has created previously.
The application then uses the receive() method of the MessageConsumer object to receive a message from the destination, as shown in the following example:

Message inMessage = consumer.receive(1000);
The parameter on the receive() call specifies how long in milliseconds the method waits for a suitable message to arrive if no message is available immediately. If you omit this parameter, the call blocks indefinitely until a suitable message arrives. If you do not want the application to wait for a message, use the receiveNoWait() method instead.

The receive() method returns a message of a specific type. For example, when an application receives a text message, the object returned by the receive() call is a TextMessage object.

However, the declared type of object returned by a receive() call is a Message object. Therefore, in order to extract the data from the body of a message that has just been received, the application must cast from the Message class to the more specific subclass, such as TextMessage. If the type of the message is not known, the application can use the instanceof operator to determine the type. It is always good practice for an application to determine the type of a message before casting so that errors can be handled gracefully.

The following code uses the instanceof operator and shows how to extract the data from the body of a text message:

if (inMessage instanceof TextMessage) {
  String replyString = ((TextMessage) inMessage).getText();
  .
  .
  .
} else {
  // Print error message if Message was not a TextMessage.
  System.out.println("Reply message was not a TextMessage");
}

If an application sends a message within a transaction, the message is not delivered to its destination until the transaction is committed. This means that an application cannot send a message and receive a reply to the message within the same transaction.

If a message consumer receives messages from a destination that is configured for read ahead, any nonpersistent messages that are in the read ahead buffer when the application ends are discarded.

In the publish/subscribe domain, JMS identifies two types of message consumer, nondurable topic subscriber and durable topic subscriber, which are described in the following two sections.

Nondurable topic subscribers

A nondurable topic subscriber receives only those messages that are published while the subscriber is active. A nondurable subscription starts when an application creates a nondurable topic subscriber and ends when the application closes the subscriber, or when the subscriber falls out of scope. As an extension in IBM MQ classes for JMS, a nondurable topic subscriber also receives retained publications.

To create a nondurable topic subscriber, an application can use the domain independent createConsumer() method, specifying a Topic object as the destination. Alternatively, an application can use the domain specific createSubscriber() method, as shown in the following example:

TopicSubscriber subscriber = session.createSubscriber(topic);
The parameter topic is a Topic object that the application has created previously.

Durable topic subscribers

Restriction: An application cannot create durable topic subscribers when using a real-time connection to a broker.

A durable topic subscriber receives all messages that are published during the life of a durable subscription. These messages include all those that are published while the subscriber is not active. As an extension in IBM MQ classes for JMS, a durable topic subscriber also receives retained publications.

To create a durable topic subscriber, an application uses the createDurableSubscriber() method of a Session object, as shown in the following example:

TopicSubscriber subscriber = session.createDurableSubscriber(topic, "D_SUB_000001");
On the createDurableSubscriber() call, the first parameter is a Topic object that the application has created previously, and the second parameter is a name that is used to identify the durable subscription.

The session used to create a durable topic subscriber must have an associated client identifier. The client identifier associated with a session is the same as the client identifier for the connection that is used to create the session. The client identifier can be specified by setting the CLIENTID property of the ConnectionFactory object. Alternatively, an application can specify the client identifier by calling the setClientID() method of the Connection object.

The name that is used to identify a durable subscription must be unique only within the client identifier, and therefore the client identifier forms part of the full, unique identifier of a durable subscription. To continue using a durable subscription that was created previously, an application must create a durable topic subscriber using a session with the same client identifier as that associated with the durable subscription, and using the same subscription name.

A durable subscription starts when an application creates a durable topic subscriber using a client identifier and subscription name for which no durable subscription currently exists. However, a durable subscription does not end when the application closes the durable topic subscriber. To end a durable subscription, an application must call the unsubscribe() method of a Session object that has the same client identifier as that associated with the durable subscription. The parameter on the unsubscribe() call is the subscription name, as shown in the following example:

session.unsubscribe("D_SUB_000001");

The scope of a durable subscription is a queue manager. If a durable subscription exists on one queue manager, and an application connected to another queue manager creates a durable subscription with the same client identifier and subscription name, the two durable subscriptions are completely independent.

Message selectors

An application can specify that only those messages that satisfy certain criteria are returned by successive receive() calls. When creating a MessageConsumer object, the application can specify a Structured Query Language (SQL) expression that determines which messages are retrieved. This SQL expression is called a message selector. The message selector can contain the names of JMS message header fields and message properties. For information about how to construct a message selector, see Message selectors in JMS.

The following example shows how an application can select messages based on a user defined property called myProp:

MessageConsumer consumer;
.
consumer = session.createConsumer(destination, "myProp = 'blue'");

The JMS specification does not allow an application to change the message selector of a message consumer. After an application creates a message consumer with a message selector, the message selector remains for the life of that consumer. If an application requires more than one message selector, the application must create a message consumer for each message selector.

Note that, when an application is connected to a Version 7 queue manager, the MSGSELECTION property of the connection factory has no effect. To optimize performance, all message selection is done by the queue manager.

Suppressing local publications

An application can create a message consumer that ignores publications published on the consumer's own connection. The application does this by setting the third parameter on a createConsumer() call to true, as shown in the following example:

MessageConsumer consumer = session.createConsumer(topic, null, true);
On a createDurableSubscriber() call, the application does this by setting the fourth parameter to true, as shown in the following example

String selector = "company = 'IBM'";
TopicSubscriber subscriber = session.createDurableSubscriber(topic, "D_SUB_000001",
                                                             selector, true);

Asynchronous delivery of messages

An application can receive messages asynchronously by registering a message listener with a message consumer. The message listener has a method called onMessage, which is called asynchronously when a suitable message is available and whose purpose is to process the message. The following code illustrates the mechanism:

import javax.jms.*;

public class MyClass implements MessageListener
{
  // The method that is called asynchronously when a suitable message is available
  public void onMessage(Message message)
  {
    System.out.println("Message is "+message);

    // The code to process the message
    .
    .
    .
  }
}
.
.
.
// Main program (possibly in another class)
.
// Creating the message listener
MyClass listener = new MyClass();

// Registering the message listener with a message consumer
consumer.setMessageListener(listener);

// The main program now continues with other processing

An application can use a session either for receiving messages synchronously using receive() calls, or for receiving messages asynchronously using message listeners, but not for both. If an application needs to receive messages synchronously and asynchronously, it must create separate sessions.

Once a session is set up to receive messages asynchronously, the following methods cannot be called on that session or on objects created from that session:

  • MessageConsumer.receive()
  • MessageConsumer.receive(long)
  • MessageConsumer.receiveNoWait()
  • Session.acknowledge()
  • MessageProducer.send(Destination, Message)
  • MessageProducer.send(Destination, Message, int, int, long)
  • MessageProducer.send(Message)
  • MessageProducer.send(Message, int, int, long)
  • MessageProducer.send(Destination, Message, CompletionListener)
  • MessageProducer.send(Destination, Message, int, int, long, CompletionListener)
  • MessageProducer.send(Message, CompletionListener)
  • MessageProducer.send(Message, int, int, long, CompletionListener)
  • Session.commit()
  • Session.createBrowser(Queue)
  • Session.createBrowser(Queue, String)
  • Session.createBytesMessage()
  • Session.createConsumer(Destination)
  • Session.createConsumer(Destination, String, boolean)
  • Session.createDurableSubscriber(Topic, String)
  • Session.createDurableSubscriber(Topic, String, String, boolean)
  • Session.createMapMessage()
  • Session.createMessage()
  • Session.createObjectMessage()
  • Session.createObjectMessage(Serializable)
  • Session.createProducer(Destination)
  • Session.createQueue(String)
  • Session.createStreamMessage()
  • Session.createTemporaryQueue()
  • Session.createTemporaryTopic()
  • Session.createTextMessage()
  • Session.createTextMessage(String)
  • Session.createTopic()
  • Session.getAcknowledgeMode()
  • Session.getMessageListener()
  • Session.getTransacted()
  • Session.rollback()
  • Session.unsubscribe(String)

If any of these methods are called, a JMSException containing the message:

JMSCC0033: A synchronous method call is not permitted when a session is being used asynchronously:'method name'
is thrown.

Receiving poison messages

An application can receive a message that cannot be processed. There can be several reasons why the message cannot be processed, for example the message might have an incorrect format. Such messages are described as poison messages and require special handling to prevent the message being recursively processed.

For details on how to handle poison messages, see Handling poison messages in IBM MQ classes for JMS.