JMS 101

I have a cunning plan. Since I find that my blogging attempts at work get curtailed by well ... work, I'm going to attempt to do most of it sitting at home while I do other things like watch tivo or listen to music. If random quotes get thrown into the middle of the blog, you now know why. Since I attempt, however futilely, to have a life this may or may not work. (don't worry, I promise not to write about that life so you won't have to deal with things like anguish over nail polish colors).

Today - I'm going to start with writing a JMS 101 blog. If you've been using JMS for a long time, I will no doubt bore you to tears (I'll get to more technical stuff in the future) so you may want to simply run away now and try again on another day. If you stay on, you should be warned that my background is on the broker (our server) side of the equation so my terminology may be suspect - I learned JMS from writing tests for my software.

All of this is covered in far more detail in the JMS Specification and MQ has a whole host of sample applications and documentation from our wonderful doc writer that you can look at. I'm not going to pretend to be able to compete with the existing documents. This is the cheating version for people like me who try first and then read the documentation later.

So what do you need to know about JMS to write a simple application:

  • Connection factories
  • Connections
  • Sessions
  • Destinations
  • Messages
  • Producers
  • Consumers

A quick diversion first. JMS has several ways to use the API. You can pick a "domain independent" version or something specific to a type of Destination. For example, there is a QueueConnectionFactory (which can only be used for queues), a TopicConnectionFactory (topic specific) and simply ConnectionFactory which can be used for either. In this blog, I'm going to use the domain independent apis. If you are wondering why there is all this confusion, its all because of how the specification has changed over the revisions.

Connection Factories

ConnectionFactories are a factory for Connections (a little obvious and probably a trifle confusing since we haven't talked about connections yet). The key thing to know about them is that they exist to hide all the proprietary stuff that a JMS provider may want you to set up, for example how to connect to the server. What happens is that you set up a configuration using JNDI which is specific to the implementation that you are using, and then everything in your code is generic. That way, if you switch to use someone else, all you have to do is change those settings. I'll talk about how to set up JNDI and use imqobjmgr (the MQ specific tool for doing that configuration) in a future blog

For now I am going to cheat and use the MQ specific API to create it.(from what I'm seen, other providers have these convenience apis as well but, in the end, you want to use JNDI to retrieve it because it makes your application portable).

ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();

Connections

The JMS Specification defines a Connection as "the client's active connection to a provider". So connection is the stream of messages to and from the provider (aka the implementation used by MQ). In our case, it represents a TCP connection to MQ's server (called the Broker) that handles all of the heavy lifting of routing and persisting messages. When I create a connection, I have the option of passing in a username and password which is authenticated by the provider. (how I actually set that up depends on the specific implementation of JMS being used - for MQ you use imqusermgr if I am using our simple file-based version or else LDAP through JNDI). Since I haven't set up any authentication, I'll just use the defaults and not pass anything in.

I can also start and stop the connection which starts and stops the flow of messages.

Connection connection = cf.createConnection();
connection.start();

Sessions

A session is a single-threaded stream of control of messages. This means that a session can not be used concurrently by two threads at the same time. (warning - once you start feeling good about JMS and decide to fork some of the work on to other threads you are going to break this and get strange behavior - I think everyone does it at least once). Sessions have cool properties. Message order is guaranteed. You can acknowledge or commit (depending on whether or not you are using transactions) several message at a time. You can rollback or recover the messages if you need to reprocess them.

Session are going to be important because everything else starts from here. You create messages, producers, consumers, destinations from the Session.

The first thing you need to know about sessions is acknowledge modes. Acknowledgement is the way the client implementation (whether the piece supplied by MQ or another provider) or the client application (what you write) tells the system (aka notifies the broker) that you are done processing a message.

ModeMeaning
AUTO_ACKNOWLEDGEthe client implementation informs the broker its done once the client has seen it
DUPS_OK_ACKNOWLEGEThe client implementation informs the broker whenever it feels like it that the client has seen the message
CLIENT_ACKNOWLEDGEthe application has control (by calling acknowledge() on the message that its been seen. Note: since the acknowledge() method is on Message, another easy mistake is to assume that only that message is acknowledged when the routine is called - nope - it also acknowledges the messages seen on the client before that message
transactedIdentifies the session as part of a single-phase transaction, so messages on the session (produced and consumed) are handled as a single block - sent or not sent together

At this point, I am going to create a simple session in auto acknowledge mode.

Session session = connection.createSession( false /\* not transacted \*/, Session.AUTO_ACKNOWLEDGE);

JMS has two models for receiving messages. An application can use a "synchronous" api (receive) and explicitly call for a message where it wants it or it can register a message listener (which is a callback that is triggered when a message comes in). For now, I'm not going to set a message listener.

Destinations

I talked a little about destinations in my last entry. I'm not planning to go back over that, its just a storehouse for sending and retrieving a messages. It has a type (queue or topic) and a name. Similar to connection factories, you can retrieve an already created destination using JDNI but I'm going to go ahead and use the simple api's and create it on the fly (this time its not cheating, this is a standard part of the api).

I'll just create a queue, since that seems to lend itself more for a hello world example.

Destination destination = session.createQueue("HelloWorld");

Messages

Messages are the pieces that are passed back and forth - the core of the messaging. There are three pieces to a message:

  • header - stuff stuck on by the provider
  • properties - name/value pairs that tag simple information on to the message. This data can add additional information or even be used to pick out specific messages through what are called selectors (which is a kind of SQL-lite language used to help a receiver identify a specific message)
  • body - the content of the message. It can be text, a java object, a map (so name/value pairs), bytes. At the point I create the message, I determine its type
A message is created of a session (see - sessions are important). For now, I'm going to create a simple text message and put "Hello World" into its body.

TextMessage message = session.createTextMessage();
message.setText("Hello Word");

Producers

Producers put messages into the system. You can specify a lot of information about how the message is handled - how long it will live, whether or not it is persistent (stored on disk), how important it is. Since this is a simple example, I'm going to use the easiest method not set any of those.

MessageProducer producer = session.createProducer(destination);
producer.send(message);

Consumers

Consumers get messages from the system. You can create it with a selector to pull out a specific set of messages but I'm going to go for the simple approach (if nothing else because it always takes me 3 iterations to get the selector right). Since I didn't use a MessageListener (see session) I'm also going to call receive to get the message.

MessageConsumer consumer = session.createConsumer(destination);
Message = consumer.receive();
Putting it together

Now I'm going to put together a pair of simple applications based on what we just discussed. The first will send a message to a queue called HelloWorld. The second will receive the message off of that Queue.

The Producing application

This simple application will:

  1. Create a connection factory (cheating)
  2. Create a connection
  3. Create a session - non-transacted, AUTO_ACKNOWLEDGE
  4. Create the queue HelloWorld
  5. Create a message producer
  6. Start the connection
  7. Create a HelloWorld message
  8. Send the message
  9. Clean everything up


import javax.jms.\*;

/\*\*
 \* simple hello world consumer
 \*/
public class HelloProducer
{
    /\*\*
     \* simple consumer
     \*/
    public HelloProducer() {
        try {
            // creating a connection factory
            // we are cheating here by not using jdni
            ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();

            // create a connection
            Connection connection = cf.createConnection();
           
            // create a session
            Session session = connection.createSession( 
                     false /\* not transacted \*/, Session.AUTO_ACKNOWLEDGE);

            // create destination HelloWorld
            Destination destination = session.createQueue("HelloWorld");

            // create a producer
            MessageProducer producer = session.createProducer(destination);

            // now that everything is ready to go, start the connection
            connection.start();

            // create our message to send
            TextMessage message = session.createTextMessage();
            message.setText("Hello World");

            // send the message to Queue HelloWorld
            System.out.println("Sending Hello World");
            producer.send(message);

            // close everything
            producer.close();
            session.close();
            connection.close();

            
        } catch (JMSException ex) {
            System.out.println("Error running program");
            ex.printStackTrace();
        }
    }


    /\*\*
     \* main method
     \*/
    public static void main(String args[]) {
        new HelloProducer();
    }
}


The Consuming application

This simple application will:

  1. Create a connection factory (cheating)
  2. Create a connection
  3. Create a session - non-transacted, AUTO_ACKNOWLEDGE
  4. Create the queue HelloWorld
  5. Create a message consumer
  6. Start the connection
  7. call receive to get the message from the queue.
  8. Print the body of the message
  9. Clean everything up

import javax.jms.\*;

/\*\*
 \* simple hello world consumer
 \*/
public class HelloConsumer
{
    /\*\*
     \* simple consumer
     \*/
    public HelloConsumer() {
        try {
            // creating a connection factory
            // we are cheating here by not using jdni
            ConnectionFactory cf= new com.sun.messaging.ConnectionFactory();

            // create a connection
            Connection connection = cf.createConnection();
           
            // create a session
            Session session = connection.createSession( 
                     false /\* not transacted \*/, Session.AUTO_ACKNOWLEDGE);

            // create destination HelloWorld
            Destination destination = session.createQueue("HelloWorld");

            // create a consumer
            MessageConsumer consumer = session.createConsumer(destination);

            // now that everything is ready to go, start the connection
            connection.start();

            // receive our message
            TextMessage m = (TextMessage)consumer.receive();

            System.out.println(m.getText());

            // close everything
            consumer.close();
            session.close();
            connection.close();

            
        } catch (JMSException ex) {
            System.out.println("Error running program");
            ex.printStackTrace();
        }
    }


    /\*\*
     \* main method
     \*/
    public static void main(String args[]) {
        new HelloConsumer();
    }
}


Running it with MQ

To run the application we just created, we need to do four things:

  1. Compile the classes
  2. Start the broker (MQ's server)
  3. Run the producing client to send a message
  4. Run the consuming client to receive a message

I'm going to give you the commands for doing these tasks on a Solaris system (since I work at sun, its the machine I normally run). You'll have to change them for other platforms.

Compiling

To compile you need to have imq.jar and jms.jar in the class path.


% javac -classpath /usr/share/lib/imq.jar:/usr/share/lib/jms.jar HelloConsumer.java HelloProducer.java

Starting the broker

To start the broker, you run the command imqbrokerd. When you see a string that says Broker ready, its ready to receive messages.


% imqbrokerd

[19/Oct/2007:16:00:35 PDT]
================================================================================
Sun Java(tm) System Message Queue 4.1
Sun Microsystems, Inc.
Version:  4.1  (Build 34-b)
Compile:  Thu Jun 28 22:33:50 PDT 2007

Copyright (c) 2007 Sun Microsystems, Inc.  All rights reserved.
SUN PROPRIETARY/CONFIDENTIAL.  Use is subject to license terms.

This product includes code licensed from RSA Data Security.
================================================================================
Java Runtime: 1.5.0_09 Sun Microsystems Inc. /usr/jdk/instances/jdk1.5.0/jre
[19/Oct/2007:16:00:35 PDT]    IMQ_HOME=/
[19/Oct/2007:16:00:35 PDT] IMQ_VARHOME=/var/imq
[19/Oct/2007:16:00:35 PDT] SunOS 5.10 sparc hostname (2 cpu) username
[19/Oct/2007:16:00:35 PDT] Max file descriptors: 65536 (65536)
[19/Oct/2007:16:00:35 PDT] Java Heap Size: max=174784k, current=35328k
[19/Oct/2007:16:00:35 PDT] Arguments:
[19/Oct/2007:16:00:35 PDT] [B1060]: Loading persistent data...
[19/Oct/2007:16:00:35 PDT] Using built-in file-based persistent store: /var/imq/ instances/imqbroker/
[19/Oct/2007:16:00:36 PDT] [B1039]: Broker "imqbroker@hostname:7676" ready.

Running the producing client

Now I'll start the producing client who sends a message.


% java -classpath .:/usr/share/lib/imq.jar:/usr/share/lib/jms.jar HelloProducer
Sending Hello World

Running the consuming client

Finally, I'll run the consumer.


% java -classpath .:/usr/share/lib/imq.jar:/usr/share/lib/jms.jar HelloConsumer
Hello World

Everything else

Well, this has gone on long enough so I'm stopping for the moment. I promise to attack authentication and creating ConnectionFactories and DestinationFactories on some future day. (maybe not right away, I might need to take a break and talk about something else for a few entries).

Comments:

cool introduction to JMS (we all know how it works, but i havent use it much)

Posted by raveman on October 25, 2007 at 05:57 PM BST #

Thanks for good introduction.
BTW, is there a way to cancel or modify a message already resides in a JMS queue?

Posted by behumble on October 25, 2007 at 08:44 PM BST #

Unfortunately, no - once a message is in the queue there isn't any way to modify or cancel it using JMS.

Posted by Linda Schneider on October 25, 2007 at 09:42 PM BST #

Another great entry Linda!

OK, so now a request from a regular reader.

Do you have any articles or other things you recommend to people who are trying to understand the types of enterprise problems people try to solve with messaging systems?

If not, I would love to see a post from you or a member of the team about common problems solved by messaging systems.

Posted by Tom Kincaid on November 01, 2007 at 05:19 AM GMT #

On useful papers which discuss when to use messaging in enterprise deployments, this comment reply is going to be deficient.

After a not terribly thorough journey through the wonderful world of google and wikipedia, I determined that actual use cases, one pagers, books and other information which provide a rational for using MOM (message oriented middleware) appear to be missing.

There are a lot of "what it is" articles but none which identify an enterprise problem and tell you how and why you would solve it with JMS.

Additionally, customers who have already made the decision to go to a JMS backbone are often reticent about revealing their architecture.

I'll try to address it in my next blog entry with some very high level information and give some real customer scenarios with any specific company information stripped out. (so you might have to wait another week for a description of MQ specific features)

Posted by Linda Schneider on November 01, 2007 at 07:32 AM GMT #

Nice blog!

One question: In the explanation about acknowledge modes, you write "the client has seen it". How does the client "see" a message? Or should I read "sent"?

Posted by Dies Koper on November 14, 2007 at 09:08 PM GMT #

@Tom, I have used queues extensively in a Microsoft environment using MSMQ and they are a fantastic tool. Microsoft has a triggers service that means whenever a message appears on the queue it can call a method on a COM object. I used this to receive and process requests from a mainframe without having to write any of the boilerplate comms stuff myself which not only saved me a lot of time it also let me sleep a little easier.

So the mainframe running in batch mode can generate a lot of requests that could swamp an application running on a Windows box. By queuing those requests I could ensure there were no resource issues.

I've also used a queue to serialise data to a hardware device attached to a serial port. And I've used queues to allow me to use an older Windows machine without worrying about the load on the processor plus if the machine did fail, all my data that wasn't processed is still waiting in the queue for when I get a replacement installed.

I've just started work in a Linux/Java shop which is why I suddenly find myself here. Thanks for the info Linda - it helps a lot.

Posted by David Clarke on November 15, 2007 at 12:40 PM GMT #

>One question: In the explanation about acknowledge modes, you write "the client has seen it". How does the client "see" a message? Or should I read "sent"?

I actually did mean seen. What was missing from my entry is the fact that Acknowledge modes (assuming that the session is not transacted) really only apply when you are receiving a message.

A message is considered "seen" (internally, we usually use the word consumed) after it has been delivered to the client application (either because receive() was called on the session OR the registered message listener was called)

For example,
\* you create a session with the mode set to CLIENT_ACKNOWLEDGE
\* you create two receivers (one on foo, one on bar)
\* you receive a message from destination foo
\* you receive a message from destination bar
\* you call acknowledge on the second message -> both are considered acknowledged which means that the broker will never send thos messages
to you again.

On the sender, MQ (and I'm sure other providers) uses an internal protocol (a reply) to confirm that a message has been received and persisted on the server. We only do this for persistent messages (since non-persistent messages can be lost). The way it works is:
\* the client sends a message which is put on a socket to be read by the broker and blocks waiting for a reply (if persistent)
\* the broker receives that message, stores the message and sends back a reply

Posted by Linda Schneider on November 15, 2007 at 01:23 PM GMT #

Great explanations, thx for being so precise on the caveats of JNDI for newby like me ! Is it possible that you add a link the code written in /tmp/jndi file ?

Posted by fbab on November 15, 2007 at 03:26 PM GMT #

Linda thanks for the information, it's straight and to the point. I'm fairly new to JMS, so please excuse the post. The tutorial all makes sense and I've downloaded Open MQ 4.1 and have your sample working and the samples that come with MQ 4.1. My issues are how does the consumer and producer change if they reside on remote clients to the MQ server? What needs to be modified for this situation to work? What are the considerations should be taken into account? Reviewing all the samples (or at least the one's I've made it through) they all have the consumer and producer on the same host.

The situation I'm looking to handle with JMS is for student registration to courses in a Course Management System at a college. One machine handles the message production when a student registers or drops a course at our Student Information System. Another machine handles the consumption of those messages and takes the appropriate actions to enroll or drop students in the Course Management System. Finally the third system is the MQ server that is in the middle of the whole scenario.

Maybe you can discuss some issues of moving these components to other systems. What needs to be considered and what are some of the best practices for implementing such a system that don't have the consumer and producer on the same machine. Again, sorry if I'm missing something obvious. Thanks!

Posted by Brad Rippe on November 15, 2007 at 04:37 PM GMT #

I am working on a project and contemplating using Glassfish, or at least an OpenMQ standalone. Google keeps pointing me back here for just about every search string containing "OpenMQ." I was wondering if there were secret hidden documents that might point me to creating OpenMQ clients through Spring and/or Tomcat.

Posted by Michael Shaw on December 14, 2007 at 01:11 PM GMT #

I have 2 issues

1. My reciever is retaining messages after once it has consumed. Seems serverstoring in a file , I am not getting location.

2. what can be done if we dont want messages to be on server, once it is restarted.

I am working on RAD7.0 with default messaging and WAS6.0 . Pls i need some urgent help........Thankssssssssss

Posted by Puneeet on April 08, 2008 at 04:17 AM BST #

Hi Linda,

for JMS we would require Java messaging Queues to be installed, isnt there any wayout inwhich we can use Java to access native OS queues like Linux queues.

Posted by Neeraj on April 19, 2008 at 06:02 AM BST #

Hi,

i am running glassfish servers on 2 machines. one machine is producing messages and another machine is conusming . i am not getting why we need to create 2 queues on both the servers. and whats need to create same connection factory on both the machines.Please clear to me.

Thanks

Posted by Bhupinder on August 12, 2008 at 12:42 AM BST #

Hi Linda!

This is a great tutorial. It brought a lot to my knowledge about JMS and IMQ. One thing I wanted to know is which path can I see the queues physically lying.

Regards,
Kashif

Posted by Kashif on November 12, 2008 at 04:00 AM GMT #

What happens if the message is not been able to consume by the client and abruptly ends without confirming the message(using Client_ACk mode).
If the queue receiver is set to receive 10 messages at one time, and there are 100 pending messages, will this message which has abruptly came out without confirming by the client would be again resubmited
1>Alomost instantly
2>Will only get processed once the 100 messages got processed.

Will this be FIFO based on the client rejection or can maintain the old precedence?

Posted by atchayya on November 12, 2008 at 05:05 PM GMT #

There is no article or enough info on how to configure OpenMQ in Tomcat settings.xml.

If you know can you please write an article in blog so many people will come to know and get benefit of it.

Posted by Krunal Shimpi on September 10, 2009 at 04:56 AM BST #

Hi Guys,

I have a problem and might excite experts here. Please let me know if you have a solution for the problem.

I am creating a custom log4j JMSAppender which will post the messages to a Topic. Now if I recieve a JMSException due to server going down, then I catch the exception and I re-initialize my connection factory, connection and session etc to a different server where another app server running. Now the problem is when i try to re-initiate, its still trying to connect the local server. This is happening with Glassfish v2.0 and v2.1. When I looked at the thread dumps, I see that there are some run-away threads that the API is creating and the process is still going on. glass fish using open mq 4.3

Posted by KR Kumar on October 23, 2009 at 11:08 AM BST #

I am using MQactive will this code work with active mq. i have a problem receiving message with mqactive (using spring jms synchronous messaging). if this code works it will help me kindly let me know.

thanks
raj

Posted by guest on March 27, 2011 at 07:22 PM BST #

Post a Comment:
Comments are closed for this entry.
About

A blog for Open Message Queue, the JMS provider in GlassFish Server, Open Source Edition

Search

Top Tags
Categories
Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today
Feeds