The Java EE 5 Tutorial

A Simple Example of Synchronous Message Receives

This section describes the sending and receiving programs in an example that uses the receive method to consume messages synchronously. This section then explains how to compile, package, and run the programs using the Application Server.

The following sections describe the steps in creating and running the example:

Writing the Client Programs for the Synchronous Receive Example

    The sending program, producer/src/java/Producer.java, performs the following steps:

  1. Injects resources for a connection factory, queue, and topic:

    @Resource(mappedName="jms/ConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(mappedName="jms/Queue")private static Queue queue;
    @Resource(mappedName="jms/Topic")private static Topic topic;
  2. Retrieves and verifies command-line arguments that specify the destination type and the number of arguments:

    final int NUM_MSGS;
    String destType = args[0];
    System.out.println("Destination type is " + destType);
    if ( ! ( destType.equals("queue") || destType.equals("topic") ) ) { 
        System.err.println("Argument must be \”queue\” or " + "\”topic\”");
        System.exit(1);
    }
    if (args.length == 2){ 
        NUM_MSGS = (new Integer(args[1])).intValue();
    } 
    else { 
        NUM_MSGS = 1;
    }
  3. Assigns either the queue or topic to a destination object, based on the specified destination type:

    Destination dest = null;
    try { 
        if (destType.equals("queue")) { 
            dest = (Destination) queue; 
        } else { 
            dest = (Destination) topic; 
        }
    } 
    catch (Exception e) {
        System.err.println("Error setting destination: " + e.toString()); 
        e.printStackTrace(); 
        System.exit(1);
    }
  4. Creates a Connection and a Session:

    Connection connection = connectionFactory.createConnection(); 
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  5. Creates a MessageProducer and a TextMessage:

    MessageProducer producer = session.createProducer(dest);
    TextMessage message = session.createTextMessage();
  6. Sends one or more messages to the destination:

    for (int i = 0; i < NUM_MSGS; i++) { 
        message.setText("This is message " + (i + 1)); 
        System.out.println("Sending message: " + message.getText()); 
        producer.send(message);
    }
  7. Sends an empty control message to indicate the end of the message stream:

    producer.send(session.createMessage());

    Sending an empty message of no specified type is a convenient way to indicate to the consumer that the final message has arrived.

  8. Closes the connection in a finally block, automatically closing the session and MessageProducer:

    } finally { 
        if (connection != null) { 
            try { connection.close(); } 
            catch (JMSException e) { } 
        }
    }

    The receiving program, synchconsumer/src/java/SynchConsumer.java, performs the following steps:

  1. Injects resources for a connection factory, queue, and topic.

  2. Assigns either the queue or topic to a destination object, based on the specified destination type.

  3. Creates a Connection and a Session.

  4. Creates a MessageConsumer:

    consumer = session.createConsumer(dest);
  5. Starts the connection, causing message delivery to begin:

    connection.start();
  6. Receives the messages sent to the destination until the end-of-message-stream control message is received:

    while (true) {
        Message m = consumer.receive(1); 
        if (m != null) { 
            if (m instanceof TextMessage) { 
                message = (TextMessage) m; 
                System.out.println("Reading message: " + message.getText()); 
            } else { 
                break; 
            } 
        }
    }

    Because the control message is not a TextMessage, the receiving program terminates the while loop and stops receiving messages after the control message arrives.

  7. Closes the connection in a finally block, automatically closing the session and MessageConsumer.

The receive method can be used in several ways to perform a synchronous receive. If you specify no arguments or an argument of 0, the method blocks indefinitely until a message arrives:

Message m = consumer.receive();
Message m = consumer.receive(0);

For a simple client program, this may not matter. But if you do not want your program to consume system resources unnecessarily, use a timed synchronous receive. Do one of the following:

The SynchConsumer program uses an indefinite while loop to receive messages, calling receive with a timeout argument. Calling receiveNoWait would have the same effect.

Starting the JMS Provider

When you use the Application Server, your JMS provider is the Application Server. Start the server as described in Starting and Stopping the Application Server.

Creating JMS Administered Objects for the Synchronous Receive Example

Creating the JMS administered objects for this section involves the following:

If you built and ran the SimpleMessage example in Chapter 23, A Message-Driven Bean Example and did not delete the resources afterward, you need to create only the topic resource.

    You can create these objects using the Ant tool. To create all the resources, do the following:

  1. In a terminal window, go to the producer directory:


    cd producer
    
  2. To create all the resources, type the following command:


    ant create-resources
    

    To create only the topic resource, type the following command:


    ant create-topic
    

These Ant targets use the asadmin create-jms-resource command to create the connection factory and the destination resources.

To verify that the resources have been created, use the following command:


asadmin list-jms-resources

The output looks like this:


jms/Queue
jms/Topic
jms/ConnectionFactory
Command list-jms-resources executed successfully.

Compiling and Packaging the Clients for the Synchronous Receive Example

The simplest way to run these examples using the Application Server is to package each one in an application client JAR file. The application client JAR file requires a manifest file, located in the src/conf directory for each example, along with the .class file.

The build.xml file for each example contains Ant targets that compile and package the example. The targets place the .class file for the example in the build/jar directory. Then the targets use the jar command to package the class file and the manifest file in an application client JAR file.

    To compile and package the Producer and SynchConsumer examples using NetBeans IDE, follow these steps:

  1. In NetBeans IDE, choose Open Project from the File menu.

  2. In the Open Project dialog, navigate to tut-install/javaeetutorial5/examples/jms/simple/.

  3. Select the producer folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. Right-click the project and choose Build.

  7. In NetBeans IDE, choose Open Project from the File menu.

  8. In the Open Project dialog, navigate to tut-install/javaeetutorial5/examples/jms/simple/.

  9. Select the synchconsumer folder.

  10. Select the Open as Main Project check box.

  11. Click Open Project.

  12. Right-click the project and choose Build.

    To compile and package the Producer and SynchConsumer examples using Ant, follow these steps:

  1. In a terminal window, go to the producer directory:


    cd producer
    
  2. Type the following command:


    ant
    
  3. In a terminal window, go to the synchconsumer directory:


    cd ../synchconsumer
    
  4. Type the following command:


    ant
    

The targets place the application client JAR file in the dist directory for each example.

Running the Clients for the Synchronous Receive Example

    To run the sample programs using NetBeans IDE, follow these steps.

  1. Run the Producer example:

    1. Right-click the producer project and choose Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue 3
      
    4. Click OK.

    5. Right-click the project and choose Run.

      The output of the program looks like this:


      Destination type is queue
      Sending message: This is message 1
      Sending message: This is message 2
      Sending message: This is message 3

      The messages are now in the queue, waiting to be received.

  2. Now run the SynchConsumer example:

    1. Right-click the synchconsumer project and choose Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and choose Run.

      The output of the program looks like this:


      Destination type is queue
      Reading message: This is message 1
      Reading message: This is message 2
      Reading message: This is message 3
  3. Now try running the programs in the opposite order. Right-click the synchconsumer project and choose Run.

    The Output pane displays the destination type and then appears to hang, waiting for messages.

  4. Right-click the producer project and choose Run.

    The Output pane shows the output of both programs, in two different tabs.

  5. Now run the Producer example using a topic instead of a queue.

    1. Right-click the producer project and choose Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic 3
      
    4. Click OK.

    5. Right-click the project and choose Run.

      The output of the program looks like this:


      Destination type is topic
      Sending message: This is message 1
      Sending message: This is message 2
      Sending message: This is message 3
  6. Now run the SynchConsumer example using the topic.

    1. Right-click the synchconsumer project and choose Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic
      
    4. Click OK.

    5. Right-click the project and choose Run.

      The result, however, is different. Because you are using a topic, messages that were sent before you started the consumer cannot be received. (See Publish/Subscribe Messaging Domain, for details.) Instead of receiving the messages, the program appears to hang.

  7. Run the Producer example again. Right-click the producer project and choose Run.

    Now the SynchConsumer example receives the messages:


    Destination type is topic
    Reading message: This is message 1
    Reading message: This is message 2
    Reading message: This is message 3

You can also run the sample programs using the appclient command. Each of the programs takes one or more command-line arguments: a destination type and, for Producer, a number of messages.

    To run the clients using the appclient command, follow these steps:

  1. In a terminal window, go to the producer/dist directory:


    cd ../producer/dist
    
  2. Run the Producer program, sending three messages to the queue:


    appclient -client producer.jar queue 3
    

    The output of the program looks like this:


    Destination type is queue
    Sending message: This is message 1
    Sending message: This is message 2
    Sending message: This is message 3

    The messages are now in the queue, waiting to be received.

  3. In the same window, go to the synchconsumer/dist directory:


    cd ../../synchconsumer/dist
    
  4. Run the SynchConsumer program, specifying the queue:


    appclient -client synchconsumer.jar queue
    

    The output of the program looks like this:


    Destination type is queue
    Reading message: This is message 1
    Reading message: This is message 2
    Reading message: This is message 3
  5. Now try running the programs in the opposite order. Run the SynchConsumer program. It displays the destination type and then appears to hang, waiting for messages.


    appclient -client synchconsumer.jar queue
    
  6. In a different terminal window, run the Producer program.


    cd tut-install/javaeetutorial5/examples/jms/simple/producer/dist
    appclient -client producer.jar queue 3
    

    When the messages have been sent, the SynchConsumer program receives them and exits.

  7. Now run the Producer program using a topic instead of a queue:


    appclient -client producer.jar topic 3
    

    The output of the program looks like this:


    Destination type is topic
    Sending message: This is message 1
    Sending message: This is message 2
    Sending message: This is message 3
  8. Now run the SynchConsumer program using the topic:


    appclient -client synchconsumer.jar topic
    

    The result, however, is different. Because you are using a topic, messages that were sent before you started the consumer cannot be received. (See Publish/Subscribe Messaging Domain, for details.) Instead of receiving the messages, the program appears to hang.

  9. Run the Producer program again. Now the SynchConsumer program receives the messages:


    Destination type is topic
    Reading message: This is message 1
    Reading message: This is message 2
    Reading message: This is message 3

Because the examples use the common interfaces, you can run them using either a queue or a topic.