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
considerations; 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 WebSphere MQ Using Java book, SC34-5456.
A
JMS client assumes that the JMS resources (such as a queue connection
factory and queue destination) already exist. A client application
can use JMS resources administered by the application server or administered
by the client container regardless of whether the client application
is running on the same machine as the server or remotely.
For
more information about developing client applications and configuring
JMS resources for them, see Developing J2EE application
client code and related tasks.
To use JMS, a typical
JMS client program completes the following general steps:
- 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 wants 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 or not 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 normally created for a specific
queue, so that all messages sent using 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, you need to 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 need
to 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);
}
}