devxlogo

Master JMS Messaging with OpenJMS

Master JMS Messaging with OpenJMS

rganizations are increasingly demanding applications where communication between software components is loosely coupled and often asynchronous. In asynchronous communication, an application sends a message, and then continues program execution without waiting for a response. In a loosely coupled system, the sending program might not even know where or what the recipient is, because the communication between software components takes place via messaging. The Java Messaging Service (JMS) is the only messaging API supported by J2EE.

OpenJMS, a SourceForge project, is a free open source implementation of Sun Microsystems’ JMS API 1.0.2 specification. In this article you’ll see how to install and use OpenJMS, along with code samples that illustrate the point-to-point and publish/subscribe messaging models it supports.

This article is not intended to be a tutorial on JMS; rather, it’s intended to show you how to use OpenJMS. If you are unfamiliar with JMS basics you should consider taking a look at Sun’s Java Message Service Tutorial before reading this article.

Obtaining and Installing OpenJMS
You can download OpenJMS from http://openjms.sourceforge.net/downloads.html. OpenJMS runs in both Windows and UNIX environments, and requires a Java 2 runtime environment. Throughout this article, I’ll assume you’re using Windows as your operating platform. Refer to the OpenJMS documentation for UNIX instructions. At the time of this article’s writing, OpenJMS was in version 0.7.6. The download contains the code necessary for running the OpenJMS server as well as the client JARS needed to write client applications that interact with the server. The download also includes documentation and sample programs.

To install OpenJMS, simply extract the files from the downloaded archive with WinZip, or with the JAR tool using the command:

   jar xvf openjms-0.7.6.zip

Before firing up the OpenJMS server, you need to set a couple of environment variables: a JAVA_HOME variable pointing to the directory where your Java 2 runtime is installed (e.g., C:jdk1.3.1_11in) and an OPENJMS_HOME variable pointing to the OpenJMS install directory (e.g., C:openjms-0.7.6).

To start the OpenJMS messaging server, use the following commands:

   cd %OPENJMS_HOME%in   startup

Similarly, you can shut down the server using the shutdown command.

   cd %OPENJMS_HOME%in   shutdown

After launching the server, you are ready to interact with it using Java programs that leverage the JMS API. Client applications must have the appropriate jar files: jms-1.02a.jar, openjms-0.7.6.jar, and openjms-client-0.7.6.jar on the Java build path. You’ll find all these in the lib directory of your OpenJMS installation.

Getting a JNDI Context
By default, OpenJMS uses an embedded JNDI (Java Naming and Directory Interface) provider which listens to port 1099. As you will see, you can use JNDI to store connection factories, topics, and queues. OpenJMS allows you to use a third-party JNDI provider, but this article’s code uses the embedded provider. To access topics and queues, you will need to create a JNDI context exemplified with the code below:

   import java.util.Hashtable;   import javax.naming.Context;   import javax.naming.InitialContext;   ...   Hashtable properties = new Hashtable();   properties.put(Context.INITIAL_CONTEXT_FACTORY,      "org.exolab.jms.jndi.InitialContextFactory");   properties.put(Context.PROVIDER_URL,      "rmi://localhost:1099/");   Context context = new InitialContext(properties);

You’ll see the syntax above in all the provided code samples, so for brevity, in the remainder of this article you’ll see a comment where the boilerplate code belongs.

Point-to-point Messaging with OpenJMS
You should recall that in the point-to-point messaging style, a sender sends messages to a queue. In this messaging model, messages are received by one?and only one?receiver.

Writing to a Queue
Writing to a queue consists of the following steps:

  1. Obtain the JNDI context.
  2. Contact the JMS provider, OpenJMS, and get a JMS connection from the queue connection factory.
  3. Create a queue connection using the connection factory.
  4. Establish a queue session for the newly established connection.
  5. Lookup the target queue destination.
  6. Create a queue sender that will be used to send messages and associate it to the queue session.
  7. Send a message using the queue sender.

The code for the QueueSend class shown below follows these steps. Note that it uses a queue named queue1 The OpenJMS install pre-configures this queue by default; however you can refer to the section Creating Your Own Topics and Queues in this article to see how to configure your own queues.

   public class QueueSend   {      public static void main(String[] args)      {         try         {            // standard boilerplate code to establish a              // JNDI context and connection                        // retrieve queue connection factory            QueueConnectionFactory                queueConnectionFactory =                (QueueConnectionFactory)context.lookup(               "JmsQueueConnectionFactory");               // create a queue connection            QueueConnection queueConnection =                       queueConnectionFactory.              createQueueConnection();                        // create a queue session            // set transactions to false and set auto             // acknowledgement of receipt of messages            QueueSession queueSession =                queueConnection.createQueueSession(               false,Session.AUTO_ACKNOWLEDGE);                        // retrieve queue            Queue queue =                (Queue)context.lookup("queue1");                        // create a queue sender and associate to             // the retrieved queue            QueueSender queueSender =                queueSession.createSender(queue);                        // send a message to the queue             TextMessage message =                queueSession.createTextMessage();            message.setText("Hello there.");            queueSender.send(message);                        System.out.println(               "Sample application writing message to " +                   "queue.");                        // clean up               ...                              }         catch (NamingException e)         {            e.printStackTrace();         }         catch (JMSException e)         {            e.printStackTrace();         }      }   }

Now that you’ve seen how to write a message to the queue, you can retrieve the message from the queue either synchronously or asynchronously.

Synchronous and Asynchronous Retrieval
Retrieving a message synchronously is similar to the process of writing to the queue, but instead of using a QueueSender object, you use a QueueReceiver object. You must also call the start() method on the queue connection, which starts the delivery of incoming messages. After doing that, you receive messages by calling the synchronous receive() method, as shown in the QueueReceiveSynchronous class below.

   public class QueueReceiveSynchronous   {      public static void main(String[] args)      {         try         {            // standard boilerplate code to establish a JNDI             //  context and connection                        // retrieve queue connection factory            QueueConnectionFactory                queueConnectionFactory =                (QueueConnectionFactory)context.lookup(               "JmsQueueConnectionFactory");                        // create a queue connection            QueueConnection queueConnection =           queueConnectionFactory.createQueueConnection();                        // start delivery of incoming messages            queueConnection.start();                        // create a queue session            // set transactions to false and set auto             // acknowledgement of receipt of messages            QueueSession queueSession =                queueConnection.createQueueSession(               false,Session.AUTO_ACKNOWLEDGE);                        // retrieve queue            Queue queue =                (Queue)context.lookup("queue1");                        // create a queue receiver and associate             // to the retrieved queue            QueueReceiver queueReceiver =                queueSession.createReceiver(queue);                        // receive message using the synchronous             // receive method             Message message = queueReceiver.receive();            String messageText = null;            if (message instanceof TextMessage)               messageText =                   ((TextMessage)message).getText();            System.out.println(messageText);                        // clean up             ...         }         catch (NamingException e)         {            e.printStackTrace();         }         catch (JMSException e)         {            e.printStackTrace();         }      }   }

Asynchronous Retrieval
To listen asynchronously to the queue, register an object that implements the MessageListener interface, that simply requires you to implement the onMessage method, in which you process the incoming message as desired. The QueueReceiveAschynchronous class shown in Listing 1 receives messages asynchronously.

Publish/Subscribe Messaging with OpenJMS
The other major message domain supported by JMS is called publish/subscribe. In the publish-and-subscribe messaging style, a publisher sends messages to a topic, and those messages may be received by one or more subscribers.

Writing to a Topic (a.k.a. Publishing)
Writing to a topic is not much different from writing to a queue. Take a look at the code below, which places a message on a topic. In essence, you have just replaced references to a queue with a topic. This time, the code leverages a TopicConnectionFactory as opposed to a QueueConnectionFactory. Similarly, it uses a TopicConnection, a TopicSession, and a TopicPublisher (as opposed to a QueueConnection, QueueSesion, and QueueSender respectively).

Writing to a topic is often referred to as “publishing,” and reading from a topic is called “subscribing.” In the point-to-point messaging domain vernacular, writing to a queue is called sending and reading from a queue is called receiving. Take note that just as you wrote to queue1 in the point-to-point example, you’ll now write to topic1 (again, a topic which the OpenJMS install process preconfigures. Refer to the section Creating Your Own Topics and Queues to find out how to create a different topic.

Unlike in the point-to-point to messaging domain however, messages in the publish/subscribe domain are not persistent. Thus, when you run a typical publisher, a subscriber program (presented in the next section) must be running to receive the messages. You can remove the requirement that the subscriber must be running by using durable subscriptions, which I’ll cover in a future article.

   public class TopicPublish   {      public static void main(String[] args)      {         // standard boilerplate code to           // establish a JNDI context and connection         try         {            // retrieve topic connection factory            TopicConnectionFactory factory =               (TopicConnectionFactory)context.lookup(              "JmsTopicConnectionFactory");                        // create a topic connection using factory            TopicConnection topicConnection =                factory.createTopicConnection();            topicConnection.start();                        // create a topic session            // set transactions to false and set auto             // acknowledgement of receipt of messages            TopicSession topicSession =                topicConnection.createTopicSession(               false,Session.AUTO_ACKNOWLEDGE);                        // lookup the topic, topic1            Topic topic = (Topic)                context.lookup("topic1");                        // create a topic publisher and associate             // to the retrieved topic            TopicPublisher topicPublisher =                topicSession.createPublisher(topic);                        // publish a message to the topic            System.out.println(               "Sample application publishing " +               "a message to a topic.");            TextMessage message =                topicSession.createTextMessage(                  "Hello world");            topicPublisher.publish(message);                        // clean up            ...         }         catch (NamingException e)         {            e.printStackTrace();         }         catch (JMSException e)         {            e.printStackTrace();         }      }   }
 
Figure 1: The TopicSubscribeAsynchronous Application: This application catches the messages sent by running the TopicPublish application three times.

Reading from a Topic (a.k.a. Subscribing)
The code for retrieving messages from the topic is programmatically very similar to retrieving messages from a queue. You can retrieve messages both synchronously and asynchronously in the publish/subscribe message domain, just as you can in the point-to-point messaging domain. Listings 3 and 4 show examples of each. Paralleling the point-to-point examples, the class TopicSubscribeSynchronous.java (see Listing 2) uses the “receive” method.

On the other hand, the TopicSubscribeAsynchronous.java class (see Listing 3) uses the onMessage method, which it is obligated to do because it implements the MessageListener interface.

After running TopicPublish a number of times, you’ll see that the TopicSubscribeAsynchronous application processes each incoming message on the topic1 topic (see Figure 1).

 
Figure 2: The OpenJMS Administrator GUI: You can use the Administrator GUI application to create and configure queues and topics, as well as configure JNDI, security, and garbage collection settings.

Creating Your Own Topics and Queues
The sample code for this article uses instances (queues and topics) preconfigured by the install program for OpenJMS. You can create your own queues and topics using the OpenJMS administration API, via the OpenJMS configuration file (openjms.xml) or via the OpenJMS Administrator GUI. You launch the Administrator GUI by running the admin batch file in the bin directory (see Figure 2).

You can configure a number of items (JNDI, Security, Garbage Collection) via XML configuration files (see the OpenJMS configuration documentation on SourceForge).

To add queues and topics, simply add to the section of the openjms.xml file (located in the config directory of your OpenJMS installation):

                                                                         

For example, to add a topic named “kulvirTopic” and a queue named “kulvirQueue” you would modify the configuration file as follows:

                                                                                       

After modifying the configuration file, restart OpenJMS. The new configuration binds both the new kulvirTopic topic and the new kulvirQueue queue in JNDI.

OpenJMS Limitations
OpenJMS does have some limitations. For example, OpenJMS does not support XA transactions or high availability through clustering and failover. Consequently, if you need those more advanced and coveted features, a commercial JMS implementation might be more appropriate. Despite its enterprise-level limitations, OpenJMS is a great tool for learning JMS programming.

In this article, you saw how to interact with the JMS server programmatically using small client applications and how to create new queues and topics for the OpenJMS server by modifying its configuration file. I have covered only the basics of OpenJMS; there is a lot more to it than appears in this article. You can learn more about OpenJMS on the project homepage. The open source community improves OpenJMS constantly, so check for updates often.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist