analytics

Monday, March 14, 2011

Java Message Service

Java Message Service




The Java Message Service (JMS) API is a Java Message Oriented Middleware (MOM) API for sending messages between two or more clients.

1.1   What Is Messaging?

Messaging is a method of communication between software components or applications. A messaging system is a peer-to-peer facility: A messaging client can send messages to, and receive messages from, any other client. Each client connects to a messaging agent that provides facilities for creating, sending, receiving, and reading messages.

Messaging enables distributed communication that is loosely coupled. A component sends a message to a destination, and the recipient can retrieve the message from the destination. However, the sender and the receiver do not have to be available at the same time in order to communicate. In fact, the sender does not need to know anything about the receiver; nor does the receiver need to know anything about the sender. The sender and the receiver need to know only what message format and what destination to use. In this respect, messaging differs from tightly coupled technologies, such as Remote Method Invocation (RMI), which require an application to know a remote application's methods.

Messaging also differs from electronic mail (e-mail), which is a method of communication between people or between software applications and people. Messaging is used for communication between software applications or software components.

1.2   What Is the JMS API?

The Java Message Service is a Java API that allows applications to create, send, receive, and read messages. Designed by Sun and several partner companies, the JMS API defines a common set of interfaces and associated semantics that allow programs written in the Java programming language to communicate with other messaging implementations.

The JMS API minimizes the set of concepts a programmer must learn to use messaging products but provides enough features to support sophisticated messaging applications. It also strives to maximize the portability of JMS applications across JMS providers in the same messaging domain.

The JMS API enables communication that is not only loosely coupled but also

Asynchronous. A JMS provider can deliver messages to a client as they arrive; a client does not have to request messages in order to receive them.

Reliable. The JMS API can ensure that a message is delivered once and only once. Lower levels of reliability are available for applications that can afford to miss messages or to receive duplicate messages.



Basic JMS API Concepts

This chapter introduces the most basic JMS API concepts, the ones you must know to get started writing simple JMS client applications:

JMS API architecture

Messaging domains

Message consumption

The next chapter introduces the JMS API programming model. Later chapters cover more advanced concepts, including the ones you need to write J2EETM applications that use message-driven beans.

2.1   JMS API Architecture

A JMS application is composed of the following parts.

A JMS provider is a messaging system that implements the JMS interfaces and provides administrative and control features. An implementation of the J2EE platform at release 1.3 includes a JMS provider.

JMS clients are the programs or components, written in the JavaTM programming language, that produce and consume messages.

Messages are the objects that communicate information between JMS clients.

Administered objects are preconfigured JMS objects created by an administrator for the use of clients. The two kinds of administered objects are destinations and connection factories, which are described in Section 3.1, "Administered Objects."

Native clients are programs that use a messaging product's native client API instead of the JMS API. An application first created before the JMS API became available and subsequently modified is likely to include both JMS and native clients.

Figure 2.1 illustrates the way these parts interact. Administrative tools allow you to bind destinations and connection factories into a Java Naming and Directory Interface (JNDI) API namespace. A JMS client can then look up the administered objects in the namespace and then establish a logical connection to the same objects through the JMS provider.



Figure 2.1   JMS API Architecture

2.2   Messaging Domains

Before the JMS API existed, most messaging products supported either the point-to-point or the publish/subscribe approach to messaging. The JMS Specification provides a separate domain for each approach and defines compliance for each domain. A standalone JMS provider may implement one or both domains. A J2EE provider must implement both domains.

In fact, most current implementations of the JMS API provide support for both the point-to-point and the publish/subscribe domains, and some JMS clients combine the use of both domains in a single application. In this way, the JMS API has extended the power and flexibility of messaging products.

2.2.1   Point-to-Point Messaging Domain

A point-to-point (PTP) product or application is built around the concept of message queues, senders, and receivers. Each message is addressed to a specific queue, and receiving clients extract messages from the queue(s) established to hold their messages. Queues retain all messages sent to them until the messages are consumed or until the messages expire.

PTP messaging has the following characteristics and is illustrated in Figure 2.2.



Figure 2.2   Point-to-Point Messaging

Each message has only one consumer.

A sender and a receiver of a message have no timing dependencies. The receiver can fetch the message whether or not it was running when the client sent the message.

The receiver acknowledges the successful processing of a message.

Use PTP messaging when every message you send must be processed successfully by one consumer.

2.2.2   Publish/Subscribe Messaging Domain

In a publish/subscribe (pub/sub) product or application, clients address messages to a topic. Publishers and subscribers are generally anonymous and may dynamically publish or subscribe to the content hierarchy. The system takes care of distributing the messages arriving from a topic's multiple publishers to its multiple subscribers. Topics retain messages only as long as it takes to distribute them to current subscribers.

Pub/sub messaging has the following characteristics.

Each message may have multiple consumers.

Publishers and subscribers have a timing dependency. A client that subscribes to a topic can consume only messages published after the client has created a subscription, and the subscriber must continue to be active in order for it to consume messages.

The JMS API relaxes this timing dependency to some extent by allowing clients to create durable subscriptions. Durable subscriptions can receive messages sent while the subscribers are not active. Durable subscriptions provide the flexibility and reliability of queues but still allow clients to send messages to many recipients. For more information about durable subscriptions, see Section 5.2.1, "Creating Durable Subscriptions."

Use pub/sub messaging when each message can be processed by zero, one, or many consumers. Figure 2.3 illustrates pub/sub messaging.



Figure 2.3   Publish/Subscribe Messaging

2.3   Message Consumption

Messaging products are inherently asynchronous in that no fundamental timing dependency exists between the production and the consumption of a message. However, the JMS Specification uses this term in a more precise sense. Messages can be consumed in either of two ways:

Synchronously. A subscriber or a receiver explicitly fetches the message from the destination by calling the receive method. The receive method can block until a message arrives or can time out if a message does not arrive within a specified time limit.

Asynchronously. A client can register a message listener with a consumer. A message listener is similar to an event listener. Whenever a message arrives at the destination, the JMS provider delivers the message by calling the listener's onMessage method, which acts on the contents of the message.



3    The JMS API Programming Model

The basic building blocks of a JMS application consist of

Administered objects: connection factories and destinations Connections

Sessions

Message producers

Message consumers

Messages

Figure 3.1 shows how all these objects fit together in a JMS client application.



Figure 3.1   The JMS API Programming Model

This chapter describes all these objects briefly and provides sample commands and code snippets that show how to create and use the objects. The last section briefly describes JMS API exception handling.

Examples that show how to combine all these objects in applications appear in later chapters. For more details, see the JMS API documentation, which you can download from the JMS Web site,http://java.sun.com/products/jms/.

3.1   Administered Objects

Two parts of a JMS application--destinations and connection factories--are best maintained administratively rather than programmatically. The technology underlying these objects is likely to be very different from one implementation of the JMS API to another. Therefore, the management of these objects belongs with other administrative tasks that vary from provider to provider.

JMS clients access these objects through interfaces that are portable, so a client application can run with little or no change on more than one implementation of the JMS API. Ordinarily, an administrator configures administered objects in a JavaTM Naming and Directory InterfaceTM (JNDI) API namespace, and JMS clients then look them up, using the JNDI API. J2EETM applications always use the JNDI API.

With the J2EE Software Development Kit (SDK) version 1.3.1, you use a tool called j2eeadmin to perform administrative tasks. For help on the tool, type j2eeadmin with no arguments.

3.1.1   Connection Factories

A connection factory is the object a client uses to create a connection with a provider. A connection factory encapsulates a set of connection configuration parameters that has been defined by an administrator. A pair of connection factories come preconfigured with the J2EE SDK and are accessible as soon as you start the service. Each connection factory is an instance of either the QueueConnectionFactory or theTopicConnectionFactory interface.

With the J2EE SDK, for example, you can use the default connection factory objects, named QueueConnectionFactory and TopicConnectionFactory, to create connections. You can also create new connection factories by using the following commands:

j2eeadmin -addJmsFactory jndi_name queue



j2eeadmin -addJmsFactory jndi_name topic

At the beginning of a JMS client program, you usually perform a JNDI API lookup of the connection factory. For example, the following code fragment obtains an InitialContext object and uses it to look up theQueueConnectionFactory and the TopicConnectionFactory by name:

Context ctx = new InitialContext();



QueueConnectionFactory queueConnectionFactory =

(QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");



TopicConnectionFactory topicConnectionFactory =

(TopicConnectionFactory) ctx.lookup("TopicConnectionFactory");

Calling the InitialContext method with no parameters results in a search of the current classpath for a vendor-specific file named jndi.properties. This file indicates which JNDI API implementation to use and which namespace to use.

3.1.2   Destinations

A destination is the object a client uses to specify the target of messages it produces and the source of messages it consumes. In the PTP messaging domain, destinations are called queues, and you use the following J2EE SDK command to create them:

j2eeadmin -addJmsDestination queue_name queue

In the pub/sub messaging domain, destinations are called topics, and you use the following J2EE SDK command to create them:

j2eeadmin -addJmsDestination topic_name topic

A JMS application may use multiple queues and/or topics.

In addition to looking up a connection factory, you usually look up a destination. For example, the following line of code performs a JNDI API lookup of the previously created topic MyTopic and assigns it to a Topicobject:

Topic myTopic = (Topic) ctx.lookup("MyTopic");

The following line of code looks up a queue named MyQueue and assigns it to a Queue object:

Queue myQueue = (Queue) ctx.lookup("MyQueue");

3.2   Connections

A connection encapsulates a virtual connection with a JMS provider. A connection could represent an open TCP/IP socket between a client and a provider service daemon. You use a connection to create one or more sessions.

Like connection factories, connections come in two forms, implementing either the QueueConnection or the TopicConnection interface. For example, once you have a QueueConnectionFactory or aTopicConnectionFactory object, you can use it to create a connection:

QueueConnection queueConnection =

queueConnectionFactory.createQueueConnection();



TopicConnection topicConnection =

topicConnectionFactory.createTopicConnection();

When an application completes, you need to close any connections that you have created. Failure to close a connection can cause resources not to be released by the JMS provider. Closing a connection also closes its sessions and their message producers and message consumers.

queueConnection.close();



topicConnection.close();

Before your application can consume messages, you must call the connection's start method; for details, see Section 3.5, "Message Consumers." If you want to stop message delivery temporarily without closing the connection, you call the stop method.

3.3   Sessions

A session is a single-threaded context for producing and consuming messages. You use sessions to create message producers, message consumers, and messages. Sessions serialize the execution of message listeners; for details, see Section 3.5.1, "Message Listeners."

A session provides a transactional context with which to group a set of sends and receives into an atomic unit of work. For details, see Section 5.2.2, "Using JMS API Local Transactions."

Sessions, like connections, come in two forms, implementing either the QueueSession or the TopicSession interface. For example, if you created a TopicConnection object, you use it to create a TopicSession:

TopicSession topicSession =

topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);

The first argument means that the session is not transacted; the second means that the session automatically acknowledges messages when they have been received successfully. (For more information, see Section 5.1.1, "Controlling Message Acknowledgment.")

Similarly, you use a QueueConnection object to create a QueueSession:

QueueSession queueSession =

queueConnection.createQueueSession(true, 0);

Here, the first argument means that the session is transacted; the second indicates that message acknowledgment is not specified for transacted sessions.

3.4   Message Producers

A message producer is an object created by a session and is used for sending messages to a destination. The PTP form of a message producer implements the QueueSender interface. The pub/sub form implements theTopicPublisher interface.

For example, you use a QueueSession to create a sender for the queue myQueue, and you use a TopicSession to create a publisher for the topic myTopic:

QueueSender queueSender = queueSession.createSender(myQueue);



TopicPublisher topicPublisher = topicSession.createPublisher(myTopic);

You can create an unidentified producer by specifying null as the argument to createSender or createPublisher. With an unidentified producer, you can wait to specify which destination to send the message to until you send or publish a message.

Once you have created a message producer, you can use it to send messages. (You have to create the messages first; see Section 3.6, "Messages.") With a QueueSender, you use the send method:

queueSender.send(message);

With a TopicPublisher, you use the publish method:

topicPublisher.publish(message);

If you created an unidentified producer, use the overloaded send or publish method that specifies the destination as the first parameter.

3.5   Message Consumers

A message consumer is an object created by a session and is used for receiving messages sent to a destination. A message consumer allows a JMS client to register interest in a destination with a JMS provider. The JMS provider manages the delivery of messages from a destination to the registered consumers of the destination.

The PTP form of message consumer implements the QueueReceiver interface. The pub/sub form implements the TopicSubscriber interface.

For example, you use a QueueSession to create a receiver for the queue myQueue, and you use a TopicSession to create a subscriber for the topic myTopic:

QueueReceiver queueReceiver = queueSession.createReceiver(myQueue);



TopicSubscriber topicSubscriber = topicSession.createSubscriber(myTopic);

You use the TopicSession.createDurableSubscriber method to create a durable topic subscriber. For details, see Section 5.2.1, "Creating Durable Subscriptions."

Once you have created a message consumer, it becomes active, and you can use it to receive messages. You can use the close method for a QueueReceiver or a TopicSubscriber to make the message consumer inactive. Message delivery does not begin until you start the connection you created by calling the start method (see Section 3.2, "Connections").

With either a QueueReceiver or a TopicSubscriber, you use the receive method to consume a message synchronously. You can use this method at any time after you call the start method:

queueConnection.start();

Message m = queueReceiver.receive();



topicConnection.start();

Message m = topicSubscriber.receive(1000); // time out after a second

To consume a message asynchronously, you use a message listener, described in Section 3.5.1, "Message Listeners."

3.5.1   Message Listeners

A message listener is an object that acts as an asynchronous event handler for messages. This object implements the MessageListener interface, which contains one method, onMessage. In the onMessage method, you define the actions to be taken when a message arrives.

You register the message listener with a specific QueueReceiver or TopicSubscriber by using the setMessageListener method. For example, if you define a class named TopicListener that implements theMessageListener interface, you can register the message listener as follows:

TopicListener topicListener = new TopicListener();



topicSubscriber.setMessageListener(topicListener);

After you register the message listener, you call the start method on the QueueConnection or the TopicConnection to begin message delivery. (If you call start before you register the message listener, you are likely to miss messages.)

Once message delivery begins, the message consumer automatically calls the message listener's onMessage method whenever a message is delivered. The onMessage method takes one argument of type Message, which the method can cast to any of the other message types (see Section 3.6.3, "Message Bodies").

A message listener is not specific to a particular destination type. The same listener can obtain messages from either a queue or a topic, depending on whether the listener is set by a QueueReceiver or aTopicSubscriber object. A message listener does, however, usually expect a specific message type and format. Moreover, if it needs to reply to messages, a message listener must either assume a particular destination type or obtain the destination type of the message and create a producer for that destination type.

Your onMessage method should handle all exceptions. It must not throw checked exceptions, and throwing a RuntimeException, though possible, is considered a programming error.

The session used to create the message consumer serializes the execution of all message listeners registered with the session. At any time, only one of the session's message listeners is running.

In the J2EE 1.3 platform, a message-driven bean is a special kind of message listener. For details, see Section 6.2, "Using Message-Driven Beans."

3.5.2   Message Selectors

If your messaging application needs to filter the messages it receives, you can use a JMS API message selector, which allows a message consumer to specify the messages it is interested in. Message selectors assign the work of filtering messages to the JMS provider rather than to the application. For an example of the use of a message selector, see Chapter 8.

A message selector is a String that contains an expression. The syntax of the expression is based on a subset of the SQL92 conditional expression syntax. The createReceiver, createSubscriber, andcreateDurableSubscriber methods each have a form that allows you to specify a message selector as an argument when you create a message consumer.

The message consumer then receives only messages whose headers and properties match the selector. (See Section 3.6.1, "Message Headers," and Section 3.6.2, "Message Properties.") A message selector cannot select messages on the basis of the content of the message body.

3.6   Messages

The ultimate purpose of a JMS application is to produce and to consume messages that can then be used by other software applications. JMS messages have a basic format that is simple but highly flexible, allowing you to create messages that match formats used by non-JMS applications on heterogeneous platforms.

A JMS message has three parts:

A header

Properties (optional)

A body (optional)

For complete documentation of message headers, properties, and bodies, see the documentation of the Message interface in the API documentation.

3.6.1   Message Headers

A JMS message header contains a number of predefined fields that contain values that both clients and providers use to identify and to route messages. (Table 3.1 lists the JMS message header fields and indicates how their values are set.) For example, every message has a unique identifier, represented in the header field JMSMessageID. The value of another header field, JMSDestination, represents the queue or the topic to which the message is sent. Other fields include a timestamp and a priority level.

Each header field has associated setter and getter methods, which are documented in the description of the Message interface. Some header fields are intended to be set by a client, but many are set automatically by thesend or the publish method, which overrides any client-set values.



Table 3.1:    How JMS Message Header Field Values Are Set

Header Field Set By

JMSDestination send or publish method

JMSDeliveryMode send or publish method

JMSExpiration send or publish method

JMSPriority send or publish method

JMSMessageID send or publish method

JMSTimestamp send or publish method

JMSCorrelationID Client

JMSReplyTo Client

JMSType Client

JMSRedelivered JMS provider

3.6.2   Message Properties

You can create and set properties for messages if you need values in addition to those provided by the header fields. You can use properties to provide compatibility with other messaging systems, or you can use them to create message selectors (see Section 3.5.2, "Message Selectors"). For an example of setting a property to be used as a message selector, see Section 8.1.2.3, "The Bean Class: PublisherBean.java."

The JMS API provides some predefined property names that a provider may support. The use of either predefined properties or user-defined properties is optional.

3.6.3   Message Bodies

The JMS API defines five message body formats, also called message types, which allow you to send and to receive data in many different forms and provide compatibility with existing messaging formats. Table 3.2describes these message types.



Table 3.2:    JMS Message Types

Message Type Body Contains

TextMessage A java.lang.String object (for example, the contents of an Extensible Markup Language file).

MapMessage A set of name/value pairs, with names as String objects and values as primitive types in the Java programming language. The entries can be accessed sequentially by enumerator or randomly by name. The order of the entries is undefined.

BytesMessage A stream of uninterpreted bytes. This message type is for literally encoding a body to match an existing message format.

StreamMessage A stream of primitive values in the Java programming language, filled and read sequentially.

ObjectMessage A Serializable object in the Java programming language.

Message Nothing. Composed of header fields and properties only. This message type is useful when a message body is not required.

The JMS API provides methods for creating messages of each type and for filling in their contents. For example, to create and send a TextMessage to a queue, you might use the following statements:

TextMessage message = queueSession.createTextMessage();

message.setText(msg_text); // msg_text is a String

queueSender.send(message);

At the consuming end, a message arrives as a generic Message object and must be cast to the appropriate message type. You can use one or more getter methods to extract the message contents. The following code fragment uses the getText method:

Message m = queueReceiver.receive();

if (m instanceof TextMessage) {

TextMessage message = (TextMessage) m;

System.out.println("Reading message: " + message.getText());

} else {

// Handle error

}

3.7   Exception Handling

The root class for exceptions thrown by JMS API methods is JMSException. Catching JMSException provides a generic way of handling all exceptions related to the JMS API. The JMSException class includes the following subclasses, which are described in the API documentation:

IllegalStateException

InvalidClientIDException

InvalidDestinationException

InvalidSelectorException

JMSSecurityException

MessageEOFException

MessageFormatException

MessageNotReadableException

MessageNotWriteableException

ResourceAllocationException

TransactionInProgressException

TransactionRolledBackException

All the examples in the tutorial catch and handle JMSException when it is appropriate to do so.



Elements

The following are JMS elements:[2]

JMS provider

An implementation of the JMS interface for a Message Oriented Middleware (MOM). Providers are implemented as either a Java JMS implementation or an adapter to a non-Java MOM.

JMS client

An application or process that produces and/or receives messages.

JMS producer/publisher

A JMS client that creates and sends messages.

JMS consumer/subscriber

A JMS client that receives messages.

JMS message

An object that contains the data being transferred between JMS clients.

JMS queue

A staging area that contains messages that have been sent and are waiting to be read. Note that, contrary to what the name queue suggests, messages have to be delivered in the order sent A JMS queue only guarantees that each message is processed only once.

JMS topic

A distribution mechanism for publishing messages that are delivered to multiple subscribers.





Models

The JMS API supports two models:

Point-to-point

Publish and subscribe

In the point-to-point model, a sender posts messages to a particular queue and a receiver reads messages from the queue. Here, the sender knows the destination of the message and posts the message directly to the receiver's queue. This model is characterized by the following:

Only one consumer gets the message.

The producer does not have to be running at the time the consumer consumes the message, nor does the consumer need to be running at the time the message is sent.

Every message successfully processed is acknowledged by the consumer.

The publish/subscribe model supports publishing messages to a particular message topic. Subscribers may register interest in receiving messages on a particular message topic. In this model, neither thepublisher nor the subscriber knows about each other. A good analogy for this is an anonymous bulletin board. The following are characteristics of this model:

Multiple consumers (or none) will receive the message.

There is a timing dependency between publishers and subscribers. The publisher has to create a message topic for clients to subscribe. The subscriber has to remain continuously active to receive messages, unless it has established a durable subscription. In that case, messages published while the subscriber is not connected will be redistributed whenever it reconnects.

Using Java, JMS provides a way of separating the application from the transport layer of providing data. The same Java classes can be used to communicate with different JMS providers by using the JNDI information for the desired provider. The classes first use a connection factory to connect to the queue or topic, and then use populate and send or publish the messages. On the receiving side, the clients then receive or subscribe to the messages.

No comments:

Post a Comment