1. Indroduction and definitions
JMS stands for Java Message Service. Messaging is a method of communication
between software components or applications. It is a peer-to-peer
system, that is, both the origin and the destination of a communication have equal
capabilties in contrast with client/server system. Messaging clients, once connected
to the messaging agent can send and receive messages to and from each other.
The messaging agent provides the facilities to create, send, receive, and read messages.
The Messaging system provides distributed communication not strictly coupled; in the sens
that, as an email, a message sent to a destination is retreived from its recipient. The
availability of the sender and the receiver is not required in order to communicate. We
need to know only about the message format and the destination to use. In RMI, we need
instead, to know a remote application's methods. The difference between an email and JMS
is that JMS is related only to communication between software applications or software
components.
Java Message Service is a Java API (Application Programming Interface). It allows
applications, written in Java, to create, send, receive, and read messages, as well
as with other messaging implementations. It provides the features to support messaging
applications. The JMS API enables communication that is not only loosely coupled but also
asynchronous (JMS provider can deliver messages to a client as they arrive), and
reliable (JMS API can ensure that a message is delivered once and only once).
2. JMS architecture
JMS architecture contains three actions:
1. Administrative tools allow binding connection factories into a
JNDI API namespace,
2. A JMS client can then look up the administered objects in the namespace,
3. The JMS client establishes then a logical connection to the same
objects through the JMS provider.
3. Messaging domains
In JMS, J2EE provider implements PTP and Publish/Subscribe domains.
3.1. PTP Mechanism
A point-to-point (PTP) application contains three elements: queue, sender,
and receiver. The message is:
1 addressed to a queue,
2 extracted from the queue by the receiver client,
3 acknowledged receipt.
Like un email provinder, each message has only one consumer; sender and
receiver have no timing dependencies. Queues retain all messages until
they are retreived or expired.
3.2. Publish/Subscribe
In a publish/subscribe (Pub/Sub) application, Publishers and
Subscribers are generally anounymous. The job of the Topic is
to take and distribute (deliver) messages from publishers to
subscribers. The Clients (as 1 in the figure) Publish (address)
messages to the Topic. Clients (as 2 and 3 in the figure) Subscribe
first to the topic and receive messages after. The related transaction
is dynamic; and the topic retains messages only as long as it
takes to distribute them to current subscribers.Each message may have
multiple consumers. Publishers and subscribers
have a timing dependency. However, the JMS API allows clients to create
permanent subscriptions even if the subscribers are not active.
Messaging products can be consumed synchronously or asynchronously. For the
first case, a subscriber fetches a message from the Topic by calling the
receive method. In the second case, a publisher can register a message
listener with a consumer.The JMS provider delivers the message by calling the
listener's onMessage method, which acts on the contents of the message.
4. JMS API Programming Model
In this paragraph, we will create and use the transaction objects.
4.1. Connection Factories
A connection factory is the object that a client uses
to create a connection with a provider.
J2EE SDK comes with a pair of preconfigured connection
factories which are QueueConnectionFactory and
TopicConnectionFactory interfaces.
It is possible to create new connection factories by using the following command-lines:
C:\> j2eeadmin -addJmsFactory jndi_name queue
C:\> j2eeadmin -addJmsFactory jndi_name topic
We perform first a JNDI API lookup of the connection factory,
and set an InitialContext object to use to look up the
QueueConnectionFactory and the TopicConnectionFactory; as:
Context ctx = new InitialContext();
//For queue:
QueueConnectionFactory queueConnectionFactory =
(QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");
//For Topic:
TopicConnectionFactory topicConnectionFactory =
(TopicConnectionFactory) ctx.lookup("TopicConnectionFactory");
Calling the InitialContext method with no parameters: InitialContext();
will use the file named jndi.properties, that indicates the JNDI API
implementation and the namespace to use.
4.2. Destinations
A destination is the object a client uses to specify the target
of messages it produces and the source of messages it receives.
In the PTP messaging domain, destinations are called queues;
we use the following J2EE SDK command to create them:
j2eeadmin -addJmsDestination queue_name queue
In the pub/sub messaging domain, destinations are called topics,
that are created by the following J2EE SDK command-line:
C:\> j2eeadmin -addJmsDestination topic_name topic
A JMS application may use multiple queues and/or topics.
In addition to looking up a connection factory, we look up
a destination. If we have created a topic named "TheTopic", we perform a JNDI API
lookup of TheTopic and assign it to a Topic object:
Topic TheTopic = (Topic) ctx.lookup("TheTopic");
For a queue domain, we have:
Queue TheQueue = (Queue) ctx.lookup("TheQueue");
That looks up a queue named TheQuue and assigns it to a Queue object.
4.3. Connections
A connection with a JMS provider is a virtual.
It could represent an open TCP/IP socket between a client
and a provider service daemon. Once a connection is created, we can then
create one or more sessions.
Like connection factories, connections have two forms that implement
the QueueConnection or the TopicConnection interface. We use
QueueConnectionFactory or a TopicConnectionFactory object to create a
connection:
//For Queue:
QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();
//For Topic:
TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();
The time an application is completed, we need to close connections
that we have created. Closing a connection also closes its sessions and communications.
We use the following codes:
queueConnection.close();
topicConnection.close();
Before to use an application , we have to call the connection's start method.
To stop message delivery temporarily without closing the connection, we call the stop method.
4.4. Sessions
A session is a single-threaded context to produce and receive messages. Like connections,
Sessions have two forms, implementing the QueueSession or the TopicSession interface.
Once TopicConnection object, or QueueConnection object is created , we use
it to successively create a TopicSession, or a QueueSession:
TopicSession topicSession =
topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
The first argument means that the session is not transacted; the second means that the
session automatically acknowledges messages when they have been received successfully.
QueueSession queueSession = queueConnection.createQueueSession(true, 0);
Here, the first argument means that the session is transacted; the second indicates that
message acknowledgment is not specified for transacted sessions.
4.4. Message Producers
A message producer is an object created by a session and
is used for sending messages to a destination. The PTP form
of a message producer implements the QueueSender interface.
The pub/sub form implements the TopicPublisher interface.
If we use a QueueSession to create a sender for
the queue "TheQueue", and TopicSession to create a
publisher for the topic "TheTopic"; the related codes are:
QueueSender queueSender = queueSession.createSender(TheQueue);
TopicPublisher topicPublisher = topicSession.createPublisher(TheTopic);
We can create an unidentified producer by specifying null as
the argument to createSender or createPublisher.
Once the message producer is created, we can use it to send
messages first created .
queueSender.send(message);
With a TopicPublisher, you use the publish method:
topicPublisher.publish(message);
For a created unidentified producer, use the overloaded send or publish
method that specifies the destination as the first parameter.
4.5. Message Consumers
A message consumer is an object created by a session, and used to
receive messages sent to a destination. It allows a
JMS client to register interest in a destination with a JMS provider,
which manages the delivery of messages from a destination
to the registered consumers. The destination is either queue or topic.
The PTP form of message consumer implements the QueueReceiver interface.
The pub/sub form implements the TopicSubscriber interface.
If we use a QueueSession to create a receiver for the queue
"TheQueue", and a TopicSession to create a subscriber for the topic
"TheTopic"; the related codes are:
//For Queue:
QueueReceiver queueReceiver = queueSession.createReceiver(TheQueue);
//For Topic:
TopicSubscriber topicSubscriber = topicSession.createSubscriber(TheTopic);
We use the TopicSession.createDurableSubscriber method to create a
durable topic subscriber.
Once we have created a message consumer, it becomes active, and we can
use it to receive messages. We can use the close method for a QueueReceiver
or a TopicSubscriber to make the message consumer inactive.
With a QueueReceiver or a TopicSubscriber, we use the receive method to
consume a message synchronously. This method can be used at any time after
the start method is called:
queueConnection.start();
Message m = queueReceiver.receive();
topicConnection.start();
Message m = topicSubscriber.receive(1500);
1500 represents the time that lasts the message before a time out.
To consume a message asynchronously, we use a message listener.
4.6. Message Listeners
A message listener is an object that acts as an asynchronous event handler
for messages. This object implements the MessageListener interface, which
contains one method, onMessage, in which we define the
actions to be taken when a message arrives.
We register the message listener with QueueReceiver or TopicSubscriber
by using the setMessageListener method.
If we define a class named TheTopicListener that
implements the MessageListener interface, we can register the
message listener as follows:
TheTopicListener topicListener = new TheTopicListener();
topicSubscriber.setMessageListener(topicListener);
After the message listener is registred first, we call then start
method on the QueueConnection or the TopicConnection to begin a message delivery.
Once message delivery begins, the message consumer automatically calls the message
listener's onMessage method whenever a message is delivered.
The onMessage method takes
one argument of type Message, which the method can cast to any of the other message types.
A message listener is used to any destination type (queue or topic). The
same listener can obtain messages from either a queue or a topic, depending
on the used object QueueReceiver or a TopicSubscriber.
A message listener needs a message type and format, and destination
type to reply to a message and create a producer for the destination type.
4.7. Message Selectors
Our applications need to filter the messages it receives. JMS API message
selector is used for this purpose. Filtering messages is done by the
JMS provider.
A message selector is a String that contains an expression that we
do not desire. The syntax of the expression is based on a subset of the
SQL92 conditional expression syntax. The createReceiver, createSubscriber,
and createDurableSubscriber methods have forms to specify a message
selector as an argument when we create a message consumer.
The message consumer then receives only messages filtred with respect to
their headers and properties, not to message body.
5. Examples
Here two examples. One for Queue domain, the second for Topic domain.
For this, we need a java compiler, jms and corba related classes. The Java
compiler J2SE v 1.4.2_04 and the j2sdkee1.3.1 server have the necessary
to run these two examples.
We can dowload J2SE v 1.4.2_04 compiler at
v 1.4.2_04,
and the server j2sdkee1.3.1 at
j2sdkee1.3.1,
Unzip the related file; then go to: Start - Setting - control panel - Advanced -
Environment Variables and set the variables and their values.
Place the following values AT FIRST
JAVA_HOME : c:\j2sdk1.4.2_04
J2EE_HOME : c:\j2sdkee1.3.1
PATH : C:\j2sdk1.4.2_04\bin;C:\j2sdkee1.3.1\bin
CLASSPATH : .;C:\j2sdkee1.3.1\lib\j2ee.jar;C:\j2sdkee1.3.1\lib\locale
5.1.PTP Queue domain
5.1.1. The steps of the programs
5.1.1.1. Sending programs
The sending program will have the following steps:
1. JNDI API lookup of the QueueConnectionFactory and queue,
2. Creating a connection and a session,
3. Creating a QueueSender,
4. Creating a TextMessage,
5. Sending messages to the queue,
6. Sending a control message to set the end of the message stream,
7. Closing the connection (then the session and QueueSender).
The related progam is:
TheQueueSender.java,
5.1.1.2. Receiving programs
The receiving program will have the following steps:
1. JNDI API lookup of the QueueConnectionFactory and queue,
2. Creating a connection and a session,
3. Creating a QueueReceiver,
4. Starting the connection, the message delivery will then begin,
5. Receiving the messages sent to the queue until the end-of-message-stream control message is received,
6. Closing the connection (then the session and QueueReceiver).
The related progam is:
TheQueueReceiver.java,
5.1.2. Compiling the programs
5.1.1.1. Sending programs
1. Compile:
C:\>javac TheQueueSender.java
C:\>javac TheQueueReceiver.java
2. Start the JMS Provider (SDK):
At another new command line prompt, start the J2EE server as follows:
We get:
j2ee -verbose,
3. Create the JMS Administered Objects:
In the first window in which the programs are compiled, use the j2eeadmin
command to create a queue named TheQueue:
C:\>j2eeadmin -addJmsDestination TheQueue queue
(The last argument is the kind of destination to create)
4. Check wether the queue has been created. Use the following command:
C:\>j2eeadmin -listJmsDestination
5. Run TheQueueSender program; send twho messages. You need to define a value for jms.properties.
C:\>java -Djms.properties=%J2EE_HOME%\config\jms_client.properties TheQueueSender TheQueue 2
6. The output of the program looks like this:
Your queue name is TheQueue
Java(TM) Message Service 1.0.2 Reference Implementation (build b14)
Sending message:
This is the 1 message
Sending message:
This is the 2 message
5.1.1.2. Receiving programs
7. In the same window, run the TheQueueReceiver program:
C:\>java -Djms.properties=%J2EE_HOME%\config\jms_client.properties TheQueueReceiver TheQueue
8. The output of the program looks like this:
The queue name is TheQueue
Java(TM) Message Service 1.0.2 Reference Implementation (build b14)
Reading the message:
This is the 1 message
Reading the message:
This is the 2 message
In a different terminal window, run TheQueueSender program. When the
messages have been sent, (In the case TheQueueReceiver is runned first,
it will display the queue and wait until TheQueueSender program
start. It receives then the messages and exits).
9. To Delete the Queue, perform:
C:\>j2eeadmin -removeJmsDestination TheQueue
10. To stop the server, perform:
5.2.Pub/Sub Topic domain
Here, we will use the publishing and subscribing programs
related to pub/sub domain. We will use also the message listener
that consumes messages asynchronously.
5.2.1. The steps of the programs
5.2.1.1. Publishing Clients programs
Here are the steps:
1. JNDI API lookup of the TopicConnectionFactory and topic
2. Creating a connection and a session
3. Creating a TopicPublisher
4. Creating a TextMessage
5. Publishing messages to the topic
6. Closes the connection (then the session and TopicPublisher)
The related progam is:
TheTopicPublisher.java,
5.2.1.2. Subscribing Clients programs
Here are the steps:
1. Performs a JNDI API lookup of the TopicConnectionFactory and topic,
2. Creates a connection and a session,
3. Creates a TopicSubscriber,
4. Creates an instance of the TextListener class and registers it as the message listener for the TopicSubscriber,
5. Starts the connection, causing message delivery to begin,
6. Listens for the messages published to the topic, stopping when the user enters the character q or Q,
7. Closes the connection, which automatically closes the session and TopicSubscriber,
The related progam is:
TheTopicSubscriber.java,
5.2.1.3. The message listener
1. When a message arrives at destination, the onMessage method is called automatically,
2. The onMessage method converts the incoming message to a TextMessage and displays its content.
The related progam is:
TextListener.java,
5.2.2. Compiling the Pub/Sub Clients programs
5.2.2.1. Sending programs
1. Compile:
C:\> javac TheTopicPublisher.java
C:\> javac TheTopicSubscriber.java
C:\>javac TextListener.java
2. Starting the JMS Provider by starting the J2EE server (if it it is not already running)
in a new another terminal window:
3. Creating the JMS Administered Objects
Create a topic named TheTopic in the first window of command-line.
(the last argument tells the command what kind of destination to create).
C:\>j2eeadmin -addJmsDestination TheTopic topic
4. Check if the ceated destination is here:
C:\>j2eeadmin -listJmsDestination
5. Running the Pub/Sub Clients programs:
C:\>java -Djms.properties=%J2EE_HOME%\config\jms_client.properties TheTopicSubscriber TheTopic
6. The program displays the following lines and hangs:
The name of the topic is TheTopic
Java(TM) Message Service 1.0.2 Reference Implementation (build b14)
To end the program, enter Q or q, then <return>
7. In another terminal window, run the TheTopicPublisher program:
C:\>java -Djms.properties=%J2EE_HOME%\config\jms_client.properties TheTopicPublisher TheTopic 4
8. The output of the program, in this second new windows, looks like this:
The name of Topic is TheTopic
Java(TM) Message Service 1.0.2 Reference Implementation (build b14)
Publishing message:
This is the 1 message
Publishing message:
This is the 2 message
Publishing message:
This is the 3 message
Publishing message:
This is the 4 message
In the first window, the program displays the following:
The name of the topic is TheTopic
Java(TM) Message Service 1.0.2 Reference Implementation (build b14)
To end the program, enter Q or q, then <return>
Reading the message:
This is the 1 message
Reading the message:
This is the 2 message
Reading the message:
This is the 3 message
Reading the message:
This is the 4 message
9. Deleting the Topic:
C:\>j2eeadmin -removeJmsDestination TheTopic
10. Stopping the Server:
|