Introducing Java Messaging Service (EJB 3)

In this section we provide an overview to JMS API by building a basic message producer. JMS is a deceptively simple and small API to a very powerful technology. The JMS API is to messaging what the Java Database Connectivity (JDBC) API is to database access. JMS provides a uniform, standard way of accessing MOM in Java and is therefore an alternative to using product-specific APIs. With the exception of Microsoft Message Queuing (MSMQ), most major MOM products support JMS.

The easiest way to learn JMS might be by looking at code in action. We’re going to explore JMS by first developing the ActionBazaar code that sends out the shipping request. We develop a message producer using JMS and learn about structure of the message interface; then in the next section, we develop the message consumer using MDBs.

Developing the JMS message producer

As we described in our scenario in section 4.1.2, when a user places an order for an "ActionBazaar Special," a shipping request is sent to a queue shared between ActionBazaar and Turtle. The code in listing 4.1 sends the message out and could be part of a method in a simple Java object invoked by the ActionBazaar application. All relevant shipping information—such as the item number, shipping address, shipping method, and insurance amount—is packed into a message and sent out to ShippingRequestQueue.


Listing 4.1 JMS code that sends out shipping requests from ActionBazaar

JMS code that sends out shipping requests from ActionBazaarJMS code that sends out shipping requests from ActionBazaar

As we explain each logical step of this code in the following sections, we’ll go through a large subset of the JMS API components and see usage patterns. Note that for simplicity we have removed the code for exception handling.

Retrieving the connection factory and destination

In JMS, administrative objects are similar to JDBC javax.sql.DataSource objects. These are resources that are created and configured outside the code and stored in JNDI. JMS has two administrative objects: javax.jms.ConnectionFactory and javax.jms.Destination, both of which we use in listing 4.1. We then retrieve the connection factory using dependency injection with the @Resource annotation, and the connection factory encapsulates all the configuration information needed to connect to MOM. We also inject the queue to forward the shipping request to, aptly named, ShippingRequestQueue. With EJB 3, using resources is much easier; you don’t have to deal with the complexity of JNDI or configure resource references in deployment descriptors. Topic 5 discusses dependency injection in greater detail.

The next step in listing 4.1 is creating a connection to MOM and getting a new JMS session.

Opening the connection and session

The javax.jms.Connection object represents a live MOM connection, which we create using the createConnection method of the connection factory. Connections are thread-safe and designed to be sharable because opening a new connection is resource intensive. A JMS session (javax.jms.Session), on the other hand, provides a single-threaded, task-oriented context for sending and receiving messages. We create a session from the connection using the createSession method. The first parameter of the method specifies whether the session is transactional. We’ve decided that our session should be transactional and therefore set the parameter to true. This means that the requests for messages to be sent won’t be realized until either the session’s commit method is called or the session is closed. (If the session isn’t transactional, messages will be sent as soon as the send method is invoked.) The second parameter of the createSession method specifies the acknowledge mode and only has an effect for nontransactional sessions receiving messages, which we’ll discuss later. Having set up the session, we are now ready to take on the meat of the matter: sending the message.

Preparing and sending the message

The session is not directly used for sending or receiving messages (we could argue that having it do so would simplify the JMS API). Instead, a javax.jms.Message-Producer needed to send messages to the shipping request queue is constructed using the session’s createProducer method. Then we create and populate the javax.jms.Message to be sent. In our example, we send the Serializable Java object ShippingRequest to Turtle, so the most appropriate message type for us is javax.jms.ObjectMessage (which we create using the createObjectMessage method). We then create an instance of the ShippingRequest object and set the item number, shipping address, shipping method, and insurance amount fields. Once ShippingRequest is set up, we set it as the payload of the message using set-Object. Finally, we instruct the message producer to send the message out using the send method.

Releasing resources

A large number of resources are allocated under the hood for both the session and connection objects, so it is important to explicitly close both once we’ve finished with them, as we do with

tmp24104_thumb

This step is even more important in our case since no messages are sent out until our transactional session is committed when we close the session.

If all goes well, a message containing the shipping request winds up in the queue. Before we look at the message consumer code that receives this message, let’s discuss the javax.jms.Message object in a little more detail.

The JMS message interface

The Message interface standardizes what is exchanged across JMS and is an extremely robust data encapsulation mechanism. As figure 4.6 shows, a JMS message has the following parts: the message header, message properties, and the message body, each of which is detailed in the sections that follow.

A good analogy for JMS messages is mailing envelopes. Let’s see how this analogy fits next.

Anatomy of a message. A JMS message has a header, properties, and a body.

Figure 4.6 Anatomy of a message. A JMS message has a header, properties, and a body.

Message headers

Headers are name-value pairs common to all messages. In our envelope analogy, the message header is the information on an envelope that is pretty standard: the to and from addresses, postage, and postmark. For example, the JMS message version of a postmark is the JMSTimestamp header. MOM sets this header to the current time when the message is sent.

Here are some other commonly used JMS headers:

tmp24106_thumb

A property can be a boolean, byte, double, float, int, long, short, String, or Object.

tmp24107_thumb

Message properties

Message properties are just like headers, but are explicitly created by the application instead of being standard across messages. In the envelope analogy, if you decide to write "Happy Holidays" on the envelope to let the receiver know the envelope contains a gift or note, the text is a property instead of a header. In the ActionBazaar example, one way to mark a shipping request as fragile would be to add a boolean property called Fragile and set it to true. The code to do this would look like this:

Message body

The message body contains the contents of the envelope; it is the payload of the message. What you’re trying to send in the body determines what message type you should use. In listing 4.1, we chose javax.jms.ObjectMessage because we were sending out the ShippingRequest Java object. Alternatively, we could have chosen to send BytesMessage, MapMessage, StreamMessage, or TextMessage. Each of these message types has a slightly different interface and usage pattern. There are no hard-and-fast rules dictating the choice of message types. Explore all the choices before making a decision for your application.

The Spring JmsTemplate

Spring’s JmsTemplate greatly simplifies common JMS tasks like sending messages by automating generic code. Using JmsTemplate, our entire message producer code could be reduced to a few lines. This is a great way to take care of tasks, as long as you aren’t doing anything too complicated such as using temporary queues, JMS headers, and properties.

At the time of this writing, Spring doesn’t have very robust asynchronous message-processing capabilities when compared to MDB. Any future MDB-like features in Spring are likely to utilize the relatively arcane JCA container, which leaves room for a great Spring/EJB 3 integration case.

We just finished reviewing most of the major parts of JMS that you need to send and use with MDB. While full coverage of JMS is beyond the scope of this topic, we encourage you to fully explore the fascinating JMS API by visiting http:// java.sun.com/products/jms/docs.html. In particular, you should explore how JMS message consumers work.

Having taken a closer look at JMS messages, the time is right to look at the Turtle server message consumer, which we’ll build using an MDB.

Next post:

Previous post: