he Java Messaging Service (JMS) API, a Sun Microsystems interface for connecting Java programs to enterprise messaging middleware, is composed of multiple object interfaces with a hierarchy of relationships. The JMS class hierarchy in particular is extensive and can seem confusing at first glance. (Figure 1 illustrates the relationships among the classes within JMS.) However, this complexity is necessary for JMS to:
|Figure 1: The JMS Class Hierarchy (UML)|
Although the reasons for its complexity are sound, the relationships among JMS classes make writing code to the JMS API tedious and confusing at times. Applying the Facade pattern offers a solution to this problem.
A Facade is a design pattern that hides the details and complexities of the lower-level software service for which it is written, making the service easier to use. In fact, the lower-level classes need not be classes at all. They can be an API in the form of a code library or a Web service.
A Facade also provides a unified entry point into the layers of the software. This reduces your application’s dependency on the software service details and allows the Facade to hide future changes in the software service itself. JMS Class Model Complexity: Simplification Needed
The JMS class model is a complex structure that can be tedious to code to and difficult to remember. It can also result in a lot of duplicate code within your application, some for working with JMS topics and the rest for JMS queues. This section describes the highly involved sequence of code that JMS requires.
To use JMS, you first must get a connection to your JMS provider. Through JNDI, retrieve the
javax.jms.ConnectionFactory object, which you’ll use to get this connection in the form of a
javax.jms.Connection object (For an overview of JNDI, read the sidebar, “Java Naming and Directory Interface (JNDI)“.).
In JMS, messages are sent to, or received from, a destination. The
javax.jms.Destination class is the base class to the more familiar
javax.jms.Queue classes. We’ll discuss these classes in more detail as we go along.
You must create a
javax.jms.Session object for every
javax.jms.Destination in which your application is interested. To create a Session object you must call the appropriate method on the Connection object. Each Session can be used by only one thread at a time. This means that for two or more threads to listen to the same Destination, they each must retrieve their own instance of a Session object for that Destination.
In turn, you use a Session object to create a
javax.jms.MessageProducer object to send messages to a Destination, a
javax.jms.MessageConsumer object to receive messages, and the
javax.jms.Message objects themselves.
All the classes discussed so far are actually abstract interfaces, thus they cannot be instantiated themselves. Instead, JMS defines more specific classes that extend these interfaces.
To clarify, the UML diagram in Figure 2 illustrates the extended class model for the
|Figure 2: The JMS Connection and Session Types|
You must know ahead of time whether you will need a
QueueConnectionFactory, or both. References to these objects also are retrieved through JNDI. The only difference between the two connection factory classes is the type of Connection object each one creates: a
TopicConnection or a
QueueConnection. In turn, the only difference between these two connection objects is the type of Session object each one creates: a
TopicSession or a
The UML diagram in Figure 3 illustrates the class model for the remaining classes,
Queue classes are subclasses of the Destination super class. Typically, destinations are created ahead of time through an administration interface on your JMS Provider. However, a
QueueSession object can create
Queue objects at runtime, respectively.
QueueSender classes extend
MessageProducer, and they send messages to a
Queue, respectively. Similarly, the
QueueReceiver classes extend
MessageConsumer, and they receive messages from a
For the sakes of improvement and simplification, you should create a JMS Facade class.The JMS Facade
The JMS Facade attempts to merge the publish/subscribe and point-to-point message models into one interface, hiding the model itself from the programmer. In my experience, application code written around the publish/subscribe model is very similar to that written around the point-to-point model. The difference lies in the details of how each delivers the messages themselves. This difference can be considered an implementation detail and, hence, should be hidden from developers in most cases. (For an overview of the JMS message models, read the sidebar, “The Java Message Service (JMS)“.)
To use the JMS Facade, start by importing the
com.JMSFacade package. Next, create an instance of the
com.JMSFacade.JMSManager class, as shown in Listing 4.
JMSManager does all the work.
|Listing 1. The
This is an example of importing and creating the JMS Facade class,
The JMS Facade attempts to abstract the concepts of publish/subscribe (Topic) and point-to-point (Queue) using the term “subject.” Very simply, code written around the JMS Facade listens to, or sends messages to, a subject that is nothing more than a text name. It is not required to denote whether the destination is a topic or queue at this point?or ever.
You don’t need to create subjects explicitly, even though the JMS Facade does allow it. The Facade creates subjects automatically when you call the
send methods. The only reason to explicitly create a subject is to control the message-delivery model, publish/subscribe or point-to-point. The default model is publish/subscribe.
To specify the message-delivery model, your code must call the
createSubject method on the
JMSManager class. This method requires two parameters:
A subject type specified as
BROADCAST_TYPE is equivalent to a JMS
Topic, which is the publish/subscribe model. A subject type of
SEND_ONCE_SUBJECT is equivalent to a JMS
Queue, which is the point-to-point model.Receiving Messages
To receive messages for a particular subject, you simply call one of the two
listen methods that the
JMSManager class provides. One version of the method delivers messages synchronously, while the other delivers messages asynchronously.
Notice that as I’ve described the
listen methods, I haven’t mentioned topics or queues. This is because, as discussed above, these details of message delivery don’t need to be exposed at this level.
Asynchronous Message Delivery
To receive messages asynchronously, call the version of the
listen method that requires both a subject name and a callback reference. This call returns right away, and your code is free to do whatever processing it needs to do.
When a message arrives for the specified subject, a method is called on the provided callback reference. The callback reference must be an object that implements the
javax.jms.MessageListener interface. The only method defined by this interface is
onMessage, which provides the message itself through a parameter defined as
The callback reference is continuously called for each message that arrives until your application instructs the JMS Facade to stop. To stop listening to messages on a subject, simply call the
stop method and provide the applicable subject name as the parameter.
You don’t have to create multiple instances of the
JMSManager class to asynchronously listen to more than one subject at a time. One instance will do the job. However, your code will need to call
listen once for each subject.
Synchronous Message Delivery
If the code you’re writing has no other purpose but to receive messages and perform processing only when they arrive, you may chose to use the synchronous version of the
listen method. You call this version of
listen by providing only the subject name to which the method will listen. The call will not return until a message arrives for the subject. When a message does arrive, it is returned from the call in the form of a
With synchronous message delivery, your code must call
listen again after a message arrives in order to receive additional messages for the specified subject. To stop receiving messages for a subject, you simply do not call
listen after a message arrives. Therefore, with the synchronous version of
listen, you never need to call the
stop method of the JMS Facade.
To provide flexibility, the JMSManager class defines three
send methods. The difference between them is the type of message payload each sends:
Besides the message payload type, each
send method works the same way, requiring a subject name to send the message to.
As with receiving messages, topics or queues aren’t mentioned. Only a subject is specified. Again, the details of message delivery are hidden at this level. However, it is normally important for a message sender to specify the message-delivery paradigm. The sender may want the message broadcast to all listeners (publish/subscribe) or only to one listener, one time (point-to-point).
Still, you can’t specify this option at the time a message is sent. The message-delivery model is a characteristic of the subject itself. To control it, your code can specify the model by creating the subject before sending or receiving messages for the applicable subject (see the previous section, Subject Creation).
You shouldn’t need more than one instance of
JMSManager, even if your code needs to send messages to more than one subject. You may call the
send method as many times as you need, specifying the same or different subject names as required each time.
JMS Made Easy
The Facade class provides a good starting point for making JMS easier to work with in your code. To see how truly easy it makes working with JMS, download my JMS Fa