Java EE 7: Implementing JMS 2.0 Shared Subscriptions in the Java SE Environment

Overview

Purpose

This tutorial covers how to implement Java Message Service 2.0 (JMS 2.0) shared nondurable subscriptions and shared durable subscriptions in the Java Platform, Standard Edition (Java SE) environment.

Time to Complete

Approximately 60 minutes

Introduction

JMS 2.0 is part of Java Platform, Enterprise Edition 7 (Java EE 7). One of the most important new features of JMS 2.0 is shared subscriptions. In JMS 1.1, a topic subscription is not permitted to have more than one consumer at a time. Because the work of processing messages about a topic subscription could not be shared among multiple threads, connections, or Java Virtual Machines (JVMs), the scalability of the application was limited. This restriction is removed in JMS 2.0 with the introduction of a new kind of topic subscription called a shared subscription.

When a message is sent to the topic, all consumers that are subscribed to the topic receive it. The basic idea in shared subscriptions is to have a set of consumers (identified by a shared subscription name) exclusively receive a message from a topic. A topic may have multiple subscriptions. Each message sent to the topic is added to each topic subscription. However, if multiple consumers are on a particular subscription, each message added to that subscription is delivered only to one of those consumers.

JMS 2.0 defines four types of topic subscriptions that can be created:

  • Unshared nondurable subscriptions: These subscriptions are created with the createConsumer method and can have only one consumer who will not receive messages sent while its consumer is offline.
  • Unshared durable subscriptions: These subscriptions are created with the createDurableConsumer method and can have onlyone consumer who will receive the messages sent while its consumer is offline.
  • Shared nondurable subscriptions: These subscriptions are introduced in JMS 2.0, are created with the createSharedConsumer method, and can have many consumers who will not receive messages sent while all consumers are offline.
  • Shared durable subscriptions: These subscriptions are introduced in JMS 2.0, are created with the createSharedDurableConsumer method, and can have many consumers who will not receive the messages sent while all consumers are offline.
In this tutorial, you learn how to use JMS 2.0 shared subscriptions:
  • Create two Java SE projects (message producer and message subscribers)
  • Configure JMS resources in GlassFish Server
  • Develop a JMS message producer
  • Develop a shared nondurable subscription
  • Develop a shared durable subscription
  • Test the project for shared durable and nondurable subscriptions

Hardware and Software Requirements

The following is a list of hardware and software requirements:

  • Download and install the latest JDK from this link (Java SE 7u21 recommended).
  • Download and install NetBeans 7.3.1 with Java EE, which includes GlassFish 4 (Java EE download bundle), from this link. During installation, be sure to select the check box to install GlassFish. JUnit is an optional installation and is not required for this tutorial.

Prerequisites

Before starting this tutorial, you should:

  • Have installed the required software.
  • Ensure that NetBeans is running.

Creating Java Projects

In this section, you create two Java SE 7 applications in the NetBeans IDE.

  1. Select File > New Project.

  2. In the New Project dialog box, perform the following steps on the Choose Project page:

    1. Select Java from Categories.
    2. Select Java Application from Projects.
    3. Click Next.
  3. On the Name and Location page:

    1. Enter JMS2Producer as the project name.
    2. Click Finish.

      Note: Uncheck Create Main Class option if it is already checked.


    3. The JMS2Producer project is created in NetBeans.

    4. Repeat steps 1, 2, and 3 to create another Java project, JMS2SharedConsumer.

Adding Libraries to the Projects

In this section, you add Java EE 7 API Library and the gf-client.jar and appserv-rt.jar libraries to the JMS2Producer and JMS2SharedConsumer projects.

Adding Java EE 7 API Library

  1. On the Projects tab, right-click JMS2Producer and select Properties.

  2. In the Project Properties dialog box, select Libraries from Categories and click Add Library.

  3. In the Add Library dialog box, select Java EE 7 API Library and click Add Library.

  4. Click OK in the Project Properties dialog box.

Adding the gf-client.jar and the appserv-rt.jar Libraries

  1. On the Projects tab, right-click JMS2Producer and select Properties.

  2. In the Project Properties dialog box, select Libraries from Categories and click Add Jar/Folder.

  3. Perform the following steps to add gf-client.jar:

    1. Browse to $GLASSFISH_HOME/lib. $GLASSFISH_HOME points to the GlassFish 4 installation directory.
    2. Select gf-client.jar.
    3. Click Open.
  4. Perform the following steps to add appserv-rt.jar:

    1. In the Project Properties dialog box, click Add Jar/Folder.

    2. Browse to $GLASSFISH_HOME/lib. $GLASSFISH_HOME points to the GlassFish 4 installation directory.
    3. Select appserv-rt.jar.
    4. Click Open.

  5. Click OK in the Project Properties dialog box.

  6. Perform the following steps to verify that the libraries were added to the project:

    1. On the Projects tab, select the JMS2Producer project.
    2. Expand the Libraries folder to view the listed libraries.
  7. Repeat steps 1 through 6 to add the three libraries to the JMS2SharedConsumer project.

Configuring the JMS Topic Resource

In this section, you configure the JMS resource, JMS Topic, in the GlassFish Server. You do not configure Connection Factory because you are using the default connection factory.

  1. Perform the following steps to log on to the GlassFish Admin console:

    1. On the Services tab, expand the Servers folder.
    2. Right-click GlassFish Server and select Start.
      Note: If your instance of GlassFish has a green triangle next to the fish icon, the server is already started and the Start option is disabled.

    3. Right-click GlassFish Server and select View Domain Admin Console.
      alt description here

      The GlassFish Admin console opens in the browser.

  2. In the GlassFish Admin console, perform the following steps to configure the topic.

    1. In the navigation bar, expand the Resources node and select JMSResources.
    2. Expand JMS Resources and select Destination Resources.

      alt description here
    3. Expand Destination Resources.

    4. On the JMS Destination Resources page, click New.
      alt description here
    5. On the New JMS Destination Resource page, enter or select the following :

      JNDI Name: jms/MyTopic
      Physical Destination Name: MyTopic
      Resource Type: javax.jms.Topic

      alt description here
    6. Click OK.

      The JMS Resources page displays jms/MyTopic in the Destination Resources table.

      alt description here

Verifying the JMS Resources

In this section, you verify the following JMS resources in the GlassFish Server: JMS Topic,jms/MyTopic, and the default connection factory, jms/_defaultConnectionFactory.

  1. Perform the following steps on the Services tab:

    1. Expand Servers.
    2. Right-click GlassFish Server and select Start.

      Note: If your GlassFish instance displays a green triangle next to the fish icon, the server is already started and the Start option is disabled.

      alt description here

      In the Output window, the GlassFish Server console indicates that GlassFish has started.

  2. Perform the following steps:

    1. Expand the Servers > GlassFish Server folders.
    2. Expand the Resources > Connectors folders.
    3. Right-click Admin Object Resources and select Refresh to display the jms/MyTopic resource.
      alt description here
    4. Expand the Connector Resources folder.
      alt description here

      The jms/_defaultConnectionFactory resource is displayed.

Developing a Message Producer

In this section, you develop a JMS message producer named Producer. The producer sends one message to the JMS topic, jms/MyTopic, and the message subscribers to this topic can consume the JMS messages.

Perform the following steps to implement the Producer class:

  1. On the Projects tab, right-click JMS2Producer and select New >Other.

    alt description here
  2. In the New File dialog box, perform the following steps on the Choose File Type page:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    alt description here
  3. On the New Java Class page, perform the following steps:

    1. Enter Producer as the class name.
    2. Enter com.example as the package name.
    3. Click Finish.
    alt description here
  4. Import the following packages:

    import javax.jms.ConnectionFactory;
    import javax.jms.JMSContext;
    import javax.jms.JMSRuntimeException;
    import javax.jms.Topic;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
  5. Declare the following variables:

    private static ConnectionFactory connectionFactory;
    private static Topic topic;
    alt description here
  6. Add the main() method  to the class.

    public static void main(String[] args) throws NamingException {
    }

    alt description here
  7. Add the following lines of code to the main() method:

    Context ctx = new InitialContext();
    connectionFactory = (ConnectionFactory)
    ctx.lookup("java:comp/DefaultJMSConnectionFactory"); topic = (Topic) ctx.lookup("jms/MyTopic");

    alt description here

    The code looks for the JMS resources (connection factory and topic) by using the Java Naming and Directory Interface (JNDI).

  8. Add the following lines of code to the main() method:

    try (JMSContext context =connectionFactory.createContext();)
    { String message = "This is message from producer";
    System.out.println("Sending message: " + message);
    context.createProducer().send(topic,message);
    } catch (JMSRuntimeException e) {
    System.err.println("Exception occurred: " + e.toString());
    System.exit(1); }
    System.exit(0);
    alt description here

    The code does the following:

    1. Injects resources for a connection factory and topic.
    2. Creates a JMSContext in a try-with-resources block.
    3. Creates a consumer on a shared nondurable subscription, specifying a subscription name:
      consumer=context.createSharedConsumer(topic, "SubName");
    4. Creates an instance of the TextListener class and registers it as the message listener for the shared consumer.
    5. Listens for the messages published to the destination and stops when the user enters q or Q.
    6. Catches and handles any exceptions. The end of the try-with-resources block automatically causes the JMSContext to be closed.

Developing a Message Listener for the Shared Subscriber

In this section, you develop a message listener, TextListener. This class acts as the listener for the SharedConsumer class. The TextListener class implements the MessageListener interface by defining an onMessage method that displays the contents of a TextMessage.

Perform the following steps to implement the TextListener class:

  1. On the Projects tab, right-click JMS2SharedConsumer and select New > Other.

    alt description here
  2. In the New File dialog box, perform the following steps on the Choose File Type page:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    alt description here
  3. On the Name and Location page, perform the following steps:

    1. Enter TextListener as the class name.
    2. Enter com.example as the package name.
    3. Click Finish.
    alt description here
  4. Import the following packages:

    import java.util.concurrent.atomic.AtomicLong;
    import javax.jms.JMSException;
    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.TextMessage;

    The TextListener class implements the MessageListener interface.

    public class TextListener implements MessageListener {

    alt description here
  5. Declare and initialize a variable, AtomicLong.

    AtomicLong count = new AtomicLong(0);

    alt description here
  6. Add the onMessage() method.

     @Override
        public void onMessage(Message m) {
            long i;

            try {
                if (m instanceof TextMessage) {
                    i = count.incrementAndGet();
                                    System.out.println("Reading message: " + m.getBody(String.class));
                } else {
                    System.err.println("Message is not a TextMessage");
                }
            } catch (JMSException e) {
                System.err.println("Exception in onMessage(): " + e.toString());
            }
        }

    alt description here
  7. Add the getCount() method.

    public long getCount() {
    return count.get();
    }

    alt description here

Developing a Shared Nondurable Subscription

In this section, you develop a shared nondurable subscriber, the SharedConsumer class, which uses a topic to receive messages by using asynchronous message delivery. It uses the TextListener message listener.

This section describes how to use a shared consumer to distribute messages about a topic among multiple consumers. All consumers share the same subscription.

Perform the following steps to implement the SharedConsumer class:
  1. On the Projects tab, right-click JMS2SharedConsumer and select New > Other.

    alt description here
  2. In the New File dialog box, perform the following steps on the Choose File Type page:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    alt description here
  3. On the New Java Class page, perform the following steps:

    1. Enter SharedConsumer as the class name.
    2. Enter com.example as the package name.
    3. Click Finish.
    alt description here
  4. Import the following packages:

    import java.io.IOException;
    import java.io.InputStreamReader;
    import javax.jms.ConnectionFactory;
    import javax.jms.JMSConsumer;
    import javax.jms.JMSContext;
    import javax.jms.JMSRuntimeException;
    import javax.jms.Topic;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.naming.Context;
  5. Declare the following variables:

    private static ConnectionFactory connectionFactory;
    private static Topic topic;


    alt description here
  6. Add the main method to the class.

    public static void main(String[] args) throws NamingException { }

    alt description here
  7. Add the following lines of code to the main method:

    Context ctx = new InitialContext();
    connectionFactory = (ConnectionFactory) ctx.lookup("java:comp/DefaultJMSConnectionFactory");
    topic = (Topic)ctx.lookup("jms/MyTopic");

    alt description here

    The code looks for the JMS resources (connection factory and topic) by using JNDI.

  8. Add the following lines of code to the main method:

    try (JMSContext context = connectionFactory.createContext();) {
                JMSConsumer consumer = context.createSharedConsumer(topic, "SubName");
                //JMSConsumer consumer=context.createSharedDurableConsumer(topic, "MakeItLast");
                System.out.println("Waiting for messages on topic");
                TextListener listener = new TextListener();
                consumer.setMessageListener(listener);
                System.out.println(
                        "To end program, enter Q or q, " + "then <return>");
                InputStreamReader inputStreamReader = new InputStreamReader(System.in);
                char answer = '\0';
                while (!((answer == 'q') || (answer == 'Q'))) {
                    try {
                        answer = (char) inputStreamReader.read();
                    } catch (IOException e) {
                        System.err.println("I/O exception: " + e.toString());
                    }
                }
                System.out.println("Text messages received: " + listener.getCount());
            } catch (JMSRuntimeException e) {
                System.err.println("Exception occurred: " + e.toString());
                System.exit(1);
            }
            System.exit(0);
        }
    }

       
    alt description here

    The code does the following:

    1. Injects resources for a connection factory and topic.
    2. In a try-with-resources block, creates a JMSContext.
    3. Creates a consumer on a shared nondurable subscription, specifying a subscription name:
      JMSConsumer consumer=context.createSharedConsumer(topic, "SubName");
    4. Creates an instance of the TextListener class and registers it as the message listener for the shared consumer.
    5. Listens for the messages published to the destination and stops when the user enters q or Q.
    6. Catches and handles any exceptions. The end of the try-with-resources block automatically causes JMSContext to be closed.

Testing the Shared Nondurable Subscriptions

In this section, you test the shared nondurable subscriptions of the JMS/MyTopic topic as follows:

  1. Run two instances of the JMS shared nondurable subscription, SharedConsumer.java, in the JMS2SharedConsumer project.
  2. Run the JMS message producer, Producer.java, in the JMS2Producer project.
  3. Enter Q or q and then press Enter to stop each client and see a report of the number of text messages received.

You observe that only one client receives the message because it is a shared subscription.

  1. Perform the following steps to run SharedConsumer.java:

    1. Expand the JMS2SharedConsumer project.
    2. Expand Source Packages > com.example.
    3. Right-click SharedConsumer.java and select Run File.
      alt description here

      In the Output console, one instance of JMS2SharedConsumer is running.

      alt description here
    4. Right-click SharedConsumer.java and select Run File to execute another instance of JMS2SharedConsumer.

      alt description here
      In the Output console, the second instance of JMS2SharedConsumer is running.
      alt description here
  2. Perform the following steps to run the JMS message producer.

    1. Expand the JMS2Producer project.
    2. Expand Source Packages > com.example.
    3. Right-click Producer.java and select Run File.
      alt description here

      In the Output console, one instance of JMS2Producer is running.

      alt description here
  3. Perform the following steps in the Output console:

    1. Click JMS2SharedConsumer(run).

      The message sent from the producer has been consumed.

      alt description here
    2. Enter q or Q to quit.

      The number of received messages is displayed.

      alt description here
  4. Perform the following steps in the Output console:

    1. Click the JMS2SharedConsumer(run)#2 tab.

      You can see it is still waiting for messages.

      alt description here
    2. Enter q or Q to quit.

      The number of messages received is 0.

      alt description here

Developing a Shared Durable Subscription

In this section, you develop a shared durable subscriber, which uses a  topic, jms/MyTopic, to receive messages by using asynchronous message delivery. It uses the TextListener message listener. The shared durable subscriber is implemented by modifying SharedConsumer.java.

This section demonstrates how shared durable subscriptions combine the advantages of durable subscriptions (the subscription remains active when the client is not) with those of shared consumers (the message load can be divided among multiple clients).

  1. Perform the following steps to modify SharedConsumer.java:

    1. On the Projects tab, expand the JMS2SharedConsumer project.
    2. Expand Source Packages > com.example.
    3. Double-click SharedConsumer.java to open it in the editor.
      alt description here
    4. Perform the following steps to modify the main() method:

      1. Comment the following line of code:
        JMSConsumer consumer = context.createSharedConsumer(topic, "SubName");

      2. Add the following line of code:
        JMSConsumer consumer=context.createSharedDurableConsumer(topic, "MakeItLast");

        alt description here

      The code does the following:

      1. Injects resources for a connection factory and topic.
      2. In a try-with-resources block, creates a JMSContext.
      3. Creates a consumer on a shared durable subscription, specifying a subscription name:
        JMSConsumer consumer=context.createSharedDurableConsumer(topic, "MakeItLast");
      4. Creates an instance of the TextListener class and registers it as the message listener for the shared consumer.
      5. Listens for the messages published to the destination and stops when the user enters q or Q.
      6. Catches and handles any exceptions. The end of the try-with-resources block automatically causes the JMSContext to be closed.

Testing the Shared Durable Subscriptions

In this section, you test the shared durable subscriptions of the JMS/MyTopic topic as follows:

  1. Run the JMS message producer, Producer.java, in the JMS2Producer project.
  2. Run two instances of the JMS shared nondurable subscription, SharedConsumer.java, in the JMS2SharedConsumer project.
  3. Enter Q or q and press Enter to stop each client and see a report of the number of received text messages.

The subscription remains active and accumulates messages even when no active consumer is on it. Whichever client starts first receives all messages that were sent when there was no active subscriber.

  1. Perform the following steps to run Producer.java:

    1. Expand the JMS2Producer project.
    2. Expand Source Packages -> com.example.
    3. Right-click Producer.java and select Run File.
      alt description here

      In the Output console, one instance of the JMS2Producer is running, and a message was sent to the JMS/MyTopic topic.

      alt description here
  2. Perform the following steps to run SharedConsumer.java:

    1. Expand the JMS2SharedConsumer project.
    2. Expand Source Packages > com.example.
    3. Right-click SharedConsumer.java and select Run File.
    4. alt description here

      In the Output console, one instance of JMS2SharedConsumer is running and is waiting for messages about the jms/MyTopic topic.

      alt description here
    5. Right-click SharedConsumer.java and select Run File to execute another instance of JMS2SharedConsumer.

      alt description here
      In the Output console, the second instance of SharedConsumer is running and is waiting for messages about the jms/MyTopic topic.
      alt description here
  3. Perform the following steps in the Output console:

    1. Click the JMS2SharedConsumer(run) tab and enter q or Q to end the program.

      The number of received messages is displayed.

      alt description here
    2. Click the JMS2SharedConsumer(run)#2 tab and enter q or Q to end the program.

      The number of messages received is displayed.

      alt description here

Summary

In this tutorial, you learned to:

  • Use the JMS 2.0 simplified API in a Java SE application
  • Understand the publish/subscribe messaging model
  • Implement publish/subscribe by using a topic
  • Implement a JMS 2.0 shared subscription model
  • Configure JMS resources in GlassFish Server
  • Develop a shared consumer
  • Develop a shared durable consumer

Resources

Credits

  • Lead Curriculum Developer: Anjana Shenoy
  • Editor: Susan Moxley
  • QA: Anjulaponni Azhagulekshmi Subbiahpillai

To navigate this Oracle by Example tutorial, note the following:

Topic List:
Click a topic to navigate to that section.
Expand All Topics:
Click the button to show or hide the details for the sections. By default, all topics are collapsed.
Hide All Images:
Click the button to show or hide the screenshots. By default, all images are displayed.
Print:
Click the button to print the content. The content that is currently displayed or hidden is printed.

To navigate to a particular section in this tutorial, select the topic from the list.