JMS overview and architecture
A JMS application comprises the following elements:
- JMS clients. Java programs that send and receive messages using the JMS API.
- Non-JMS clients. It is important to realize that legacy programs will often be part of an overall JMS application, and their inclusion must be anticipated in planning.
- Messages. The format and content of messages to be exchanged by JMS and non-JMS clients is integral to the design of a JMS application.
- JMS provider. As was stated previously, JMS defines a set of interfaces for which a provider must supply concrete implementations specific to its MOM product.
- Administered objects. An administrator of a messaging-system provider creates objects that are isolated from the proprietary technologies of the provider.
Providers of MOM products differ significantly in the mechanisms and techniques they use to implement messaging. To keep JMS clients portable, objects that implement the JMS interfaces must be isolated from a provider's proprietary technologies.
The mechanism for doing this is administered objects. These objects, which implement JMS interfaces, are created by an administrator of the provider's messaging system and are placed in the JNDI namespace.
The objects are then retrieved by JMS programs and accessed through the JMS interfaces that they implement. The JMS provider must supply a tool that allows creation of administered objects and their placement in the JNDI namespace.
There are two types of administered objects:
ConnectionFactory: Used to create a connection to the provider's underlying messaging system.
Destination: Used by the JMS client to specify the destination of messages being sent or the source of messages being received.
Although the administered objects themselves are instances of classes specific to a provider's implementation, they are retrieved using a portable mechanism (JNDI) and accessed through portable interfaces (JMS). The JMS program needs to know only the JNDI name and the JMS interface type of the administered object; no provider-specific knowledge is required.
JMS defines a set of high-level interfaces that encapsulate various messaging concepts. In turn, these interfaces are further defined and customized for the two messaging domains -- PTP and pub/sub.
The high-level interfaces are:
ConnectionFactory: An administered object that creates a
Connection: An active connection to a provider.
Destination: An administered object that encapsulates the identity of a message destination, such as where messages are sent to or received from.
Session: A single-threaded context for sending and receiving messages. For reasons of simplicity and because
Sessions control transactions, concurrent access by multiple threads is restricted. Multiple
Sessions can be used for multithreaded applications.
MessageProducer: Used for sending messages.
MessageConsumer: Used for receiving messages.
The following table identifies the domain-specific interfaces inherited from each high-level interface.
|High-level interface||PTP domain||Pub/sub domain|
In previous versions of JMS, the high-level interfaces were parents of the domain-specific interfaces and contained only those functions common to both domains. No implementations of the high-level interfaces were provided by the JMS provider. In JMS 1.1, the high-level interfaces are now considered "common interfaces" and contain all of the functionality of both domains; JMS providers must provide implementations of these interfaces. Although the common interfaces are still parents to the domain-specific interfaces, they are now the preferred method for JMS client programming, and the domain-specific interfaces are provided only for backward compatibility.
The following restates the table on the previous section, showing the common interfaces.
|JMS common interface||PTP domain||Pub/sub domain|
Unification of domains with the common interfaces results in some domain-specific classes inheriting methods that are not suited for that domain. The JMS provider is required to throw an
IllegalStateException should this occur in client code.
A typical JMS program goes through the following steps to begin producing and consuming messages:
- Look up a
- Look up one or more
Destinations through JNDI.
- Use the
ConnectionFactoryto create a
- Use the
Connectionto create one or more
- Use a
Destinationto create the required
- Start the
At this point, messages can begin to flow, and the application can receive, process, and send messages, as required. In later sections, we'll develop JMS programs and you'll get to see this setup in detail.
At the heart of a messaging system are, of course, messages. JMS provides several message types for different types of content, but all messages derive from the
Message is divided into three constituent parts:
- The header is a standard set of fields that are used by both clients and providers to identify and route messages.
- Properties provide a facility for adding optional header fields to a message. If your application needs to categorize or classify a message in a way not provided by the standard header fields, you can add a property to the message to accomplish that categorization or classification.
get<Type>Property(...)methods are provided to set and get properties of a variety of Java types, including
Object. JMS defines a standard set of properties that are optional for providers to supply.
- The body of the message contains the content to be delivered to a receiving application. Each message interface is specialized for the type of content it supports.
The following list gives the name of each header field of
its corresponding Java type, and a description of the field:
Uniquely identifies each message that is sent by a provider. This field is set by the provider during the send process; clients cannot determine the
JMSMessageIDfor a message until after it has been sent.
Destinationto which the message is sent; set by the provider during the send process.
Contains the value
DeliveryMode.NON_PERSISTENT. A persistent message is delivered "once and only once"; a non-persistent message is delivered "at most once." Be aware that "at most once" includes not being delivered at all. A non-persistent message might be lost by a provider during application or system failure. Extra care will be taken to assure that a persistent message is not affected by failures. There is often considerable overhead in sending persistent messages, and the trade-offs between reliability and performance must be carefully considered when deciding the delivery mode of a message.
The time that the message was delivered to a provider to be sent; set by the provider during the send process.
The time when a message should expire. This value is calculated during the send process as the sum of the time-to-live value of the sending method and the current time. Expired messages should not be delivered by the provider. A value of 0 indicates that the message will not expire.
The priority of the message; set by the provider during the send process. A priority of 0 is the lowest priority; a priority of 9 is the highest priority.
Typically used to link a response message with a request message; set by the JMS program sending the message. A JMS program responding to a message from another JMS program would copy the
JMSMessageIDof the message it is responding to into this field, so that the requesting program could correlate the response to the particular request that it made.
Used by a requesting program to indicate where a reply message should be sent; set by the JMS program sending the message.
Can be used by a JMS program to indicate the type of the message. Some providers maintain a repository of message types and will use this field to reference the type definition in the repository; in this case, the JMS program should not use this field.
Indicates that the message was delivered earlier to the JMS program, but that the program did not acknowledge its receipt; set by the provider during receive processing.
The following list gives the name of each standard property of
its corresponding Java type, and a description of the property. Support for standard properties by a provider is optional. JMS reserves the "JMSX" property name for these and future JMS-defined properties.
Identity of the user sending the message.
Identity of the application sending the message.
Number of times delivery of the message has been attempted.
Identity of the message group to which this message belongs.
Sequence number of this message within the message group.
Identity of the transaction within which this message was produced.
Identity of the transaction within which this message was consumed.
The time JMS delivered the message to the consumer.
Used by providers that maintain a message warehouse of messages; generally not of interest to JMS producers or consumers.
Reserved for provider-specific properties.
There are five forms of message body, and each form is defined by an interface that extends
These interfaces are:
StreamMessage: Contains a stream of Java primitive values that are filled and read sequentially using standard stream operations.
MapMessage: Contains a set of name-value pairs; the names are of type
stringand the values are Java primitives.
TextMessage: Contains a
ObjectMessage: Contains a
SerializableJava object; JDK 1.2 collection classes can be used.
BytesMessage: Contains a stream of uninterpreted bytes; allows encoding a body to match an existing message format.
Each provider supplies classes specific to its product that implement these interfaces. It is important to note that the JMS specification mandates that providers must be prepared to accept and handle a
Message object that is not an instance of one of its own
Although these "alien" objects might not be handled by a provider as efficiently as one of the provider's own implementations, they must be handled to ensure interoperability of all JMS providers.
A JMS transaction groups a set of produced messages and a set of consumed messages into an atomic unit of work. If an error occurs during a transaction, the production and consumption of messages that occurred before the error can be "undone."
Session objects control transactions, and a
Session can be denoted as transacted when it is created. A transacted
Session always has a current transaction, that is, there is no
rollback() end one transaction and automatically begin another.
Distributed transactions can be supported by the Java Transaction API (JTA)
XAResource API, though this is optional for providers.
Acknowledgment is the mechanism whereby a provider is informed that a message has been successfully received.
Session receiving the message is transacted, acknowledgment is handled automatically. If the
Session is not transacted, then the type of acknowledgment is determined when the
Session is created.
There are three types of acknowledgment:
Session.DUPS_OK_ACKNOWLEDGE: Lazy acknowledgment of message delivery; reduces overhead by minimizing work done to prevent duplicates; should be used only if duplicate messages are expected and can be handled.
Session.AUTO_ACKNOWLEDGE: Message delivery is automatically acknowledged upon completion of the method that receives the message.
Session.CLIENT_ACKNOWLEDGE: Message delivery is explicitly acknowledged by calling the
acknowledge()method on the
JMS provides a mechanism, called a message selector, for a JMS program to filter and categorize the messages it receives.
The message selector is a
String that contains an expression whose syntax is based on a subset of SQL92. The message selector is evaluated when an attempt is made to receive a message, and only messages that match the selection criteria of the selector are made available to the program.
Selection is based on matches to header fields and properties; body values cannot be used for selection. The syntax for message selectors is provided in detail in the JMS specification.
The authors of JMS included the
TextMessage message type on the presumption that
String messages will be used extensively.
Their reasoning is that XML will be a popular, if not the most popular, means of representing the content of messages. A portable transport mechanism (JMS) coupled with a portable data representation (XML) is proving to be a powerful tool in enterprise application integration (EAI) and other areas of data exchange.
J2EE version 1.2 requires compliant application servers to have the JMS API present but did not mandate the presence of a JMS provider.
J2EE version 1.3 requires application servers to supply a JMS provider. J2EE version 1.3 also introduced the message-driven bean,
adding asynchronous notification abilities to Enterprise JavaBeans containers, as part of the EJB 2.0 specification. A message-driven bean implements the
MessageListener interface (see
MessageListener ) later in this tutorial), and is invoked by the EJB container on the arrival of a message at a destination designated at deployment time. Message-driven beans contain the business logic to process the message, including, if needed, invoking other enterprise beans.
J2EE version 1.4 requires J2EE products to include a JMS version 1.1 provider that supports both point-to-point and publish/subscribe messaging. It specifies that J2EE applications will not use the JMS client APIs for transaction handling; transactions will be handled by the J2EE containers. J2EE version 1.4 also requires that components in Web and EJB containers not create more than one active
Session per connection.