Intro to Java Message Service 1.1
This article illustrates how to program using the new features of the Java Message Service (JMS) 1.1 API. The new features in JMS 1.1 are demonstrated through a sample application.
This article illustrates how to program using the new features of the Java Message Service (JMS) 1.1 API. The new features in JMS 1.1 are demonstrated through a sample application.
What is new in JMS 1.1?
JMS 1.1 is the latest API from Sun Microsystems, Inc that supercedes the JMS 1.0.2b API. The key change in JMS 1.1 is the unification of the API for Pub/Sub and Point-to-Point domains by introducing a new set of methods at the base interface/class that are generic and apply to both the Pub/Sub and Point-to-Point domains. These additions preserve the backward compatibility to JMS 1.0.2b and simplify the client programming by presenting a unified programming API.
Today, most of the enterprise applications use an amalgamation of Pub/Sub and PTP domains while using messaging as a transport backbone to solve complex business problems. While JMS 1.0.2b provided a robust way of dealing with each domain separately, there was no clean way of merging the domains within a transaction. A very important benefit of this unification of the domains [available in JMS 1.1] is in the area of transactions. In JMS the scope of a transaction is at Session level. The Session interface in JMS 1.0.2b didn't provide any methods to create Producers and/or Consumers. These methods were only available in TopicSession and QueueSession interfaces. This meant that using JMS 1.0.2b in a given transaction, you could either deal with topics or deal with queues.
In JMS 1.1 you can use the generic Session class and then create the appropriate Producers (Publishers and Senders) or Consumers (Subscribers and Receivers) on either Topics and/or Queues thereby extending the transactions across both the domains. In the next few sections we will review the appropriate interface hierarchies and the changes brought by the new API.
ConnectionFactory
A ConnectionFactory is typically configured by an administrator and is used to get a connection instance to a JMS server. The hierarchy in JMS 1.0.2b as well as JMS 1.1 is shown in Figure 1
Figure 1 - ConnectionFactory Hierarchy
In JMS 1.0.2b you could not create a connection from the base ConnectionFactory. In JMS 1.1, methods are added to the base ConnectionFactory to create connections so that you do not need to go to TopicConnectionFactory or QueueConnectionFactory to create connections. The new methods are:
Connection createConnection() throws JMSException; Connection createConnection(String userName, String password) throws JMSException;
Connection
This represents an active connection to a JMS server. The hierarchy in JMS 1.0.2b as well as JMS 1.1 is shown in Figure 2.
Figure 2 - Connection Hierarchy
In JMS 1.0.2b you could not create a session or connection consumer from the base Connection. You had to do it from TopicConnection or QueueConnection. In JMS 1.1 methods have been added to the base Connection to create sessions and connection consumers so that you do not need to go to TopicConnection or QueueConnection to do so. The new methods are:
ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException; ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException; Session createSession(boolean transacted, int acknowledgeMode) throws JMSException;
Session
Session provides the context in which messages are produced and consumed. The hierarchy in JMS 1.0.2b as well as JMS 1.1 is shown in Figure 3.
Figure 3 - Session Hierarchy
In JMS 1.0.2b you could not create a producer, consumer, browser, topic or queue from the base Session. You had to do it from TopicSession or QueueSession. In JMS 1.1 methods have been added to the base Session to create producers, consumers, browsers, topics or queues so that you do not need to go to TopicSession or QueueSession to do so. The new methods are:
MessageProducer createProducer(Destination destination) throws JMSException; MessageConsumer createConsumer(Destination destination) throws JMSException; MessageConsumer createConsumer(Destination destination, java.lang.String messageSelector) throws JMSException; MessageConsumer createConsumer(Destination destination, java.lang.String messageSelector, boolean NoLocal) throws JMSException; TopicSubscriber createDurableSubscriber(Topic topic, java.lang.String name) throws JMSException; TopicSubscriber createDurableSubscriber(Topic topic, java.lang.String name, java.lang.String messageSelector, boolean noLocal) throws JMSException; QueueBrowser createBrowser(Queue queue) throws JMSException; QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException; Queue createQueue(String name) throws JMSException; Topic createTopic(String name) throws JMSException; TemporaryTopic createTemporaryTopic() throws JMSException; TemporaryQueue createTemporaryQueue() throws JMSException; void unsubscribe(String string name);
MessageProducer
A client uses a message producer to send a message to the destination (topic or queue). The hierarchy in JMS 1.0.2b as well as JMS 1.1 is shown in Figure 4.
Figure 4 - MessageProducer Hierarchy
In JMS 1.0.2b you could not send a message from the base MessageProducer. You had to do it from TopicPublisher or QueueSender. In JMS 1.1 methods have been added to the base MessageProducer to send messages so that you do not need to go to TopicPublisher or QueueSender to do so. The new methods are:
Destination getDestination() throws JMSException; void send(Message message) throws JMSException; void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException; void send(Destination destination, Message message) throws JMSException; void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException;
Programming steps using JMS API
Writing a JMS program involves a certain number of steps. You can follow the steps outlined in the following table using JMS 1.0.2b and JMS 1.1
Sample Application
This tutorial uses the sample application, TransactionTextMsg, to demonstrate the the domain unification for transacted sessions.
This application consists of three programs:
TxtMessage_Producer:
Produces message on a topic
TransactionTextMsg_ProducerConsumer:
Consumes message on a topic and produce message on a queue within a single transaction.
TxtMessage_Consumer:
Consumes message on a queue
Any text message produced by TxtMessage_Producer are displayed on the TransactionTextMsg_ProducerConsumer and also sent to TxtMessage_Consumer. It is however not displayed on the TxtMessage_Consumer until the user specifies that the transaction is committed. If the user chooses to rollback the transaction no message will be displayed on TxtMessage_Consumer.
Lets analyze the most relevant portions of TransactionTextMsg_ProducerConsumer class to see how to use the JMS 1.1 API to do a transacted session across Pub/Sub and Point-to-Point domain. In the following code snippet only Step 1 is vendor dependent. If you want to try the sample with any JMS 1.1 provider other than AshnaMQ 2.1 then you must make appropriate changes in Step 1.
// ----------------------------------- // | Step 1: Create a JNDI connection | // ----------------------------------- Properties env = new Properties(); // Specifying the JNDI connection properties for AshnaMQ String providerURL = "jndi:Ashna://" + server + ":" + port; env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ashnasoft.jms.client.jndi.InitialContextFactoryImpl"); env.put(Context.PROVIDER_URL, providerURL); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, username); env.put(Context.SECURITY_CREDENTIALS, password); InitialContext ic = new InitialContext(env); // JNDI connection // ------------------------------------------------------------ // | Step 2: Perform JNDI lookups on the administered objects | // ------------------------------------------------------------ // Look up the connection factory ConnectionFactory conFactory = (ConnectionFactory)ic.lookup("ConnectionFactory"); // Look up the specified topic and queue topic = (Topic)ic.lookup(topicName); queue = (Queue)ic.lookup(queueName); // ------------------------------ // | Step 3: Create a connection | // ------------------------------ connection = conFactory.createConnection(username, password); connection.setClientID("TransactionTextMsg_ProducerConsumer"); // -------------------------------------- // | Step 4: Create a TRANSACTED session | // -------------------------------------- session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); // 'true' boolean value indicates transacted session // ---------------------------------- // | Step 5: Create a topic consumer | // ---------------------------------- consumer = session.createConsumer(topic); // ------------------------------------ // | Step 6: Set the message listener | // ------------------------------------ consumer.setMessageListener(this); // ---------------------------------- // | Step 7: Create a queue producer | // ---------------------------------- producer = session.createProducer(queue); // Set any remaining instance variables this.username = username; // Allow the topic listener to begin receiving messages connection.start();
The suggested demonstration for the TransactionTextMsg application is described in Appendix A.
Summary
JMS is now the de facto standard API for Message Oriented Middleware. The latest specification, JMS 1.1, is a maintenance release for prior JMS versions by Sun Microsystems. The enhancements include the unification of the Pub/Sub and PTP domains under a common programming and transactional model along with some minor API modifications.
For further information, contact [email protected].
Appendix A: Suggested Demonstration
To try the sample application with AshnaMQ 2.1 simply follow the steps defined below. To try the sample application with any other JMS provider please make sure that it supports JMS 1.1, then download the sample application from ftp://ftp.ashnasoft.com/TransactionTextMsgJMS11.zip and modify Step 1 of the Java files to make it specific to the provider.
- Download the sample application and download AshnaMQ 2.1 installer from http://www.ashnasoft.com
- Follow the instructions in the AshnaMQ 2.1 readme.html file to start the JMS server and setup the environment to run the samples. The samples are located in [AshnaMQ Home]samplesjms11TransactionTextMsg
- The samples run using a pre-configured topic, queue and username so you do not need to setup any topic, queue or username. If you accept the defaults then the application will use a topic named SampleTopic, queue named SampleQueue, user named admin, etc.
- Open three command consoles and setup the environment to run the sample by using setenv.bat or setenv.sh
- In the first console, start an instance of the producer client by typing the following at the command line:
java TransactionTextMsg_ProducerConsumer
- In the second console, start an instance of the transacted producer-consumer pair by typing the following at the command line:
java TransactionTextMsg_ProducerConsumer
- In the third console, start an instance of the consumer client by typing the following at the command line:
java TextMsg_Consumer
- Type text messages in the Producer client window and press the "Enter" key after each message. Note that since the messages are transacted in the ProducerConsumer window, you will not see them appear immediately in the Consumer client window. Once you are ready to deliver all the messages, type SEND in the ProducerConsumer. You should then see the same messages appear in the Consumer client window. If you type CANCEL in the ProducerConsumer client window then you will see no messages delievered to the Consumer client.