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.
MessageConsumer consumer = session.createConsumer(destination);
The parameter destination is a Queue or Topic object that the application has created previously.
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.
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.
TopicSubscriber subscriber = session.createSubscriber(topic);
The
parameter topic is a Topic object that the application has created previously.
Durable topic subscribers
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.
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.
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.
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
true, as shown in the following example:
MessageConsumer consumer = session.createConsumer(topic, null, true);
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
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.
Tailoring buffer sizes to suit the messages being received
When a message is received from IBM MQ by a non-JMS application a message buffer must be provided by the application for the message to be written into. JMS applications do not need to manually create a buffer. The IBM MQ classes for JMS automatically create and size message buffers to suit the sizes of the messages being received. For most applications, automatically managed buffers provide a suitable balance of performance and convenience for the application developer. In certain circumstances, it might be beneficial to specify the initial size of the message buffer manually. The default initial size of an IBM MQ JMS receive buffer is 4 KB. If an application is always going to receive messages that are 256 KB in size, it might be preferable to configure the initial buffer size to 256 KB. This can avoid the need for the IBM MQ classes for JMS to attempt and fail to receive the message into a 4 KB buffer before resizing it to 256 KB and successfully receiving it. For a client-connected application, this can avoid the need for a potentially wasted network round trip while the IBM MQ classes for JMS determine the correct buffer size to use.
The initial buffer size can be configured by setting the com.ibm.mq.jmqi.defaultMaxMsgSize Java property to your chosen value, in bytes. Note that this property affects all IBM MQ JMS applications that are running inside the Java virtual machine, so be careful not to adversely affect other message consumers that receive messages of a different size.
The IBM MQ classes for JMS still attempt to automatically reduce the size of the buffer if several messages smaller than the configured size are received. By default, this happens if 10 messages are received which are all smaller than the buffer size. For example, if 10 messages are received in a row that are 128 KB in size, the buffer is reduced to from 256 KB to 128 KB. It is then increased again when larger messages are received. It is possible to configure the number of messages that must be received before a buffer is reduced in size. For example, this might be useful if the application is known to receive five large messages followed by 10 smaller messages and then another five large messages. With the default settings, the buffer would be reduced after the 10 smaller messages had been received and would need increasing again for the larger messages. The Java system property com.ibm.mq.jmqi.smallMsgBufferReductionThreshold can be set to the number of messages that must be received before the size of the buffer is reduced. In this example, it could be set to 20 to prevent 10 smaller messages from reducing the buffer size.
The properties can be set independently of each other. For example, you might choose to leave the initial buffer size to its default value of 4 KB but increase the value of com.ibm.mq.jmqi.smallMsgBufferReductionThreshold so once the buffer is increased in size it stays that size for longer.
If large numbers of MQRC_TRUNCATED_MSG_FAILED (2080) return codes are seen for your JMS applications in MQI statistics records, this might be an indication that you would benefit from configuring a higher initial buffer size for those applications, or reducing the frequency with which buffer sizes are reduced. However, it is important to note that for a long running application you are likely to see only a very small number of MQRC_TRUNCATED_MSG_FAILED return codes. This is because typically the buffer is increased to the correct size immediately after the first large message is received, and is not reduced in size unless a number of smaller messages are received. It is therefore possible that a large number of MQRC_TRUNCATED_MSG_FAILED indicates other poor application practices such as connecting to IBM MQ to receive just one or two messages before disconnecting.