Friday 16 October 2009

JMS: Basic Concepts - Part 1



1. Introduction
My intention with this post is to summarize the most important ideas behind of JMS. As always, I am not trying to reinvent the wheel and all the notes are taken from the specification.


2. What Is the JMS API?

The Java Message Service is a Java API that allows applications to create, send, receive, and read messages.
The JMS API enables distributed 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.

3.  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 Remote Procedure Call (RPC), under the following circumstances.

• The provider wants the components not to depend on information about other components’ interfaces, so that 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.


4. JMS API Architecture
A JMS application is composed of the following parts.

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 Java 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.

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.





5.  Messaging Domains
Point-to-Point Messaging Domain
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:

• 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.

Publish/Subscribe Messaging Domain
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.


6.  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.

7.Main elements in JMS application

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.

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.


  • Context ctx = new InitialContext();

             

  • QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");

             

  • TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) ctx.lookup("TopicConnectionFactory");


Destinations
A destination is the object a client uses to specify the target of messages it produces
and the source of messages it consumes.


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



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


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.


  • QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();

             

  • TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();


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.
A session provides a transactional context with which to group a set of sends and receives into an atomic unit of work.


  • TopicSession topicSession = topicConnection.createTopicSession(false,

                     Session.AUTO_ACKNOWLEDGE);
             

  • QueueSession queueSession = queueConnection.createQueueSession(true, 0);


Message Producers
A message producer is an object created by a session and is used for sending messages to a destination.


  • QueueSender queueSender = queueSession.createSender(myQueue);



  • TopicPublisher topicPublisher = topicSession.createPublisher(myTopic);


Once you have created a message producer, you can use it to send messages.


  • queueSender.send(message);



  • topicPublisher.publish(message);


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.


  • QueueReceiver queueReceiver = queueSession.createReceiver(myQueue);



  • TopicSubscriber topicSubscriber = topicSession.createSubscriber(myTopic);


You use the TopicSession.createDurableSubscriber method to create a durable topic subscriber.

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 the MessageListener interface, you can register the message listener as follows:


  • TopicListener topicListener = new TopicListener();



  • topicSubscriber.setMessageListener(topicListener);


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 a TopicSubscriber 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 on page 75.

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.

8. Examples

Example 1 - Sending Messages to a Queue
/*
       * Look up connection factory and queue. If either does
       * not exist, exit.
       */
       try {
              queueConnectionFactory = (QueueConnectionFactory)
              jndiContext.lookup("QueueConnectionFactory");
              queue = (Queue) jndiContext.lookup(queueName);
       } catch (NamingException e) {
              ......................
       }
       /*
       * Create connection.
       * Create session from connection; false means session is not transacted.
       * Create sender and text message.
       * Send messages, varying text slightly.
       * Send end-of-messages message.
       * Finally, close connection.
       */
       try {
              queueConnection = queueConnectionFactory.createQueueConnection();
              queueSession =
queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
              queueSender = queueSession.createSender(queue);
              message = queueSession.createTextMessage();
              for (int i = 0; i < NUM_MSGS; i++) {
                           message.setText("This is message " + (i + 1));
                           queueSender.send(message);
              }
              /*
               * Send a non-text control message indicating end of
               * messages.
               */
              queueSender.send(queueSession.createMessage());
       } catch (JMSException e) {
                     .............
       } finally {  
                     if (queueConnection != null) {
                           try {
                                  queueConnection.close();
                           } catch (JMSException e) {
                                  .............
}
                     }
       }

     
Example 2 - Publishing Messages to a Topic

/*
* Look up connection factory and topic. If either does
      * not exist, exit.
      */
try {
                     topicConnectionFactory = (TopicConnectionFactory)
                     jndiContext.lookup("TopicConnectionFactory");
                     topic = (Topic) jndiContext.lookup(topicName);
              } catch (NamingException e) {
                     ............
              }
       /*
       * Create connection.
       * Create session from connection; false means session is
       * not transacted.
       * Create publisher and text message.
       * Send messages, varying text slightly.
       * Finally, close connection.
       */
       try {
              topicConnection =
                     topicConnectionFactory.createTopicConnection();
              topicSession =
topicConnection.createTopicSession(false,
                           Session.AUTO_ACKNOWLEDGE);
              topicPublisher = topicSession.createPublisher(topic);
              message = topicSession.createTextMessage();
              for (int i = 0; i < NUM_MSGS; i++) {
                           message.setText("This is message " + (i + 1));
                           topicPublisher.publish(message);
              }
       } catch (JMSException e) {
              ............
       } finally {
              if (topicConnection != null) {
                     try {
                           topicConnection.close();
                     } catch (JMSException e) {
                           ............
                     }
              }
       }
      

No comments:

Post a Comment