Use this task to develop a JMS client application to use
messages to communicate with enterprise applications.
About this task
This topic gives an overview of the steps needed to develop a JMS client application. This topic
only describes the JMS-related case; it does not describe general client programming, which you
should already be familiar with. For detailed information about these steps, and for examples of
developing JMS clients, see the Java
Message Service documentation and the Using Java section of the WebSphere® MQ documentation.
A
JMS client assumes that the JMS resources (such as a queue connection
factory and queue destination) already exist. A client application
can obtain suitable JMS resources either by JNDI lookup or programmatically
without using JNDI.
For information about the Thin Client for JMS with WebSphere Application Server, which is an embeddable
technology that provides JMS V1.1 connections to a WebSphere Application Server default messaging provider
messaging engine, see Using JMS to connect to a WebSphere Application Server default messaging provider messaging engine.
To use JMS, a typical
JMS client program completes the following general steps. This example
is based on the use of JNDI lookups to obtain JMS resources.
Procedure
- Import JMS packages.
An enterprise application that uses JMS starts with a number of import statements for JMS; for
example:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.jms.*;
- Get an initial context.
try {
ctx = new InitialContext(env);
...
- Define the parameters that the client is to use; for example,
to identify the queue connection factory and to assemble a message
to be sent.
public class JMSppSampleClient
{
public static void main(String[] args)
throws JMSException, Exception
{
String messageID = null;
String outString = null;
String qcfName = "java:comp/env/jms/ConnectionFactory";
String qnameIn = "java:comp/env/jms/Q1";
String qnameOut = "java:comp/env/jms/Q2";
boolean verbose = false;
QueueSession session = null;
QueueConnection connection = null;
Context ctx = null;
QueueConnectionFactory qcf = null;
Queue inQueue = null;
Queue outQueue = null;
...
- Retrieve administered objects from the JNDI namespace.
The InitialContext.lookup() method is used to retrieve administered objects (a queue
connection factory and the queue
destinations):
qcf = (QueueConnectionFactory)ctx.lookup( qcfName );
...
inQueue = (Queue)ctx.lookup( qnameIn );
outQueue = (Queue)ctx.lookup( qnameOut );
...
- Create a connection to the messaging service provider.
The connection provides access to the underlying transport, and is used to create sessions.
The createQueueConnection() method on the factory object is used to create the
connection.
connection = qcf.createQueueConnection();
The JMS
specification defines that connections should be created in the stopped state. Until the connection
starts, MessageConsumers that are associated with the connection cannot receive any messages. To
start the connection, issue the following
command:
connection.start();
- Create a session, for sending and receiving messages.
The session provides a context for producing and consuming messages, including the methods
used to create MessageProducers and MessageConsumers. The createQueueSession method is used on the
connection to obtain a session. The method takes two parameters:
- A boolean that determines whether the session is transacted.
- A parameter that determines the acknowledge mode.
boolean transacted = false;
session = connection.createQueueSession( transacted,
Session.AUTO_ACKNOWLEDGE);
In
this example, the session is not transacted, and it should automatically acknowledge received
messages. With these settings, a message is backed out only after a system error or if the client
application terminates unexpectedly.
- Send the message.
- Create MessageProducers to create messages.
For point-to-point the MessageProducer is a QueueSender that is created by passing an output
queue object (retrieved earlier) into the createSender method on the session. A QueueSender is
usually created for a specific queue, so that all messages sent by that sender are sent to the same
destination.
QueueSender queueSender = session.createSender(inQueue);
- Create the message.
Use the session to create
an empty message and add the data passed.
JMS provides several message
types, each of which embodies some knowledge of its content. To avoid
referencing the vendor-specific class names for the message types,
methods are provided on the Session object for message creation.
In this example, a text message is created from the outString property, which could be provided
as an input parameter on invocation of the client program or constructed in some other
way:
TextMessage outMessage = session.createTextMessage(outString);
- Send the message.
To send the message, the message is passed to the send method on the
QueueSender:
queueSender.send(outMessage);
- Receive replies.
- Create a correlation ID to link the message sent with
any replies.
In this example, the client receives reply messages that are related to the message that it
has sent, by using a provider-specific message ID in a
JMSCorrelationID.
messageID = outMessage.getJMSMessageID();
The
correlation ID is then used in a message selector, to select only messages that have that
ID:
String selector = "JMSCorrelationID = '"+messageID+"'";
- Create a MessageReceiver to receive messages.
For point-to-point the MessageReceiver is a QueueReceiver that is created by passing an input
queue object (retrieved earlier) and the message selector into the createReceiver method on the
session.
QueueReceiver queueReceiver = session.createReceiver(outQueue, selector);
- Retrieve the reply message.
To retrieve a reply message, the receive method on the QueueReceiver is
used:
Message inMessage = queueReceiver.receive(2000);
The parameter
in the receive call is a timeout in milliseconds. This parameter defines how long the method should
wait if there is no message available immediately. If you omit this parameter, the call blocks
indefinitely. If you do not want any delay, use the receiveNoWait()method. In this example, the
receive call returns when the message arrives, or after 2000ms, whichever is sooner.
- Act on the message received.
When a message is received, you can act on it as needed by the business logic of the client.
Some general JMS actions are to check that the message is of the correct type and extract the
content of the message. To extract the content from the body of the message, cast from the generic
Message class (which is the declared return type of the receive methods) to the more specific
subclass, such as TextMessage. It is good practice always to test the message class before casting,
so that unexpected errors can be handled gracefully.
In this example, the instanceof operator is
used to check that the message received is of the TextMessage type. The message content is then
extracted by casting to the TextMessage
subclass.
if ( inMessage instanceof TextMessage )
...
String replyString = ((TextMessage) inMessage).getText();
- Closing down.
If the application needs to create many short-lived JMS objects at the Session level or lower,
it is important to close all the JMS resources used. To do this, you call the close() method on the
various classes (QueueConnection, QueueSession, QueueSender, and QueueReceiver) when the resources
are no longer
required.
queueReceiver.close();
...
queueSender.close();
...
session.close();
session = null;
...
connection.close();
connection = null;
- Publishing and subscribing messages.
To use publish/subscribe support instead of point-to-point messaging, the general client
actions are the same; for example, to create a session and connection. The exceptions are that topic
resources are used instead of queue resources (such as TopicPublisher instead of QueueSender), as
shown in the following example to publish a
message:
// Creating a TopicPublisher
TopicPublisher pub = session.createPublisher(topic);
...
pub.publish(outMessage);
...
// Closing TopicPublisher
pub.close();
- Handling errors
Any JMS runtime errors are reported by exceptions. The majority of methods in JMS throw
JMSExceptions to indicate errors. It is good programming practice to catch these exceptions and
display them on a suitable output.
Unlike normal Java™ exceptions, a JMSException
can contain another exception embedded in it. The implementation of JMSException does not include
the embedded exception in the output of its toString()method. Therefore, you must check explicitly
for an embedded exception and print it out, as shown in the following
example:
catch (JMSException je)
{
System.out.println("JMS failed with "+je);
Exception le = je.getLinkedException();
if (le != null)
{
System.out.println("linked exception "+le);
}
}