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.
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 in order 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.
The current version of the JMS specification is Version 1.1.
When Can You Use the JMS API?
An enterprise application provider is likely to choose a messaging API over a tightly coupled API, such as a remote procedure call (RPC), under the following circumstances.
- The provider wants the components not to depend on information about other components’ interfaces, so components can be easily replaced.
- The provider wants the application to run whether or not all components are up and running simultaneously.
- The application business model allows a component to send information to another and to continue to operate without receiving an immediate response.
For example, components of an enterprise application for an automobile manufacturer can use the JMS API in situations like these:
- The inventory component can send a message to the factory component when the inventory level for a product goes below a certain level so the factory can make more cars.
- The factory component can send a message to the parts components so the factory can assemble the parts it needs.
- The parts components in turn can send messages to their own inventory and order components to update their inventories and to order new parts from suppliers.
- Both the factory and the parts components can send messages to the accounting component to update budget numbers.
- The business can publish updated catalog items to its sales force.
Messaging Domains:
most messaging products supported either the point-to-point or the publish/subscribe approach to messaging.
Point-to-Point Messaging Domain
A point-to-point (PTP) product or application is built on the concept of message queues, senders, and receivers. Each message is addressed to a specific queue, and receiving clients extract messages from the queues established to hold their messages. Queues retain all messages sent to them until the messages are consumed or expire.
PTP messaging, illustrated in Figure 47-3, has the following characteristics:
- 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.
Figure 47-3 Point-to-Point Messaging
Publish/Subscribe Messaging Domain
In a publish/subscribe (pub/sub) product or application, clients address messages to a topic, which functions somewhat like a bulletin board. Publishers and subscribers are generally anonymous and can 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 can 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 subscribers to create durable subscriptions, which 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 Creating Durable Subscriptions.
Use pub/sub messaging when each message can be processed by any number of consumers (or none). Figure 47-4 illustrates pub/sub messaging.
Figure 47-4 Publish/Subscribe Messaging
Message Consumption
Messaging products are inherently asynchronous: There is no fundamental timing dependency 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.
The JMS API Programming Model
The basic building blocks of a JMS application are:
- Administered objects: connection factories and destinations
- Connections
- Sessions
- Message producers
- Message consumers
- Messages
JMS Connection Factories
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, orTopicConnectionFactory interface. To learn how to create connection factories
At the beginning of a JMS client program, you usually inject a connection factory resource into a ConnectionFactory object. For example, the following code fragment specifies a resource whose JNDI name is jms/ConnectionFactory and assigns it to a ConnectionFactory object:
@Resource(lookup = "jms/ConnectionFactory")
private static ConnectionFactory connectionFactory;
In a Java EE application, JMS administered objects are normally placed in the jms naming subcontext.
JMS 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.
In the pub/sub messaging domain, destinations are called topics.
A JMS application can use multiple queues or topics (or both).
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 one domain or the other. To create an application that allows you to use the same code for both topics and queues, you assign the destination to aDestination 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/Queue")
private static Queue queue;
@Resource(lookup = "jms/Topic")
private static Topic topic;
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.
JMS Connections
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.
Connections implement the Connection interface. When you have a ConnectionFactory object, you can use it to create a Connection:
Connection connection = connectionFactory.createConnection();
Before an application completes, you must close any connections 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.
connection.close();
JMS Sessions
A session is a single-threaded context for producing and consuming messages. You use sessions to create the following:
- Message producers
- Message consumers
- Messages
- Queue browsers
- Temporary queues and topics
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 API Local Transactions.
Sessions implement the Session interface. After you create a Connection object, you use it to create a Session:
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
The first argument means the session is not transacted; the second means the session automatically acknowledges messages when they have been received successfully. (For more information, see Controlling Message Acknowledgment.)
To create a transacted session, use the following code:
Session session = connection.createSession(true, 0);
A session is a single-threaded context for producing and consuming messages. You use sessions to create the following:
JMS Message Producers
A message producer is an object that is created by a session and used for sending messages to a destination. It implements the MessageProducer interface.
You use a Session to create a MessageProducer for a destination. The following examples show that you can create a producer for a Destination object, aQueue object, or a Topic object.
MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);
You can create an unidentified producer by specifying null as the argument to createProducer. With an unidentified producer, you do not specify a destination until you send a message.
After you have created a message producer, you can use it to send messages by using the send method:
producer.send(message);
You must first create the messages; see JMS Messages.
If you have created an unidentified producer, use an overloaded send method that specifies the destination as the first parameter. For example:
MessageProducer anon_prod = session.createProducer(null);
anon_prod.send(dest, message);
A message producer is an object that is created by a session and used for sending messages to a destination. It implements the MessageProducer interface.You use a Session to create a MessageProducer for a destination. The following examples show that you can create a producer for a Destination object, aQueue object, or a Topic object.MessageProducer producer = session.createProducer(dest); MessageProducer producer = session.createProducer(queue); MessageProducer producer = session.createProducer(topic);You can create an unidentified producer by specifying null as the argument to createProducer. With an unidentified producer, you do not specify a destination until you send a message.After you have created a message producer, you can use it to send messages by using the send method:producer.send(message);You must first create the messages; see JMS Messages.If you have created an unidentified producer, use an overloaded send method that specifies the destination as the first parameter. For example:MessageProducer anon_prod = session.createProducer(null); anon_prod.send(dest, message);
JMS Message Consumers
A message consumer is an object that is created by a session and used for receiving messages sent to a destination. It implements the MessageConsumerinterface.
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.
For example, you could use a Session to create a MessageConsumer for a Destination object, a Queue object, or a Topic object:
MessageConsumer consumer = session.createConsumer(dest);
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer consumer = session.createConsumer(topic);
You use the Session.createDurableSubscriber method to create a durable topic subscriber. This method is valid only if you are using a topic. For details,
After 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 MessageConsumer to make the message consumer inactive. Message delivery does not begin until you start the connection you created by calling its start method. (Remember always to call the start method; forgetting to start the connection is one of the most common JMS programming errors.)
You use the receive method to consume a message synchronously. You can use this method at any time after you call the start method:
connection.start();
Message m = consumer.receive();
connection.start();
Message m = consumer.receive(1000); // time out after a second
To consume a message asynchronously, you use a message listener, as described in the next section.
A message consumer is an object that is created by a session and used for receiving messages sent to a destination. It implements the MessageConsumerinterface.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.For example, you could use a Session to create a MessageConsumer for a Destination object, a Queue object, or a Topic object:MessageConsumer consumer = session.createConsumer(dest); MessageConsumer consumer = session.createConsumer(queue); MessageConsumer consumer = session.createConsumer(topic);You use the Session.createDurableSubscriber method to create a durable topic subscriber. This method is valid only if you are using a topic. For details,After 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 MessageConsumer to make the message consumer inactive. Message delivery does not begin until you start the connection you created by calling its start method. (Remember always to call the start method; forgetting to start the connection is one of the most common JMS programming errors.)You use the receive method to consume a message synchronously. You can use this method at any time after you call the start method:connection.start(); Message m = consumer.receive(); connection.start(); Message m = consumer.receive(1000); // time out after a secondTo consume a message asynchronously, you use a message listener, as described in the next section.
JMS 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 MessageConsumer by using the setMessageListener method. For example, if you define a class namedListener that implements the MessageListener interface, you can register the message listener as follows:
Listener myListener = new Listener();
consumer.setMessageListener(myListener);
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 MessageConsumer by using the setMessageListener method. For example, if you define a class namedListener that implements the MessageListener interface, you can register the message listener as follows:Listener myListener = new Listener(); consumer.setMessageListener(myListener);
JMS Messages
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.
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.
Message Headers
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 47-1lists 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 or the publish method, which overrides any client-set values.
Table 47-1 How JMS Message Header Field Values Are Set
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 47-1lists 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 or the publish method, which overrides any client-set values.Table 47-1 How JMS Message Header Field Values Are Set
Header Field
|
Set By
|
|---|---|
send or publish method
| |
send or publish method
| |
send or publish method
| |
send or publish method
| |
send or publish method
| |
send or publish method
| |
Client
| |
Client
| |
Client
| |
JMS provider
|
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 JMS Message Selectors). For an example of setting a property to be used as a message selector, see An Application That Uses the JMS API with a Session Bean.
The JMS API provides some predefined property names that a provider can support. The use of these predefined properties or of user-defined properties is optional.
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 An Application That Uses the JMS API with a Session Bean.The JMS API provides some predefined property names that a provider can support. The use of these predefined properties or of user-defined properties is optional.
Message Bodies
The JMS API defines five message body formats, also called message types, which allow you to send and receive data in many different forms and which provide compatibility with existing messaging formats. Table 47-2 describes these message types.
Table 47-2 JMS Message Types
The JMS API defines five message body formats, also called message types, which allow you to send and receive data in many different forms and which provide compatibility with existing messaging formats. Table 47-2 describes these message types.Table 47-2 JMS Message Types
Message Type
|
Body Contains
|
|---|---|
A java.lang.String object (for example, the contents of an XML file).
| |
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.
| |
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 Serializable object in the Java programming language.
| |
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 = session.createTextMessage(); message.setText(msg_text); // msg_text is a String producer.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 = consumer.receive();
if (m instanceof TextMessage) {
TextMessage message = (TextMessage) m;
System.out.println("Reading message: " + message.getText());
} else {
// Handle error
}
JMS 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, described in the API documentation:
- IllegalStateException
- InvalidClientIDException
- InvalidDestinationException
- InvalidSelectorException
- JMSSecurityException
- MessageEOFException
- MessageFormatException
- MessageNotReadableException
- MessageNotWriteableException
- ResourceAllocationException
- TransactionInProgressException
- TransactionRolledBackException
Using Basic Reliability Mechanisms
The basic mechanisms for achieving or affecting reliable message delivery are as follows:
- Controlling message acknowledgment: You can specify various levels of control over message acknowledgment.
- Specifying message persistence: You can specify that messages are persistent, meaning they must not be lost in the event of a provider failure.
- Setting message priority levels: You can set various priority levels for messages, which can affect the order in which the messages are delivered.
- Allowing messages to expire: You can specify an expiration time for messages so they will not be delivered if they are obsolete.
- Creating temporary destinations: You can create temporary destinations that last only for the duration of the connection in which they are created.
Controlling Message Acknowledgment
Until a JMS message has been acknowledged, it is not considered to be successfully consumed. The successful consumption of a message ordinarily takes place in three stages.
- The client receives the message.
- The client processes the message.
- The message is acknowledged. Acknowledgment is initiated either by the JMS provider or by the client, depending on the session acknowledgment mode.
- Session.AUTO_ACKNOWLEDGE: The session automatically acknowledges a client’s receipt of a message either when the client has successfully returned from a call to receive or when the MessageListener it has called to process the message returns successfully.
A synchronous receive in an AUTO_ACKNOWLEDGE session is the one exception to the rule that message consumption is a three-stage process as described earlier. In this case, the receipt and acknowledgment take place in one step, followed by the processing of the message. - Session.CLIENT_ACKNOWLEDGE: A client acknowledges a message by calling the message’s acknowledge method. In this mode, acknowledgment takes place on the session level: Acknowledging a consumed message automatically acknowledges the receipt of all messages that have been consumed by its session. For example, if a message consumer consumes ten messages and then acknowledges the fifth message delivered, all ten messages are acknowledged.
Note - In the Java EE platform, a CLIENT_ACKNOWLEDGE session can be used only in an application client, not in a web component or enterprise bean.
- Session.DUPS_OK_ACKNOWLEDGE: This option instructs the session to lazily acknowledge the delivery of messages. This is likely to result in the delivery of some duplicate messages if the JMS provider fails, so it should be used only by consumers that can tolerate duplicate messages. (If the JMS provider redelivers a message, it must set the value of the JMSRedelivered message header to true.) This option can reduce session overhead by minimizing the work the session does to prevent duplicates
Specifying Message Persistence
The JMS API supports two delivery modes specifying whether messages are lost if the JMS provider fails. These delivery modes are fields of the DeliveryModeinterface.
- The PERSISTENT delivery mode, the default, instructs the JMS provider to take extra care to ensure that a message is not lost in transit in case of a JMS provider failure. A message sent with this delivery mode is logged to stable storage when it is sent.
- The NON_PERSISTENT delivery mode does not require the JMS provider to store the message or otherwise guarantee that it is not lost if the provider fails.
You can specify the delivery mode in either of two ways.
- You can use the setDeliveryMode method of the MessageProducer interface to set the delivery mode for all messages sent by that producer. For example, the following call sets the delivery mode to NON_PERSISTENT for a producer:
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
- You can use the long form of the send or the publish method to set the delivery mode for a specific message. The second argument sets the delivery mode. For example, the following send call sets the delivery mode for message to NON_PERSISTENT:
producer.send(message, DeliveryMode.NON_PERSISTENT, 3, 10000);
Setting Message Priority Levels
You can use message priority levels to instruct the JMS provider to deliver urgent messages first. You can set the priority level in either of two ways.
- You can use the setPriority method of the MessageProducer interface to set the priority level for all messages sent by that producer. For example, the following call sets a priority level of 7 for a producer:
producer.setPriority(7);
- You can use the long form of the send or the publish method to set the priority level for a specific message. The third argument sets the priority level. For example, the following send call sets the priority level for message to 3:
producer.send(message, DeliveryMode.NON_PERSISTENT, 3, 10000);
The ten levels of priority range from 0 (lowest) to 9 (highest). If you do not specify a priority level, the default level is 4. A JMS provider tries to deliver higher-priority messages before lower-priority ones but does not have to deliver messages in exact order of priority.
Allowing Messages to Expire
By default, a message never expires. If a message will become obsolete after a certain period, however, you may want to set an expiration time. You can do this in either of two ways.
- You can use the setTimeToLive method of the MessageProducer interface to set a default expiration time for all messages sent by that producer. For example, the following call sets a time to live of one minute for a producer:
producer.setTimeToLive(60000);
- You can use the long form of the send or the publish method to set an expiration time for a specific message. The fourth argument sets the expiration time in milliseconds. For example, the following send call sets a time to live of 10 seconds:
producer.send(message, DeliveryMode.NON_PERSISTENT, 3, 10000);
No comments:
Post a Comment