@Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
private static ConnectionFactory connectionFactory;
Java Platform, Enterprise Edition (Java EE) 8 The Java EE Tutorial |
Previous | Next | Contents |
The basic building blocks of a JMS application are
Administered objects: connection factories and destinations
Connections
Sessions
JMSContext
objects, which combine a connection and a session in one
object
Message producers
Message consumers
Messages
Figure 48-5 shows how all these objects fit together in a JMS client application.
JMS also provides queue browsers, objects that allow an application to browse messages on a queue.
This section describes all these objects briefly and provides sample commands and code snippets that show how to create and use the objects. The last subsection briefly describes JMS API exception handling.
Examples that show how to combine all these objects in applications appear in Chapter 49, "Java Message Service Examples," beginning with Writing Simple JMS Applications. For more detail, see the JMS API documentation, part of the Java EE API documentation.
Two parts of a JMS application, destinations and connection factories, are commonly 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 administered 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 JNDI namespace, and JMS clients then access them by using resource injection.
With GlassFish Server, you can use the asadmin create-jms-resource
command or the Administration Console to create JMS administered objects
in the form of connector resources. You can also specify the resources
in a file named glassfish-resources.xml
that you can bundle with an
application.
NetBeans IDE provides a wizard that allows you to create JMS resources for GlassFish Server. See Creating JMS Administered Objects for details.
The Java EE platform specification allows a developer to create administered objects using annotations or deployment descriptor elements. Objects created in this way are specific to the application for which they are created. See Creating Resources for Java EE Applications for details. Definitions in a deployment descriptor override those specified by annotations.
A connection factory is the object a client uses to create a connection
to a provider. A connection factory encapsulates a set of connection
configuration parameters that has been defined by an administrator. Each
connection factory is an instance of the ConnectionFactory
,
QueueConnectionFactory
, or TopicConnectionFactory
interface. To
learn how to create connection factories, see
Creating JMS Administered Objects.
At the beginning of a JMS client program, you usually inject a
connection factory resource into a ConnectionFactory
object. A Java EE
server must provide a JMS connection factory with the logical JNDI name
java:comp/DefaultJMSConnectionFactory
. The actual JNDI name will be
implementation-specific.
For example, the following code fragment looks up the default JMS
connection factory and assigns it to a ConnectionFactory
object:
@Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
private static ConnectionFactory connectionFactory;
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 style, destinations are called queues. In the pub/sub messaging style, destinations are called topics. A JMS application can use multiple queues or topics (or both). To learn how to create destination resources, see Creating JMS Administered Objects.
To create a destination using GlassFish Server, you create a JMS destination resource that specifies a JNDI name for the destination.
In the GlassFish Server implementation of JMS, each destination resource refers to a physical destination. You can create a physical destination explicitly, but if you do not, the Application Server creates it when it is needed and deletes it when you delete the destination resource.
In addition to injecting a connection factory resource into a client
program, you usually inject a destination resource. Unlike connection
factories, destinations are specific to either the PTP or pub/sub
messaging style. To create an application that allows you to use the
same code for both topics and queues, you assign the destination to a
Destination
object.
The following code specifies two resources, a queue and a topic. The resource names are mapped to destination resources created in the JNDI namespace:
@Resource(lookup = "jms/MyQueue")
private static Queue queue;
@Resource(lookup = "jms/MyTopic")
private static Topic topic;
In a Java EE application, JMS administered objects are normally placed
in the jms
naming subcontext.
With the common interfaces, you can mix or match connection factories
and destinations. That is, in addition to using the ConnectionFactory
interface, you can inject a QueueConnectionFactory
resource and use it
with a Topic
, and you can inject a TopicConnectionFactory
resource
and use it with a Queue
. The behavior of the application will depend
on the kind of destination you use and not on the kind of connection
factory you use.
A connection encapsulates a virtual connection with a JMS provider. For example, 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.
Note: In the Java EE platform, the ability to create multiple sessions from a single connection is limited to application clients. In web and enterprise bean components, a connection can create no more than one session. |
You normally create a connection by creating a JMSContext
object. See
JMSContext Objects for details.
A session is a single-threaded context for producing and consuming messages.
You normally create a session (as well as a connection) by creating a
JMSContext
object. See JMSContext Objects for details.
You use sessions to create message producers, message consumers,
messages, queue browsers, and temporary destinations.
Sessions serialize the execution of message listeners; for details, see JMS 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 Using JMS Local Transactions.
A JMSContext
object combines a connection and a session in a single
object. That is, it provides both an active connection to a JMS provider
and a single-threaded context for sending and receiving messages.
You use the JMSContext
to create the following objects:
Message producers
Message consumers
Messages
Queue browsers
Temporary queues and topics (see Creating Temporary Destinations)
You can create a JMSContext
in a try
-with-resources block.
To create a JMSContext
, call the createContext
method on the
connection factory:
JMSContext context = connectionFactory.createContext();
When called with no arguments from an application client or a Java SE
client, or from the Java EE web or EJB container when there is no active
JTA transaction in progress, the createContext
method creates a
non-transacted session with an acknowledgment mode of
JMSContext.AUTO_ACKNOWLEDGE
. When called with no arguments from the
web or EJB container when there is an active JTA transaction in
progress, the createContext
method creates a transacted session. For
information about the way JMS transactions work in Java EE applications,
see Using the JMS API in Java EE
Applications.
From an application client or a Java SE client, you can also call the
createContext
method with the argument JMSContext.SESSION_TRANSACTED
to create a transacted session:
JMSContext context =
connectionFactory.createContext(JMSContext.SESSION_TRANSACTED);
The session uses local transactions; see Using JMS Local Transactions for details.
Alternatively, you can specify a non-default acknowledgment mode; see Controlling Message Acknowledgment for more information.
When you use a JMSContext
, message delivery normally begins as soon as
you create a consumer. See JMS Message Consumers for more
information.
If you create a JMSContext
in a try
-with-resources block, you do not
need to close it explicitly. It will be closed when the try
block
comes to an end. Make sure that your application completes all its JMS
activity within the try
-with-resources block. If you do not use a
try
-with-resources block, you must call the close
method on the
JMSContext
to close the connection when the application has finished
its work.
A message producer is an object that is created by a JMSContext
or a
session and used for sending messages to a destination. A message
producer created by a JMSContext
implements the JMSProducer
interface. You could create it this way:
try (JMSContext context = connectionFactory.createContext();) {
JMSProducer producer = context.createProducer();
...
However, a JMSProducer
is a lightweight object that does not consume
significant resources. For this reason, you do not need to save the
JMSProducer
in a variable; you can create a new one each time you send
a message. You send messages to a specific destination by using the
send
method. For example:
context.createProducer().send(dest, message);
You can create the message in a variable before sending it, as shown
here, or you can create it within the send
call. See JMS
Messages for more information.
A message consumer is an object that is created by a JMSContext
or a
session and used for receiving messages sent to a destination. A message
producer created by a JMSContext
implements the JMSConsumer
interface. The simplest way to create a message consumer is to use the
JMSContext.createConsumer
method:
try (JMSContext context = connectionFactory.createContext();) {
JMSConsumer consumer = context.createConsumer(dest);
...
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.
When you use a JMSContext
to create a message consumer, message
delivery begins as soon as you have created the consumer. You can
disable this behavior by calling setAutoStart(false)
when you create
the JMSContext
and then calling the start
method explicitly to start
message delivery. If you want to stop message delivery temporarily
without closing the connection, you can call the stop
method; to
restart message delivery, call start
.
You use the receive
method to consume a message synchronously. You can
use this method at any time after you create the consumer.
If you specify no arguments or an argument of 0
, the method blocks
indefinitely until a message arrives:
Message m = consumer.receive();
Message m = consumer.receive(0);
For a simple client, this may not matter. But if it is possible that a
message might not be available, use a synchronous receive with a
timeout: Call the receive
method with a timeout argument greater than
0
. One second is a recommended timeout value:
Message m = consumer.receive(1000); // time out after a second
To enable asynchronous message delivery from an application client or a Java SE client, you use a message listener, as described in the next section.
You can use the JMSContext.createDurableConsumer
method to create a
durable topic subscription. This method is valid only if you are using a
topic. For details, see Creating Durable Subscriptions. For
topics, you can also create shared consumers; see
Creating Shared Subscriptions.
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.
From an application client or a Java SE client, you register the message
listener with a specific message consumer by using the
setMessageListener
method. For example, if you define a class named
Listener
that implements the MessageListener
interface, you can
register the message listener as follows:
Listener myListener = new Listener();
consumer.setMessageListener(myListener);
When message delivery begins, the JMS provider automatically calls the
message listener’s onMessage
method whenever a message is delivered.
The onMessage
method takes one argument of type Message
, which your
implementation of the method can cast to another message subtype as
needed (see Message Bodies).
In the Java EE web or EJB container, you use message-driven beans for
asynchronous message delivery. A message-driven bean also implements the
MessageListener
interface and contains an onMessage
method. For
details, see Using Message-Driven Beans
to Receive Messages Asynchronously.
Your onMessage
method should handle all exceptions. Throwing a
RuntimeException
is considered a programming error.
For a simple example of the use of a message listener, see Using a Message Listener for Asynchronous Message Delivery. Chapter 49, "Java Message Service Examples," contains several more examples of message listeners and message-driven beans.
If your messaging application needs to filter the messages it receives, you can use a JMS message selector, which allows a message consumer for a destination to specify the messages that interest it. Message selectors assign the work of filtering messages to the JMS provider rather than to the application. For an example of an application that uses a message selector, see Sending Messages from a Session Bean to an MDB.
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 message selector in the example selects any
message that has a NewsType
property that is set to the value
'Sports'
or 'Opinion'
:
NewsType = 'Sports' OR NewsType = 'Opinion'
The createConsumer
and createDurableConsumer
methods, as well as the
methods for creating shared consumers, allow 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 Message Headers and Message Properties.) A message selector cannot select messages on the basis of the content of the message body.
The semantics of consuming messages from topics are more complex than the semantics of consuming messages from queues.
An application consumes messages from a topic by creating a subscription on that topic and creating a consumer on that subscription. Subscriptions may be durable or nondurable, and they may be shared or unshared.
A subscription may be thought of as an entity within the JMS provider itself, whereas a consumer is a JMS object within the application.
A subscription will receive a copy of every message that is sent to the topic after the subscription is created, unless a message selector is specified. If a message selector is specified, only those messages whose properties match the message selector will be added to the subscription.
Unshared subscriptions are restricted to a single consumer. In this case, all the messages in the subscription are delivered to that consumer. Shared subscriptions allow multiple consumers. In this case, each message in the subscription is delivered to only one consumer. JMS does not define how messages are distributed between multiple consumers on the same subscription.
Subscriptions may be durable or nondurable.
A nondurable subscription exists only as long as there is an active consumer on the subscription. This means that any messages sent to the topic will be added to the subscription only while a consumer exists and is not closed.
A nondurable subscription may be either unshared or shared.
An unshared nondurable subscription does not have a name and may have only a single consumer object associated with it. It is created automatically when the consumer object is created. It is not persisted and is deleted automatically when the consumer object is closed.
The JMSContext.createConsumer
method creates a consumer on an unshared
nondurable subscription if a topic is specified as the destination.
A shared nondurable subscription is identified by name and an optional client identifier, and may have several consumer objects consuming messages from it. It is created automatically when the first consumer object is created. It is not persisted and is deleted automatically when the last consumer object is closed. See Creating Shared Subscriptions for more information.
At the cost of higher overhead, a subscription may be durable. A durable subscription is persisted and continues to accumulate messages until explicitly deleted, even if there are no consumer objects consuming messages from it. See Creating Durable Subscriptions for details.
To ensure that a pub/sub application receives all sent messages, use durable subscriptions for the consumers on the topic.
Like a nondurable subscription, a durable subscription may be either unshared or shared.
An unshared durable subscription is identified by name and client identifier (which must be set) and may have only a single consumer object associated with it.
A shared durable subscription is identified by name and an optional client identifier, and may have several consumer objects consuming messages from it.
A durable subscription that exists but that does not currently have a non-closed consumer object associated with it is described as being inactive.
You can use the JMSContext.createDurableConsumer
method to create a
consumer on an unshared durable subscription. An unshared durable
subscription can have only one active consumer at a time.
A consumer identifies the durable subscription from which it consumes messages by specifying a unique identity that is retained by the JMS provider. Subsequent consumer objects that have the same identity resume the subscription in the state in which it was left by the preceding consumer. If a durable subscription has no active consumer, the JMS provider retains the subscription’s messages until they are received by the subscription or until they expire.
You establish the unique identity of an unshared durable subscription by setting the following:
A client ID for the connection
A topic and a subscription name for the subscription
You can set the client ID administratively for a client-specific
connection factory using either the command line or the Administration
Console. (In an application client or a Java SE client, you can instead
call JMSContext.setClientID
.)
After using this connection factory to create the JMSContext
, you call
the createDurableConsumer
method with two arguments: the topic and a
string that specifies the name of the subscription:
String subName = "MySub";
JMSConsumer consumer = context.createDurableConsumer(myTopic, subName);
The subscription becomes active after you create the consumer. Later, you might close the consumer:
consumer.close();
The JMS provider stores the messages sent to the topic, as it would
store messages sent to a queue. If the program or another application
calls createDurableConsumer
using the same connection factory and its
client ID, the same topic, and the same subscription name, then the
subscription is reactivated and the JMS provider delivers the messages
that were sent while the subscription was inactive.
To delete a durable subscription, first close the consumer, then call
the unsubscribe
method with the subscription name as the argument:
consumer.close();
context.unsubscribe(subName);
The unsubscribe
method deletes the state the provider maintains for
the subscription.
Figure 48-6 and Figure 48-7 show the
difference between a nondurable and a durable subscription. With an
ordinary, nondurable subscription, the consumer and the subscription
begin and end at the same point and are, in effect, identical. When the
consumer is closed, the subscription also ends. Here, create
stands
for a call to JMSContext.createConsumer
with a Topic
argument, and
close
stands for a call to JMSConsumer.close
. Any messages sent to
the topic between the time of the first close
and the time of the
second create
are not added to either subscription. In
Figure 48-6, the consumers receive messages M1, M2, M5, and
M6, but they do not receive messages M3 and M4.
With a durable subscription, the consumer can be closed and re-created,
but the subscription continues to exist and to hold messages until the
application calls the unsubscribe
method. In Figure 48-7,
create
stands for a call to JMSContext.createDurableConsumer
,
close
stands for a call to JMSConsumer.close
, and unsubscribe
stands for a call to JMSContext.unsubscribe
. Messages sent after the
first consumer is closed are received when the second consumer is
created (on the same durable subscription), so even though messages M2,
M4, and M5 arrive while there is no consumer, they are not lost.
A shared durable subscription allows you to use multiple consumers to
receive messages from a durable subscription. If you use a shared
durable subscription, the connection factory you use does not need to
have a client identifier. To create a shared durable subscription, call
the JMSContext.createSharedDurableConsumer
method, specifying the
topic and subscription name:
JMSConsumer consumer =
context.createSharedDurableConsumer(topic, "MakeItLast");
See Acknowledging Messages, Using Durable Subscriptions, Using Shared Durable Subscriptions, and Sending Messages from a Session Bean to an MDB for examples of Java EE applications that use durable subscriptions.
A topic subscription created by the createConsumer
or
createDurableConsumer
method can have only one consumer (although a
topic can have many). Multiple clients consuming from the same topic
have, by definition, multiple subscriptions to the topic, and all the
clients receive all the messages sent to the topic (unless they filter
them with message selectors).
It is, however, possible to create a nondurable shared subscription to a
topic by using the createSharedConsumer
method and specifying not only
a destination but a subscription name:
consumer = context.createSharedConsumer(topicName, "SubName");
With a shared subscription, messages will be distributed among multiple clients that use the same topic and subscription name. Each message sent to the topic will be added to every subscription (subject to any message selectors), but each message added to a subscription will be delivered to only one of the consumers on that subscription, so it will be received by only one of the clients. A shared subscription can be useful if you want to share the message load among several consumers on the subscription rather than having just one consumer on the subscription receive each message. This feature can improve the scalability of Java EE application client applications and Java SE applications. (Message-driven beans share the work of processing messages from a topic among multiple threads.)
See Using Shared Nondurable Subscriptions for a simple example of using shared nondurable consumers.
You can also create shared durable subscriptions by using the
JMSContext.createSharedDurableConsumer
method. For details, see
Creating Durable Subscriptions.
The ultimate purpose of a JMS application is to produce and 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 can have three parts: a header, properties, and a body. Only the header is required. The following sections describe these parts.
For complete documentation of message headers, properties, and bodies,
see the documentation of the Message
interface in the API
documentation. For a list of possible message types, see
Message Bodies.
The following topics are addressed here:
A JMS message header contains a number of predefined fields that contain
values used by both clients and providers to identify and route
messages. Table 48-1 lists and describes the JMS message
header fields and indicates how their values are set. For example, every
message has a unique identifier, which is 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 the send
method, which overrides any client-set
values.
Table 48-1 How JMS Message Header Field Values Are Set
Header Field |
Description |
Set By |
|
Destination to which the message is being sent |
JMS
provider |
|
Delivery mode specified when the message was sent (see Specifying Message Persistence) |
JMS provider |
|
The time the message was sent plus the delivery delay specified when the message was sent (see Specifying a Delivery Delay |
JMS
provider |
|
Expiration time of the message (see Allowing Messages to Expire) |
JMS
provider |
|
The priority of the message (see Setting Message Priority Levels) |
JMS
provider |
|
Value that uniquely identifies each message sent by a provider |
JMS provider |
|
The time the message was handed off to a provider to be sent |
JMS provider |
|
Value that links one message to another; commonly
the |
Client application |
|
Destination where replies to the message should be sent |
Client application |
|
Type identifier supplied by client application |
Client application |
|
Whether the message is being redelivered |
JMS provider prior to delivery |
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 JMS Message Selectors). For an example of setting a property to be used as a message selector, see Sending Messages from a Session Bean to an MDB.
The JMS API provides some predefined property names that begin with
JMSX
. A JMS provider is required to implement only one of these,
JMSXDeliveryCount
(which specifies the number of times a message has
been delivered); the rest are optional. The use of these predefined
properties or of user-defined properties in applications is optional.
The JMS API defines six different types of messages. Each message type corresponds to a different message body. These message types allow you to send and receive data in many different forms. Table 48-2 describes these message types.
Table 48-2 JMS Message Types
Message Type |
Body Contains |
|
A |
|
A set of name-value pairs, with names as |
|
A stream of uninterpreted bytes. This message type is for literally encoding a body to match an existing message format. |
|
A stream of primitive values in the Java programming language, filled and read sequentially. |
|
A |
|
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
, you might use the following statements:
TextMessage message = context.createTextMessage();
message.setText(msg_text); // msg_text is a String
context.createProducer().send(message);
At the consuming end, a message arrives as a generic Message
object.
You can then cast the object to the appropriate message type and use
more specific methods to access the body and extract the message
contents (and its headers and properties if needed). For example, you
might use the stream-oriented read methods of BytesMessage
. You must
always cast to the appropriate message type to retrieve the body of a
StreamMessage
.
Instead of casting the message to a message type, you can call the
getBody
method on the Message
, specifying the type of the message as
an argument. For example, you can retrieve a TextMessage
as a
String
. The following code fragment uses the getBody
method:
Message m = consumer.receive();
if (m instanceof TextMessage) {
String message = m.getBody(String.class);
System.out.println("Reading message: " + message);
} else {
// Handle error or process another message type
}
The JMS API provides shortcuts for creating and receiving a
TextMessage
, BytesMessage
, MapMessage
, or ObjectMessage
. For
example, you do not have to wrap a string in a TextMessage
; instead,
you can send and receive the string directly. For example, you can send
a string as follows:
String message = "This is a message";
context.createProducer().send(dest, message);
You can receive the message by using the receiveBody
method:
String message = receiver.receiveBody(String.class);
You can use the receiveBody
method to receive any type of message
except StreamMessage
and Message
, as long as the body of the message
can be assigned to a particular type.
An empty Message
can be useful if you want to send a message that is
simply a signal to the application. Some of the examples in
Chapter 49, "Java Message Service
Examples," send an empty message after sending a series of text
messages. For example:
context.createProducer().send(dest, context.createMessage());
The consumer code can then interpret a non-text message as a signal that all the messages sent have now been received.
The examples in Chapter 49, "Java Message
Service Examples," use messages of type TextMessage
, MapMessage
,
and Message
.
Messages sent to a queue remain in the queue until the message consumer
for that queue consumes them. The JMS API provides a QueueBrowser
object that allows you to browse the messages in the queue and display
the header values for each message. To create a QueueBrowser
object,
use the JMSContext.createBrowser
method. For example:
QueueBrowser browser = context.createBrowser(queue);
See Browsing Messages on a Queue for an
example of using a QueueBrowser
object.
The createBrowser
method allows you to specify a message selector as a
second argument when you create a QueueBrowser
. For information on
message selectors, see JMS Message Selectors.
The JMS API provides no mechanism for browsing a topic. Messages usually disappear from a topic as soon as they appear: If there are no message consumers to consume them, the JMS provider removes them. Although durable subscriptions allow messages to remain on a topic while the message consumer is not active, JMS does not define any facility for examining them.
The root class for all checked exceptions in the JMS API is
JMSException
. The root cause for all unchecked exceptions in the JMS
API is JMSRuntimeException
.
Catching JMSException
and JMSRuntimeException
provides a generic way
of handling all exceptions related to the JMS API.
The JMSException
and JMSRuntimeException
classes include the
following subclasses, described in the API documentation:
IllegalStateException
, IllegalStateRuntimeException
InvalidClientIDException
, InvalidClientIDRuntimeException
InvalidDestinationException
, InvalidDestinationRuntimeException
InvalidSelectorException
, InvalidSelectorRuntimeException
JMSSecurityException
, JMSSecurityRuntimeException
MessageEOFException
MessageFormatException
, MessageFormatRuntimeException
MessageNotReadableException
MessageNotWriteableException
, MessageNotWriteableRuntimeException
ResourceAllocationException
, ResourceAllocationRuntimeException
TransactionInProgressException
,
TransactionInProgressRuntimeException
TransactionRolledBackException
,
TransactionRolledBackRuntimeException
All the examples in the tutorial catch and handle JMSException
or
JMSRuntimeException
when it is appropriate to do so.
Previous | Next | Contents |