BUY THIS BOOK
Add to Cart

Print Book $34.95


Add to Cart

Print+PDF $45.44

Add to Cart

PDF $27.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £24.95

What is this?

Looking to Reprint or License this content?


Java Message Service
Java Message Service By Richard Monson-Haefel, David A. Chappell
December 2000
Pages: 238

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Understanding the Messaging Paradigm
Computers and people can communicate by using messaging systems to exchange messages over electronic networks. The most ubiquitous messaging system today is email, which facilitates communication among people. While email is an important human-to-human messaging system, this book is not about email. Instead, this book is concerned with messaging systems that allow different software applications to communicate with each other. These application-to-application messaging systems, when used in business systems, are generically referred to as enterprise messaging systems, or Message-Oriented Middleware (MOM).
Enterprise messaging systems allow two or more applications to exchange information in the form of messages. A message, in this case, is a self-contained package of business data and network routing headers. The business data contained in a message can be anything—depending on the business scenario—and usually contains information about some business transaction. In enterprise messaging systems, messages inform an application of some event or occurrence in another system.
Using Message-Oriented Middleware, messages are transmitted from one application to another across a network. MOM products ensure that messages are properly distributed among applications. In addition, MOMs usually provide fault tolerance, load balancing, scalability, and transactional support for enterprises that need to reliably exchange large quantities of messages.
MOM vendors use different message formats and network protocols for exchanging messages, but the basic semantics are the same. An API is used to create a message, give it a payload (application data), assign it routing information, and then send the message. The same API is used to receive messages produced by other applications.
In all modern enterprise messaging systems, applications exchange messages through virtual channels called destinations . When a message is sent, it's addressed to a destination, not a specific application. Any application that subscribes or registers an interest in that destination may receive that message. In this way, the applications that receive messages and those that send messages are decoupled. Senders and receivers are not bound to each other in any way and may send and receive messages as they see fit.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enterprise Messaging
Enterprise messaging is not a new concept. Messaging products such as IBM MQSeries, Microsoft MSMQ , TIBCO Rendevous, Open Horizon Ambrosia, and Modulus InterAgent have been in existence for many years. Newer messaging products such as Progress SonicMQ , Softwired iBus, and FioranoMQ have been built from the ground up, based on the need for doing reliable Business-to-Business communications over the Internet.
A key concept of enterprise messaging is messages are delivered asynchronously from one system to others over a network. To deliver a message asynchronously means the sender is not required to wait for the message to be received or handled by the recipient; it is free to send the message and continue processing. Asynchronous messages are treated as autonomous units—each message is self-contained and carries all of the data and state needed by the business logic that processes it.
In asynchronous messaging, applications use a simple API to construct a message, then hand it off to the Message-Oriented Middleware for delivery to one or more intended recipients (Figure 1.1). A message is a package of business data that is sent from one application to another over the network. The message should be self-describing in that it should contain all the necessary context to allow the recipients to carry out their work independently.
Figure 1.1: Message-Oriented Middleware
Message-Oriented Middleware architectures of today vary in their implementation. The spectrum ranges from a centralized architecture that depends on a message server to perform routing, to a decentralized architecture that distributes the "server" processing out to the client machines. A varied array of protocols including TCP/IP, HTTP, SSL, and IP multicast are employed at the network transport layer. Some messaging products use a hybrid of both approaches, depending on the usage model.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Java Message Service ( JMS)
The Java Message Service ( JMS) is an API for enterprise messaging created by Sun Microsystems. JMS is not a messaging system itself; it's an abstraction of the interfaces and classes needed by messaging clients when communicating with messaging systems. In the same way that JDBC abstracts access to relational databases and JNDI abstracts access to naming and directory services, JMS abstracts access to MOMs. Using JMS, a messaging application's messaging clients are portable across MOM products.
The creation of JMS was an industry effort. JavaSoft took the lead on the spec and worked very closely with the messaging vendors throughout the process. The initial objective was to provide a Java API for connectivity to MOM systems. However, this changed to the wider objective of supporting messaging as a first-class Java distributed computing paradigm equally with Remote Procedure Call (RPC) based systems like CORBA and Enterprise JavaBeans:
There were a number of MOM vendors that participated in the creation of JMS. It was an industry effort rather than a Sun effort. Sun was the spec lead and did shepherd the work but it would not have been successful without the direct involvement of the messaging vendors. Although our original objective was to provide a Java API for connectivity to MOM systems, this changed over the course of the work to a broader objective of supporting messaging as a first class Java distributed computing paradigm on equal footing with RPC.
—Mark Hapner, JMS spec lead, Sun Microsystems
The result is a best-of-breed, robust specification that includes a rich set of message delivery semantics, combined with a simple yet flexible API for incorporating messaging into applications. The intent was that in addition to new vendors, existing messaging vendors would support the JMS API.
JMS provides for two types of messaging models, publish-and-subscribe and point-to-point queuing. The JMS specification refers to these as
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Application Scenarios
Until now, our discussion of enterprise messaging has been somewhat abstract. This section attempts to give some real-world scenarios to provide you with a better idea of the types of problems that enterprise messaging systems can solve.
Most mature organizations have both legacy and new applications that are implemented independently and cannot interoperate. In many cases, organizations have a strong desire to integrate these applications so they can share information and cooperate in larger enterprise-wide operations. The integration of these applications is generally called Enterprise Application Integration (EAI).
A variety of vendor and home-grown solutions are used for EAI, but enterprise messaging systems are central to most of them. Enterprise messaging systems allow stovepipe applications to communicate events and to exchange data while remaining physically independent. Data and events can be exchanged in the form of messages via topics or queues, which provide an abstraction that decouples participating applications.
As an example, a messaging system might be used to integrate an Internet order processing system with an Enterprise Resource Planning (ERP) system like SAP. The Internet system uses JMS to deliver business data about new orders to a topic. An ERP gateway application, which accesses a SAP application via its native API, can subscribe to the order topic. As new orders are broadcast to the topic, the gateway receives the orders and enters them into the SAP application.
Historically, businesses exchanged data using Electronic Data Interchange (EDI) systems. Data was exchanged using rigid, fixed formats over proprietary Value-Added Networks (VANs). Cost of entry was high and data was usually exchanged in batch processes—not as real-time business events.
The Internet, XML, and modern messaging systems have radically changed how businesses exchange data and interact in what is now called Business-to-Business (B2B). The use of messaging systems is central to modern B2B solutions because it allows organizations to cooperate without requiring them to tightly integrate their business systems. In addition, it lowers the barriers to entry since finer-grained participation is possible. Businesses can join in B2B and disengage depending on the queues and topics with which they interact.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
RPC Versus Asynchronous Messaging
RPC (Remote Procedure Call) is a term commonly used to describe a distributed computing model that is used today by middleware technologies such as CORBA, Java RMI, and Microsoft's DCOM. Component-based architectures such as Enterprise JavaBeans are built on top of this model. RPC-based technologies have been, and will continue to be, a viable solution for many applications. However, the enterprise messaging model is superior in certain types of distributed applications. In this section we will discuss the pros and cons of each model. In Chapter 8, J2EE, EJB, and JMS, we will discuss a means of combining the two.
One of the most successful areas of the tightly coupled RPC model has been in building 3-tier, or n -tier applications. In this model, a presentation layer (1st tier), communicates using RPC with business logic on the middle tier (2nd tier), which accesses data housed on the back end (3rd tier). Sun Microsystems' J2EE platform and Microsoft's DNA are the most modern examples of this architecture.
With J2EE, JSP and Servlets represent the presentation tier while Enterprise JavaBeans is the middle tier. Microsoft's DNA is architecturally similar to J2EE, relying on ASP for presentation and COM+ for the middle tier. Regardless of the platform, the core technology used in these systems is RPC-based middleware. Whether it's the EJB or COM+, RPC is the defining communication paradigm.
RPC attempts to mimic the behavior of a system that runs in one process. When a remote procedure is invoked, the caller is blocked until the procedure completes and returns control to the caller. This synchronized model allows the developer to view the system as if it runs in one process. Work is performed sequentially, ensuring that tasks are completed in a predefined order. The synchronized nature of RPC tightly couples the client (the software making the call) to the server (the software servicing the call). The client cannot proceed—it is blocked—until the server responds.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Developing a Simple Example
Now that you understand Message-Oriented Middleware and some JMS concepts, you are ready to write your first JMS application. This chapter provides a gentle introduction to JMS using the publish-and-subscribe messaging model. You will get your feet wet with JMS and learn some of the basic classes and interfaces. Chapter 4, covers publish-and-subscribe in detail, and Chapter 5, covers the point-to-point message model.
As with all examples in this book, example code and instructions specific to several vendors is provided in the book download at O'Reilly's web site (see the preface for details). You will need to install and configure your JMS provider according to the instructions provided by your vendor.
Internet chat provides an interesting application for learning about the JMS pub/sub messaging model. Used mostly for entertainment, web-based chat applications can be found on thousands of web sites. In a chat application, people join virtual chat rooms where they can "chat" with a group of other people.
To illustrate how JMS works, we will use the JMS pub/sub API to build a simple chat application. The requirements of Internet chat map neatly onto the publish-and-subscribe messaging model. In this model, a producer can send a message to many consumers by delivering the message to a single topic. A message producer is also called a publisher and a message consumer is also called a subscriber . In reality, using JMS for a chat application would be overkill, since chat systems don't require enterprise quality service.
The following source code is a JMS-based chat client. Every participant in a chat session uses this Chat program to join a specific chat room (topic), and deliver and receive messages to and from that room:
package chap2.chat;

import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.io.InputStreamReader;
import java.util.Properties;

public class Chat implements javax.jms.MessageListener{
    private TopicSession pubSession;
    private TopicSession subSession;
    private TopicPublisher publisher;
    private TopicConnection connection;
    private String username;

    /* Constructor. Establish JMS publisher and subscriber */
    public Chat(String topicName, String username, String password)
    throws Exception {
        // Obtain a JNDI connection
        Properties env = new Properties( );
        // ... specify the JNDI properties specific to the vendor

        InitialContext jndi = new InitialContext(env);

        // Look up a JMS connection factory
        TopicConnectionFactory conFactory =
        (TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");

        // Create a JMS connection
        TopicConnection connection =
        conFactory.createTopicConnection(username,password);

        // Create two JMS session objects
        TopicSession pubSession =
        connection.createTopicSession(false,
                                      Session.AUTO_ACKNOWLEDGE);
        TopicSession subSession =
        connection.createTopicSession(false,
                                      Session.AUTO_ACKNOWLEDGE);

        // Look up a JMS topic
        Topic chatTopic = (Topic)jndi.lookup(topicName);

        // Create a JMS publisher and subscriber
        TopicPublisher publisher = 
            pubSession.createPublisher(chatTopic);
        TopicSubscriber subscriber = 
            subSession.createSubscriber(chatTopic);

        // Set a JMS message listener
        subscriber.setMessageListener(this);

        // Intialize the Chat application
        set(connection, pubSession, subSession, publisher, username);

        // Start the JMS connection; allows messages to be delivered
        connection.start( );

    }
    /* Initialize the instance variables */
    public void set(TopicConnection con, TopicSession pubSess,
                    TopicSession subSess, TopicPublisher pub, 
                    String username) {
        this.connection = con;
        this.pubSession = pubSess;
        this.subSession = subSess;
        this.publisher = pub;
        this.username = username;
    }
    /* Receive message from topic subscriber */
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = (TextMessage) message;
            String text = textMessage.getText( );
            System.out.println(text);
        } catch (JMSException jmse){ jmse.printStackTrace( ); }
    }
    /* Create and send message using topic publisher */
    protected void writeMessage(String text) throws JMSException {
        TextMessage message = pubSession.createTextMessage( );
        message.setText(username+" : "+text);
        publisher.publish(message);
    }
    /* Close the JMS connection */
    public void close( ) throws JMSException {
        connection.close( );
    }
    /* Run the Chat client */
    public static void main(String [] args){
        try{
            if (args.length!=3)
                System.out.println("Topic or username missing");

            // args[0]=topicName; args[1]=username; args[2]=password
            Chat chat = new Chat(args[0],args[1],args[2]);

            // Read from command line
            BufferedReader commandLine = new 
              java.io.BufferedReader(new InputStreamReader(System.in));

            // Loop until the word "exit" is typed
            while(true){
                String s = commandLine.readLine( );
                if (s.equalsIgnoreCase("exit")){
                    chat.close( ); // close down connection
                    System.exit(0);// exit program
                } else 
                    chat.writeMessage(s);
            }
        } catch (Exception e){ e.printStackTrace( ); }
    }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Chat Application
Internet chat provides an interesting application for learning about the JMS pub/sub messaging model. Used mostly for entertainment, web-based chat applications can be found on thousands of web sites. In a chat application, people join virtual chat rooms where they can "chat" with a group of other people.
To illustrate how JMS works, we will use the JMS pub/sub API to build a simple chat application. The requirements of Internet chat map neatly onto the publish-and-subscribe messaging model. In this model, a producer can send a message to many consumers by delivering the message to a single topic. A message producer is also called a publisher and a message consumer is also called a subscriber . In reality, using JMS for a chat application would be overkill, since chat systems don't require enterprise quality service.
The following source code is a JMS-based chat client. Every participant in a chat session uses this Chat program to join a specific chat room (topic), and deliver and receive messages to and from that room:
package chap2.chat;

import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.io.InputStreamReader;
import java.util.Properties;

public class Chat implements javax.jms.MessageListener{
    private TopicSession pubSession;
    private TopicSession subSession;
    private TopicPublisher publisher;
    private TopicConnection connection;
    private String username;

    /* Constructor. Establish JMS publisher and subscriber */
    public Chat(String topicName, String username, String password)
    throws Exception {
        // Obtain a JNDI connection
        Properties env = new Properties( );
        // ... specify the JNDI properties specific to the vendor

        InitialContext jndi = new InitialContext(env);

        // Look up a JMS connection factory
        TopicConnectionFactory conFactory =
        (TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");

        // Create a JMS connection
        TopicConnection connection =
        conFactory.createTopicConnection(username,password);

        // Create two JMS session objects
        TopicSession pubSession =
        connection.createTopicSession(false,
                                      Session.AUTO_ACKNOWLEDGE);
        TopicSession subSession =
        connection.createTopicSession(false,
                                      Session.AUTO_ACKNOWLEDGE);

        // Look up a JMS topic
        Topic chatTopic = (Topic)jndi.lookup(topicName);

        // Create a JMS publisher and subscriber
        TopicPublisher publisher = 
            pubSession.createPublisher(chatTopic);
        TopicSubscriber subscriber = 
            subSession.createSubscriber(chatTopic);

        // Set a JMS message listener
        subscriber.setMessageListener(this);

        // Intialize the Chat application
        set(connection, pubSession, subSession, publisher, username);

        // Start the JMS connection; allows messages to be delivered
        connection.start( );

    }
    /* Initialize the instance variables */
    public void set(TopicConnection con, TopicSession pubSess,
                    TopicSession subSess, TopicPublisher pub, 
                    String username) {
        this.connection = con;
        this.pubSession = pubSess;
        this.subSession = subSess;
        this.publisher = pub;
        this.username = username;
    }
    /* Receive message from topic subscriber */
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = (TextMessage) message;
            String text = textMessage.getText( );
            System.out.println(text);
        } catch (JMSException jmse){ jmse.printStackTrace( ); }
    }
    /* Create and send message using topic publisher */
    protected void writeMessage(String text) throws JMSException {
        TextMessage message = pubSession.createTextMessage( );
        message.setText(username+" : "+text);
        publisher.publish(message);
    }
    /* Close the JMS connection */
    public void close( ) throws JMSException {
        connection.close( );
    }
    /* Run the Chat client */
    public static void main(String [] args){
        try{
            if (args.length!=3)
                System.out.println("Topic or username missing");

            // args[0]=topicName; args[1]=username; args[2]=password
            Chat chat = new Chat(args[0],args[1],args[2]);

            // Read from command line
            BufferedReader commandLine = new 
              java.io.BufferedReader(new InputStreamReader(System.in));

            // Loop until the word "exit" is typed
            while(true){
                String s = commandLine.readLine( );
                if (s.equalsIgnoreCase("exit")){
                    chat.close( ); // close down connection
                    System.exit(0);// exit program
                } else 
                    chat.writeMessage(s);
            }
        } catch (Exception e){ e.printStackTrace( ); }
    }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Anatomy of a JMS Message
This chapter focuses on the anatomy of a message: the individual parts that make up a message (headers, properties, and the different kinds of message payloads). Appendix B, Appendix C, and Appendix D cover additional information that will prove invaluable as a reference when developing JMS applications. Appendix B, provides in-depth information on the purpose and application of JMS headers; Appendix C, covers the rules governing the use of JMS properties; and Appendix D, covers the syntax of message selectors. Although you do not need to read these appendixes to understand subsequent chapters in this book, you will need them as a reference when implementing real JMS applications. After you finish reading this chapter, take a look at Appendixes Appendix B, Appendix C, and Appendix D so you're familiar with their content.
The Message is the most important part of the entire JMS specification. All data and events in a JMS application are communicated with messages, while the rest of JMS exists to facilitate the transfer of messages. They are the lifeblood of the system.
A JMS message both carries application data and provides event notification. Its role is unique to distributed computing. In RPC-based systems (CORBA, Java RMI, DCOM), a message is a command to execute a method or procedure, which blocks the sender until a reply has been received. A JMS message is not a command; it transfers data and tells the receiver that something has happened. A message doesn't dictate what the recipient should do and the sender doesn't wait for a response. This decouples the sender from the receiver, making messaging systems and their messages far more dynamic and flexible than request-reply paradigms.
A Message object has two parts: the message data itself, called the payload or message body, and the message headers and properties (see Figure 3.1).
Figure 3.1: Anatomy of a message
Messages come in various types that are defined by the payload they carry. The payload itself might be very structured, as with
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Headers
Every JMS message has a set of standard headers. Each header is identified by a set of accessor and mutator methods that follow the idiom setJMS<HEADER>( ) , getJMS<HEADER>( ) . Here is a partial definition of the Message interface that shows all the JMS header methods:
public interface Message {
    
    public Destination getJMSDestination(  ) throws JMSException; 
    public void setJMSDestination(Destination destination) 
    throws JMSException; 

    public int getJMSDeliveryMode(  ) throws JMSException; 
    public void setJMSDeliveryMode(int deliveryMode) 
    throws JMSException; 

    public String getJMSMessageID(  ) throws JMSException; 
    public void setJMSMessageID(String id) throws JMSException; 

    public long getJMSTimestamp(  ) throws JMSException; 
    public void setJMSTimestamp(long timestamp) throws JMSException; 

    public long getJMSExpiration(  ) throws JMSException; 
    public void setJMSExpiration(long expiration) throws JMSException; 

    public boolean getJMSRedelivered(  ) throws JMSException; 
    public void setJMSRedelivered(boolean redelivered) 
    throws JMSException; 

    public int getJMSPriority(  ) throws JMSException; 
    public void setJMSPriority(int priority) throws JMSException; 

    public Destination getJMSReplyTo(  ) throws JMSException; 
    public void setJMSReplyTo(Destination replyTo) throws JMSException; 

    public String getJMSCorrelationID(  ) throws JMSException; 
    public void setJMSCorrelationID(String correlationID) 
    throws JMSException; 
    public byte[] getJMSCorrelationIDAsBytes(  ) throws JMSException; 
    public void setJMSCorrelationIDAsBytes(byte[] correlationID) 
    throws JMSException; 

    public String getJMSType(  ) throws JMSException; 
    public void setJMSType(String type) throws JMSException; 

}
JMS headers are divided into two large groups: automatically assigned headers and developer-assigned headers. The next two sections discuss these two types.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Properties
Properties act like additional headers that can be assigned to a message. They provide the developer with more information about the message. The Message interface provides several accessor and mutator methods for reading and writing properties. The value of a property can be a String, boolean, byte, double, int, long, or float.
There are three basic categories of message properties: application-specific properties, JMS-defined properties, and provider-specific properties. Application properties are defined and applied to Message objects by the application developer; the JMS extension and provider-specific properties are additional headers that are, for the most part, automatically added by the JMS provider.
Any property defined by the application developer can be an application-specific property. Application properties are set before the message is delivered. There are no predefined application properties; developers are free to define any properties that fit their needs. For example, in the chat example developed in Chapter 2, a special property could be added that identifies the user sending the message:
TextMessage message = pubSession.createTextMessage(  );
message.setText(text);
message.setStringProperty("username",username);
publisher.publish(message);
As an application specific-property, username is not meaningful outside the Chat application; it is used exclusively by the application to filter messages based on the identity of the publisher.
Property values can be any boolean, byte, short, int, long, float, double, or String. The javax.jms.Message interface provides accessor and mutator methods for each of these property value types. Here is a subset of the Message interface definition that shows these methods:
package javax.jms;

public interface Message {
    
    public String getStringProperty(String name) 
       throws JMSException, MessageFormatException; 
    public void 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Message Selectors
A message selector allows a JMS consumer to be more selective about the messages it receives from a particular destination (topic or queue). Message selectors use message properties and headers as criteria in conditional expressions. These conditional expressions use boolean logic to declare which messages should be delivered to a JMS consumer.
For example, in the chat client developed in Chapter 2, message selectors could be used to filter out messages from specific people. To accomplish this we would first declare a new property in the message that identifies the username of the JMS client publishing the message:
protected void writeMessage(String text) throws JMSException{
   TextMessage message = session.createTextMessage(  );
   message.setText(text);
   message.setStringProperty("username",username);
   publisher.publish(message);
 }
JMS clients can now use that property to filter messages. Message selectors are declared when the message consumer is created:
TopicSubscriber subscriber = 
session.createSubscriber(chatTopic, " username != 'William' ",false);
In this code, the message selector (shown in bold) tells the message server to deliver to the consumer only those messages that do not have a username property equal to 'William'.
When a JMS consumer declares a message selector for a particular destination, the selector is applied only to messages delivered to that consumer. Every JMS client can have a different selector specified for each of its consumers.
The message selectors are based on a subset of the SQL-92 conditional expression syntax, which is used in the WHERE clauses of SQL statements. If you are familiar with SQL 92, the conditional expressions used in message selectors will be familiar to you. The syntax used for conditional expressions is covered in detail in Appendix D.
What happens to messages that are not selected for delivery to the consumer by its message selector? This depends on the message model used. In the pub/sub model, the message is simply not delivered to that consumer, but it is delivered to other consumers. This is true of both nondurable and durable subscriptions. In the p2p model, the messages remain in the queue, so other consumers of the queue can see them, but they are not visible to the consumer that used the message selector.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Message Types
The Java Message Service defines six Message interface types that must be supported by JMS providers. Although JMS defines the Message interfaces, it doesn't define their implementation. This allows vendors to implement and transport messages in their own way, while maintaining a consistent and standard interface for the JMS application developer. The six message interfaces are Message and its five sub-interfaces: TextMessage, StreamMessage, MapMessage, ObjectMessage, and BytesMessage.
The Message interfaces are defined according to the kind of payload they are designed to carry. In some cases, Message types were included in JMS to support legacy payloads that are common and useful, which is the case with the text, bytes, and stream message types. In other cases, the Message types were defined to facilitate emerging needs; for example, ObjectMessage can transport serializable Java objects. Some vendors may provide other proprietary message types. Progress' SonicMQ and SoftWired's iBus, for example, provide an XMLMessage type that extends the TextMessage, allowing developers to deal with the message directly through DOM or SAX interfaces. The XMLMessage type may become a standard message type in a future version of the specification. At the time of this writing, Sun Microsystems was starting discussions about adding an XMLMessage type.
The simplest type of message is the javax.jms.Message , which serves as the base interface to the other message types. As shown below, the Message type can be created and used as a JMS message with no payload:
// Create and deliver a Message
Message message =  session.createMessage(  );
publisher.publish(message);
...
// Receive a message on the consumer
public void onMessage(Message message){
    // No payload, process event notification
}
This type of message contains only JMS headers and properties, and is used in event notification. An event notification is a broadcast, warning, or notice of some occurrence. If the business scenario requires a simple notification without a payload, then the lightweight
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Publish-and-Subscribe Messaging
This chapter focuses on the publish-and-subscribe (pub/sub) messaging model that was introduced in Chapter 2. The pub/sub messaging model allows a message producer (also called a publisher) to broadcast a message to one or more consumers (called subscribers). There are three important aspects of the pub/sub model:
  • Messages are pushed to consumers, which means that consumers are delivered messages without having to request them. Messages are exchanged through a virtual channel called a topic. A topic is a destination where producers can publish, and subscribers can consume, messages. Messages delivered to a topic are automatically pushed to all qualified consumers.
  • As in enterprise messaging in general, there is no coupling of the producers to the consumers. Subscribers and publishers can be added dynamically at runtime, which allows the system to grow or shrink in complexity over time.
  • Every client that subscribes to a topic receives its own copy of messages published to that topic. A single message produced by one publisher may be copied and distributed to hundreds, or even thousands of subscribers.
In Chapter 2 you learned the basics of the pub/sub model by developing a simple chat client. In this chapter we will build on those lessons and examine more advanced features of this model, including guaranteed messaging, topic-based addressing, durable subscriptions, request-reply, and temporary topics.
In this chapter we abandon the simple chat example for a more complex and real-world Business-to-Business (B2B) scenario. In our new example, a wholesaler wants to distribute price information to retailers, and the retailers want to respond by generating orders. We'll implement this scenario using the publish-and-subscribe model: the wholesaler will publish messages containing new prices and hot deals, and the retailers will respond by creating their own messages to order stock.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Getting Started with the B2B Application
In this chapter we abandon the simple chat example for a more complex and real-world Business-to-Business (B2B) scenario. In our new example, a wholesaler wants to distribute price information to retailers, and the retailers want to respond by generating orders. We'll implement this scenario using the publish-and-subscribe model: the wholesaler will publish messages containing new prices and hot deals, and the retailers will respond by creating their own messages to order stock.
This scenario is typical of many Business-to-Business operations. We call the clients retailers and wholesalers, but these names are really only for convenience. There's little difference between our wholesaler/retailer scenario and a stock broker broadcasting stock prices to investors, or a manufacturer broadcasting bid requests to multiple suppliers. The fact that we use a retailer and a wholesaler to illustrate our example is much less important than the way we apply JMS.
Our simple trading system is implemented by two classes, both of which are JMS clients: Wholesaler and Retailer . In the interest of keeping the code simple, we won't implement a fancy user interface; our application has a rudimentary command-line user interface.
Before looking at the code, let's look at how the application works. As with the Chat application, the Wholesaler class includes a main( ) method so it can be run as a standalone Java application. It's executed from the command line as follows:
java chap4.B2B.Wholesaler localhost username password
            
username and password are the authentication information for the client. The Retailer class can be executed in the same manner:
java chap4.B2B.Retailer localhost username password
            
Start your JMS server, then run one instance of a Wholesaler client and a Retailer client in separate command windows. In the Wholesaler client you are prompted to enter an item description, an old price, and a new price. Enter the following as shown:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Temporary Topics
In the chat example we explored in Chapter 2, we assumed that JMS clients would communicate with each other using established topics on which messages are asynchronously produced and consumed. In the next sections, we'll explore ways to augment this basic mechanism. We'll start by looking at temporary topics, which is a mechanism for JMS clients to create topics dynamically.
The constructor of the Wholesaler class creates a temporary topic. This topic is used as a JMSReplyTo destination for messages published to the "Hot Deals" topic in the publishPriceQuotes( ) method:
public Wholesaler(String broker, String username, String password){
    try {
        ...
        session = 
        connect.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
        ...
        buyOrdersTopic = session.createTemporaryTopic( );
        ...
}
...
private void publishPriceQuotes(String dealDesc, String username, 
                                   String itemDesc,  float oldPrice, 
                                   float newPrice){
      try {
        javax.jms.StreamMessage message = session.createStreamMessage( );
        ...
        message.setJMSReplyTo(buyOrdersTopic);               
                   
        publisher.publish(
            message,
            javax.jms.DeliveryMode.PERSISTENT,
            javax.jms.Message.DEFAULT_PRIORITY,
            600000);
       ...
}
When the Retailer client decides to respond to a "Hot Deals" message with a buy order, it uses the JMSReplyTo destination, which is the temporary topic created by Wholesaler application:
private void autoBuy (javax.jms.Message message){
    int count = 1000;
    try {
        StreamMessage strmMsg = (StreamMessage)message;
        ...           
        // If price reduction is greater than 10 percent, buy
        if (newPrice == 0 || oldPrice / newPrice > 1.1){
            ...
            javax.jms.Topic buytopic = 
                (javax.jms.Topic)message.getJMSReplyTo( );
                    
            publisher = session.createPublisher(
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Durable Subscriptions
A durable subscription is one that outlasts a client's connection with a message server. While a durable subscriber is disconnected from the JMS server, it is the responsibility of the server to store messages the subscriber misses. When the durable subscriber reconnects, the message server sends it all the unexpired messages that accumulated. This behavior is commonly referred to as store-and-forward messaging . Store-and-forward messaging is a key component of the guaranteed messaging solution. Durable subscriptions make a JMS consumer tolerant of disconnections, whether they are intentional or the result of a partial failure
We can demonstrate durable subscriptions with the B2B example. If you still have the Retailer application up and running, try simulating an abnormal shutdown by typing Ctrl-C in the command window. Leave the Wholesaler running. In the command window for the wholesaler application, type:
Surfboards, 500.00, 299.99
Hockey Sticks, 20.00, 9.99
Once the deals have been entered, restart the Retailer application:
java chap4.B2B.Retailer localhost username password
         
The first time you ran the Retailer application, a topic was registered as durable. When you abnormally terminated the application, the subscription information was retained by the JMS provider. When the Retailer application comes back up, the surfboards and hockey sticks messages are received, processed, and responded to. Because the Retailer had a durable subscription to the "Hot Deals" topic, the JMS server saved the messages that arrived while the Retailer was down. The messages were then delivered when the Retailer resubscribed to the topic.
Here's how we set up the durable subscription. A durable subscription is created by a TopicSession object, the same as with a nondurable subscription. The Retailer class obtains a durable subscription in its constructor:
public Retailer( String broker, String username, String password){
    try {
        ...           
        hotDealsTopic = (Topic)jndi.lookup("Hot Deals");
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Publishing the Message Persistently
Both the Wholesaler and Retailer classes publish messages using the persistent delivery mode:
publisher.publish(
    message,
    javax.jms.DeliveryMode.PERSISTENT,
    javax.jms.Message.DEFAULT_PRIORITY,
    1800000);
Note the use of the overloaded publish( ) method, with parameters that specify delivery mode, priority, and message expiration. This method provides an alternative to using the Message.setJMSDeliveryMode( ) and TopicPublisher.setTimeToLive( ) operations, as discussed in Chapter 3. In JMS, the delivery mode (persistent, nonpersistent) is a Quality of Service (QoS) setting on the message itself. Marking the message as persistent ensures that the message will be saved to a reliable persistent store by the JMS provider before the publish( ) method returns, and allows client execution to continue. More on how and why this works reliably can be found in Chapter 6.
When you are using a temporary topic as a way of posting a reply to a message, you should realize that the total round trip (the initial message and the reply) isn't guaranteed to survive a certain failure condition, even if you use persistent messages. The problem is that temporary topics cannot be used for durable subscriptions. Consider the following scenario:
  1. A JMS client (producer) creates a temporary topic, puts it in the JMSReplyTo header of a message, marks the message as persistent, and publishes it.
  2. The subscriber gets the message and publishes a response on the temporary topic using a persistent message.
  3. The original producer expects a reply on the temporary topic, but disconnects or crashes before it is received.
  4. The original producer restarts, and is no longer able to subscribe to the original temporary topic that it had established in its previous life. It can't resubscribe because the temporary topic was only valid for the duration of the previous connection. Calling
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
JMSCorrelationID
In the B2B example, we are using the JMSCorrelationID as a way for the Retailer to associate its identity with its reply message, as illustrated by the following code in Retailer.autoBuy( ) :
private void autoBuy (javax.jms.Message message){
    ...
    publisher = session.createPublisher(buytopic);
    textMsg.setJMSCorrelationID("DurableRetailer");
                
    publisher.publish(
           textMsg,
           javax.jms.DeliveryMode.PERSISTENT,
           javax.jms.Message.DEFAULT_PRIORITY,
           1800000);
        ...           
}
In Wholesaler, the JMSCorrelationID is extracted in the onMessage( ) handler, and simply printed on the command line:
public void onMessage( javax.jms.Message message){
        ...           
         System.out.println("Order received - "+text+
                            " from " + message.getJMSCorrelationID( ));
        ...           
   }
Another way to associate the Retailer's identity with the reply message would be to store something unique in a message property, or in the message body itself.
A more common use of the JMSCorrelationID is not for the sake of establishing identity; it is for correlating the asynchronous reception of a message with a message that had been previously sent. A message consumer wishing to create a message to be used as a response may place the JMSMessageID of the original message in the JMSCorrelationID of the response message.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Request and Reply
JMS provides design patterns and helper classes to make it easier to write applications that need a direct request-reply between two end points. We have already shown two JMS features that can be used as part of a request-reply solution: temporary topics and the JMSReplyTo header. These features can be used independently or in combination to create an asynchronous request-reply conversation. On occasion you may want to create a synchronous request-reply conversation. There are two ways of doing this. You may call the TopicSubscriber.receive( ) method directly, or you may make use of the TopicRequestor class.
The receive( ) method is defined in the MessageConsumer class, which is the superclass of TopicSubscriber. The receive( ) method is a way of proactively asking for the message rather than passively receiving it through the onMessage( ) callback. In fact, the use of the receive( ) method negates the use of the onMessage( ) callback. The default behavior of the receive( ) method is to block program execution until a message is retrieved from the message server. The receive( ) method effectively changes the pub/sub model from a "push" to a "pull" model. From the client's perspective, you can think of this as a polling mechanism; although that's not necessarily how it is implemented by the JMS provider.
There are three flavors of the receive( ) method:
package javax.jms;
public interface MessageConsumer{
    ...
    Message receive( );
    Message receive(long timeout);
    Message receiveNoWait( );
    ...
}
The receive( ) method with no parameters blocks indefinitely, until a message is received. The receive(long timeout) method blocks until a message is received, or until the timeout period expires, whichever comes first. The receive( ) method will return null if the session is closed while the method is blocking. The receiveNoWait( ) method does not block at all. It either returns a message if one is available, or it returns
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Unsubscribing
Upon closing the session, the JMS provider should automatically take care of unsubscribing any nondurable subscriptions that were created by the session. But there may be cases where you want to explicitly unsubscribe a durable subscriber in a client application. Here is how that is accomplished in Retailer.exit( ) :
private void exit(String s){
        try {
            if ( s != null && 
                s.equalsIgnoreCase("unsubscribe"))
            {
                subscriber.close( );
                session.unsubscribe("Hot Deals Subscription");
            }
            connect.close( );
        } catch (javax.jms.JMSException jmse){
            jmse.printStackTrace( );
        }
        System.exit(0);
    }
For nondurable subscriptions, calling the close( ) method on the TopicSubscriber class is sufficient. For durable subscriptions, there is a unsubscribe(String name) method on the TopicSession object, which takes the subscription name as its parameter. This informs the JMS provider that it should no longer store messages on behalf of this client. It is an error to call the unsubscribe( ) method without first closing the subscription. Hence both methods need to be called for durable subscriptions.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 5: Point-to-Point Messaging
This chapter focuses on the point-to-point (p2p) messaging domain. Many of the concepts of p2p messaging are similar to those we learned in Chapter 4. To avoid redundancy, this chapter highlights the areas where the two models are the same, and focuses on the areas where the two models differ.
In the p2p model, the producer is called a sender and the consumer is called a receiver . The most important characteristics of the point-to-point model are:
  • Messages are exchanged through a virtual channel called a queue. A queue is a destination to which producers send messages, and a source from which receivers consume messages.
  • Each message is delivered only to one receiver. Multiple receivers may connect to a queue, but each message in the queue may only be consumed by one of the queue's receivers.
  • Messages are ordered. A queue delivers messages to consumers in the order they were placed in the queue by the message server. As messages are consumed they are removed from the head of the queue.
  • There is no coupling of the producers to the consumers. Receivers and senders can be added dynamically at runtime, allowing the system to grow or shrink in complexity over time. (This is a characteristic of messaging systems in general.)
In this chapter, we introduce new versions of our Wholesaler and Retailer classes, called QWholesaler and QRetailer. QWholesaler still uses pub/sub to broadcast price quotes, while QRetailer uses a p2p queue to respond with "buy" orders instead of publishing to a temporary topic.
The rest of the chapter focuses on the unique capabilities offered by p2p: examining a queue using the QueueBrowser interface, and load balancing among multiple recipients of a queue.
Like publish/subscribe messaging, point-to-point messaging is based on the concept of sending a message to a named destination. The actual network location of the destination is transparent to the sender, because the p2p client works with a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Point-to-Point and Publish-and-Subscribe
Like publish/subscribe messaging, point-to-point messaging is based on the concept of sending a message to a named destination. The actual network location of the destination is transparent to the sender, because the p2p client works with a Queue identifier obtained from a JNDI namespace, the same way that a pub/sub client uses a Topic identifier.
The pub/sub model is based on a push model, which means that consumers are delivered messages without having to request them. Messages are exchanged through a virtual channel called a topic. From the viewpoint of the receiver, a p2p queue can either push or pull messages, depending on whether it uses the asynchronous onMessage( ) callback, or a synchronous receive( ) method. Both of these methods are explained in more detail later.
In the p2p model, as in the pub/sub model, there is no direct coupling of the producers to the consumers. The destination queue provides a virtual channel that decouples consumers from producers. In the pub/sub model, multiple consumers that subscribe to the same topic each receive their own copy of every message addressed to that topic. In the p2p model, multiple consumers can use the same queue, but each message delivered to the queue can only be received by one of the queue's consumers. How messages delivered to a queue are distributed to the queue's consumers depends on the policies of the JMS provider. Some JMS providers use load-balancing techniques to distribute messages evenly among consumers, while others will use more arbitrary policies.
Messages intended for a p2p queue can be either persistent or nonpersistent. Persistent messages survive JMS provider failures, while nonpersistent messages do not. Messages may have a priority and an expiration time. One important difference between point-to-point and publish/subscribe messaging is that p2p messages are always delivered, regardless of the current connection status of the receiver. Once a message is delivered to a queue, it stays there even if there is no consumer currently connected. More details on failure scenarios can be found in Chapter 6.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The QWholesaler and QRetailer
Let's rethink our wholesaler/retailer scenario in terms of the distinction between the two message models. The pub/sub model is well suited for sending price quotes, since that is naturally a one-to-many broadcast. However, when the retailer responds with a "buy" order, it is more appropriate to use a point-to-point queue. In the real world, retailers naturally deal with many wholesalers, and you would only send a purchase order to the wholesaler that offered the quote.
From the user's perspective, the QWholesaler and QRetailer examples that we'll develop now are functionally equivalent to the Wholesaler and Retailer examples introduced in Chapter 4. The difference lies in the use of the point-to-point queue for responses to price quotes. If you wish to see these classes in action, start your JMS provider and execute the following commands, each in a separate command window:
java chap5.B2B.QWholesaler localhost username password
java chap5.B2B.QRetailer localhost username password
         
Here is the listing for the QRetailer class in its entirety. Later, we will examine this class in detail:
import java.util.StringTokenizer;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.jms.TopicConnectionFactory;
import javax.jms.QueueConnectio