Download PDF Home History |  | API Search Feedback FAQ

> ## Writing Simple JMS Client Applications

> This section shows how to create, package, and run simple JMS client programs packaged as stand-alone application clients. These clients access a J2EE server. The clients demonstrate the basic tasks that a JMS application must perform:

> - Creating a connection and a session
> - Creating message producers and consumers
> - Sending and receiving messages

> In a J2EE application, some of these tasks are performed, in whole or in part, by the container. If you learn about these tasks, you will have a good basis for understanding how a JMS application works on the J2EE platform.

> This section covers the following topics:

> - An example that uses synchronous message receives
> - An example that uses a message listener
> - Running JMS clients on multiple systems

> Each example uses two programs: one that sends messages and one that receives them. You can run the programs in two terminal windows.

> When you write a JMS application to run in a J2EE application, you use many of the same methods in much the same sequence as you do for a stand-alone application client. However, there are some significant differences. Using the JMS API in a J2EE Application describes these differences, and Chapter 34 provides examples that illustrate them.

> The examples for this section are in the following directory:

>     <INSTALL>/j2eetutorial14/examples/jms/simple/

> ### 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
> - Compiling the Clients
> - Starting the JMS Provider
> - Creating JMS Administered Objects
> - Packaging the Clients
> - Running the Clients

> #### Writing the Client Programs

> The sending program, `src/SimpleProducer.java`, performs the following steps:

> 1. Retrieves command-line arguments that specify the destination name and the number of arguments:

> `final int NUM_MSGS;
> String destName = new String(args[0]);
> System.out.println("Destination name is " + destName);
> if (args.length == 2){
> NUM_MSGS = (new Integer(args[1])).intValue();
> } else {
> NUM_MSGS = 1;
> }`

> 2. Performs a JNDI lookup of the `ConnectionFactory` and `Destination`:

> `/*
> * Create a JNDI API InitialContext object if none exists
> * yet.
> */
> Context jndiContext = null;
> try {
> jndiContext = new InitialContext();
> } catch (NamingException e) {
> System.out.println("Could not create JNDI API " +
> "context: " + e.toString());
> System.exit(1);
> }
> /*
> * Look up connection factory and destination. If either
> * does not exist, exit. If you look up a
> * TopicConnectionFactory instead of a
> * QueueConnectionFactory, program behavior is the same.
> */
> ConnectionFactory connectionFactory = null;
> Destination dest = null;
> try {
> connectionFactory = (ConnectionFactory)
> jndiContext.lookup("jms/QueueConnectionFactory");
> dest = (Destination) jndiContext.lookup(destName);
> }
> } catch (Exception e) {
> System.out.println("JNDI API lookup failed: " +
> e.toString());
> System.exit(1);
> }`

> 3. Creates a `Connection` and a `Session`:

> `Connection connection =
> connectionFactory.createConnection();
> Session session = connection.createSession(false,
> Session.AUTO_ACKNOWLEDGE);`

> 4. Creates a `MessageProducer` and a `TextMessage`:

> `MessageProducer producer =
> session.createProducer(dest);
> TextMessage message = session.createTextMessage();`

> 5. 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);
> }`

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

> 7. 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, `src/SimpleSynchConsumer.java`, performs the following steps:

> 1. Performs a JNDI lookup of the `ConnectionFactory` and `Destination`.
> 2. Creates a `Connection` and a `Session`.
> 3. Creates a `MessageConsumer`:

> `consumer = session.createConsumer(dest);`

> 4. Starts the connection, causing message delivery to begin:

> `connection.start();`

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

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

> - Call the `receive` method with a timeout argument greater than `0`:

> `Message m = consumer.receive(1); // 1 millisecond`

> - Call the `receiveNoWait` method, which receives a message only if one is available:

> `Message m = consumer.receiveNoWait();`

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

> #### Compiling the Clients

> You can compile the examples using the `asant` tool, as described in Building the Examples.

> To compile the examples, do the following:

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

> `<``INSTALL``>/j2eetutorial14/examples/jms/simple/`

> 2. Type the following command:

> `asant build`

> This command uses the `build.xml` file in the `simple` directory to compile all the source files in the directory. The class files are placed in the `build` directory.

> #### 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

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

> - Starting the Admin Console
> - Creating two connection factories
> - Creating two physical destinations
> - Creating two destination resources

> If you built and ran the `SimpleMessage` example in Chapter 28 and did not delete the resources afterward, you need to create only half of these resources: those that involve topics.

> To start the Admin Console, follow the instructions in Starting the Admin Console.

> To create the connection factories, perform the following steps:

> 1. In the tree component, expand the Resources node, then expand the JMS Resources node.
> 2. Select the Connection Factories node.
> 3. On the JMS Connection Factories page, click New. The Create JMS Connection Factory page appears.
> 4. In the JNDI Name field, type `jms/QueueConnectionFactory`.
> 5. Choose`javax.jms.QueueConnectionFactory` from the Type combo box.
> 6. Select the Enabled checkbox. The Admin Console appears as shown in Figure 33-6.
> 7. Click OK to save the connection factory.
> 8. Click New again.
> 9. In the JNDI Name field, type `jms/TopicConnectionFactory`.
> 10. Choose`javax.jms.TopicConnectionFactory` from the Type combo box.
> 11. Select the Enabled checkbox.
> 12. Click OK.

> Figure 33-6 Creating a JMS Connection Factory

> To create the physical destinations, perform the following steps:

> 1. In the tree component, expand the Resources node, then expand the JMS Resources node.
> 2. Select the Physical Destinations node.
> 3. On the Physical Destinations page, click New. The Create Physical Destination page appears.
> 4. In the Physical Destination Name field, type `PhysicalQueue`.
> 5. Choose `queue` from the Type combo box.
> 6. Click OK.
> 7. Click New again.
> 8. In the Physical Destination Name field, type `PhysicalTopic`.
> 9. Choose `topic` from the Type combo box.
> 10. Click OK.

> To create the destination resources and link them to the physical destinations, perform the following steps:

> 1. In the tree component, expand the Resources node, then expand the JMS Resources node.
> 2. Select the Destination Resources node.
> 3. On the JMS Destination Resources page, click New. The Create JMS Destination Resource page appears.
> 4. In the JNDI Name field, type `jms/Queue`.
> 5. Choose `javax.jms.Queue` from the Type combo box.
> 6. Select the Enabled checkbox.
> 7. In the Additional Properties area, type `PhysicalQueue` in the Value field for the `Name` property.
> 8. Click OK.
> 9. Click New again.
> 10. In the JNDI Name field, type `jms/Topic`.
> 11. Choose `javax.jms.Topic` from the Type combo box.
> 12. Select the Enabled checkbox.
> 13. In the Additional Properties area, type `PhysicalTopic` in the Value field for the `Name` property. The Admin Console appears as shown in Figure 33-7.
> 14. Click OK to save the resource.

> Figure 33-7 Creating a JMS Destination Resource

> #### Packaging the Clients

> The simplest way to run these examples using the Application Server is to package each one in an application client JAR file.

> First, start `deploytool`:

> - On Windows systems, choose StartProgramsSun MicrosystemsApplication ServerDeploytool.
> - On UNIX systems, use the `deploytool` command.

> Package the `SimpleProducer` example as follows:

> 1. Choose FileNewApplication Client to start the Application Client wizard.
> 2. In the JAR File Contents screen, select the radio button labeled Create New Stand-Alone AppClient Module.
> 3. Click Browse next to the AppClient Location field and navigate to the `<``INSTALL``>/j2eetutorial14/examples/jms/simple/` directory.
> 4. Type `SimpleProducer` in the File Name field, and click Create Module File.
> 5. Verify that `SimpleProducer` appears in the AppClient Name field.
> 6. Click the Edit button next to the Contents text area.
> 7. In the dialog box, locate the `build` directory. Select `SimpleProducer.class` from the Available Files tree. Click Add and then OK.
> 8. In the General screen, select `SimpleProducer` in the Main Class combo box.
> 9. Click Next.
> 10. Click Finish.

> Package the `SimpleSynchConsumer` example in the same way, except for the values listed in Table 33-3.

> Table 33-3 Application Values for SimpleSynchConsumer
> Wizard Field or Area | Value
> --- | ---
> File Name | `SimpleSynchConsumer.jar`
> AppClient Name | `SimpleSynchConsumer`
> Available Files class | `build/SimpleSynchConsumer.class`
> Main Class | `SimpleSynchConsumer`

> #### Running the Clients

> You run the sample programs using the `appclient` command. Each of the programs takes command-line arguments: a destination name, a destination type, and, for `SimpleProducer`, a number of messages.

> Run the clients as follows.

> 1. Run the `SimpleProducer` program, sending three messages to the queue `jms/Queue`:

> `appclient -client SimpleProducer.jar jms/Queue 3`

> The output of the program looks like this:

> `Destination name is jms/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. In the same window, run the `SimpleSynchConsumer` program, specifying the queue name:

> `appclient -client SimpleSynchConsumer.jar jms/Queue`

> The output of the program looks like this:

> `Destination name is jms/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. Run the `SimpleSynchConsumer` program. It displays the queue name and then appears to hang, waiting for messages.
> 4. In a different terminal window, run the `SimpleProducer` program. When the messages have been sent, the `SimpleSynchConsumer` program receives them and exits.
> 5. Now run the `SimpleProducer` program using a topic instead of a queue:

> `appclient -client SimpleProducer.jar jms/Topic 3`

> The output of the program looks like this:

> `Destination name is jms/Topic
> Sending message: This is message 1
> Sending message: This is message 2
> Sending message: This is message 3`

> 6. Now run the `SimpleSynchConsumer` program using the topic:

> `appclient -client SimpleSynchConsumer.jar jms/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.

> 7. Run the `SimpleProducer` program again in another terminal window. Now the `SimpleSynchConsumer` program receives the messages:

> `Destination name is jms/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.

> ### A Simple Example of Asynchronous Message Consumption

> This section describes the receiving programs in an example that uses a message listener to consume messages asynchronously. This section then explains how to compile and run the programs using the Application Server.

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

> - Writing the Client Programs
> - Compiling the Clients
> - Starting the JMS Provider
> - Packaging the SimpleAsynchConsumer Client
> - Running the Clients

> #### Writing the Client Programs

> The sending program is `src/SimpleProducer.java`, the same program used in the example in A Simple Example of Synchronous Message Receives. You may, however, want to comment out the following line of code, where the producer sends a nontext control message to indicate the end of the messages:

>     producer.send(session.createMessage());

> An asynchronous consumer normally runs indefinitely. This one runs until the user types the letter `q` or `Q` to stop the program, so it does not use the nontext control message.

> The receiving program, `src/SimpleAsynchConsumer.java`, performs the following steps:

> 1. Performs a JNDI lookup of the `ConnectionFactory` and `Destination`.
> 2. Creates a `Connection` and a `Session`.
> 3. Creates a `MessageConsumer`.
> 4. Creates an instance of the `TextListener` class and registers it as the message listener for the `MessageConsumer`:

> `listener = new TextListener();
> consumer.setMessageListener(listener);`

> 5. Starts the connection, causing message delivery to begin.
> 6. Listens for the messages published to the destination, stopping when the user types the character `q` or `Q`:

> `System.out.println("To end program, type Q or q, " +
> "then <return>");
> inputStreamReader = new InputStreamReader(System.in);
> while (!((answer == 'q') || (answer == 'Q'))) {
> try {
> answer = (char) inputStreamReader.read();
> } catch (IOException e) {
> System.out.println("I/O exception: "
> + e.toString());
> }
> }`

> 7. Closes the connection, which automatically closes the session and `MessageConsumer`.

> The message listener, `src/TextListener.java`, follows these steps:

> 1. When a message arrives, the `onMessage` method is called automatically.
> 2. The `onMessage` method converts the incoming message to a `TextMessage` and displays its content. If the message is not a text message, it reports this fact:

> `public void onMessage(Message message) {
> TextMessage msg = null;
> try {
> if (message instanceof TextMessage) {
> msg = (TextMessage) message;
> System.out.println("Reading message: " +
> msg.getText());
> } else {
> System.out.println("Message is not a " +
> "TextMessage");
> }
> } catch (JMSException e) {
> System.out.println("JMSException in onMessage(): " +
> e.toString());
> } catch (Throwable t) {
> System.out.println("Exception in onMessage():" +
> t.getMessage());
> }
> }`

> #### Compiling the Clients

> Compile the programs if you did not do so before or if you edited `SimpleProducer.java` as described in Writing the Client Programs:

>     asant build

> #### Starting the JMS Provider

> If you did not do so before, start the Application Server.

> You will use the connection factories and destinations you created in Creating JMS Administered Objects.

> #### Packaging the SimpleAsynchConsumer Client

> If you did not do so before, start `deploytool`.

> If you did not package the `SimpleProducer` example, follow the instructions in Packaging the Clients to do so. Package the `SimpleAsynchConsumer` example in the same way as `SimpleProducer`, except for the values listed in Table 33-4.

> Table 33-4 Application Values for SimpleAsynchConsumer
> Wizard Field or Area | Value
> --- | ---
> File Name | `SimpleAsynchConsumer.jar`
> AppClient Name | `SimpleAsynchConsumer`
> Available Files classes | `build/SimpleAsynchConsumer.class build/TextListener.class`
> Main Class | `SimpleAsynchConsumer`

> #### Running the Clients

> As before, you run the sample programs using the `appclient` command.

> Run the clients as follows.

> 1. Run the `SimpleAsynchConsumer` program, specifying the topic `jms/Topic` and its type.

> `appclient -client SimpleAsynchConsumer.jar jms/Topic`

> The program displays the following lines and appears to hang:

> `Destination name is jms/Topic
> To end program, type Q or q, then <return>`

> 2. In another terminal window, run the `SimpleProducer` program, sending three messages. The commands look like this:

> `appclient -client SimpleProducer.jar jms/Topic 3`

> The output of the program looks like this:

> `Destination name is jms/Topic
> Sending message: This is message 1
> Sending message: This is message 2
> Sending message: This is message 3`

> In the other window, the `SimpleAsynchConsumer` program displays the following:

> `Destination name is jms/Topic
> To end program, type Q or q, then <return>
> Reading message: This is message 1
> Reading message: This is message 2
> Reading message: This is message 3`

> If you did not edit `SimpleProducer.java`, the following line also appears:

> `Message is not a TextMessage`

> 3. Type `Q` or `q` to stop the program.
> 4. Now run the programs using a queue. In this case, as with the synchronous example, you can run the `SimpleProducer` program first, because there is no timing dependency between the sender and receiver:

> `appclient -client SimpleProducer.jar jms/Queue 3`

> The output of the program looks like this:

> `Destination name is jms/Queue
> Sending message: This is message 1
> Sending message: This is message 2
> Sending message: This is message 3`

> 5. Run the `SimpleAsynchConsumer` program:

> `appclient -client SimpleAsynchConsumer.jar jms/Queue`

> The output of the program looks like this:

> `Destination name is jms/Queue
> To end program, type Q or q, then <return>
> Reading message: This is message 1
> Reading message: This is message 2
> Reading message: This is message 3`

> 6. Type `Q` or `q` to stop the program.

> ### Running JMS Client Programs on Multiple Systems

> JMS client programs using the Application Server can exchange messages with each other when they are running on different systems in a network. The systems must be visible to each other by name--the UNIX host name or the Microsoft Windows computer name--and must both be running the Application Server. You do not have to install the tutorial examples on both systems; you can use the examples installed on one system if you can access its file system from the other system.

> Note: Any mechanism for exchanging messages between systems is specific to the J2EE server implementation. This tutorial describes how to use the Application Server for this purpose.

> Suppose that you want to run the `SimpleProducer` program on one system, `earth`, and the `SimpleSynchConsumer` program on another system, `jupiter`. Before you can do so, you need to perform these tasks:

> - Create two new connection factories
> - Edit the source code
> - Recompile the source code and update the client JAR files

> Note: A limitation in the JMS provider in the Application Server may cause a runtime failure to create a connection to systems that use the Dynamic Host Configuration Protocol (DHCP) to obtain an IP address. You can, however, create a connection from a system that uses DHCP to a system that does not use DHCP. In the examples in this tutorial, `earth` can be a system that uses DHCP, and `jupiter` can be a system that does not use DHCP.

> Before you begin, start the server on both systems:

> 1. Start the Application Server on `earth` and log in to the Admin Console.
> 2. Start the Application Server on `jupiter` and log in to the Admin Console.

> #### Creating Administered Objects for Multiple Systems

> To run these programs, you must do the following:

> - Create a new connection factory on both `earth` and `jupiter`
> - Create a destination resource and physical destination on both `earth` and `jupiter`

> Create a new connection factory on `jupiter` as follows:

> 1. In the Admin Console, expand the Resources node, then expand the JMS Resources node.
> 2. Select the Connection Factories node.
> 3. On the JMS Connection Factories page, click New. The Create JMS Connection Factory page appears.
> 4. In the JNDI Name field, type `jms/JupiterQueueConnectionFactory`.
> 5. Choose`javax.jms.QueueConnectionFactory` from the Type combo box.
> 6. Select the Enabled checkbox.
> 7. Click OK.

> Create a new connection factory with the same name on `earth` as follows:

> 1. In the Admin Console, expand the Resources node, then expand the JMS Resources node.
> 2. Select the Connection Factories node.
> 3. On the JMS Connection Factories page, click New. The Create JMS Connection Factory page appears.
> 4. In the JNDI Name field, type `jms/JupiterQueueConnectionFactory`.
> 5. Choose`javax.jms.QueueConnectionFactory` from the Type combo box.
> 6. Select the Enabled checkbox.
> 7. In the Additional Properties area, find the `AddressList` property. In the Value field, type the name of the remote system (whatever the real name of `jupiter` is). If the JMS service on the remote system uses a port number other than the default (7676), specify the port number also, using the syntax `system_name``:``port-number`.
> 8. Click OK.

> If you have already been working on either `earth` or `jupiter`, you have the queue on one system. On the system that does not have the queue, perform the following steps:

> 1. Use the Admin Console to create a physical destination named `PhysicalQueue`, just as you did in Creating JMS Administered Objects.
> 2. Use the Admin Console to create a destination resource named `jms/Queue` and set its `Name` property to the value `PhysicalQueue`.

> When you run the programs, they will work as shown in Figure 33-8. The program run on `earth` needs the queue on `earth` only in order that the JNDI lookup will succeed. The connection, session, and message producer are all created on `jupiter` using the connection factory that points to `jupiter`. The messages sent from `earth` will be received on `jupiter`.

> Figure 33-8 Sending Messages from One System to Another

> #### Running the Programs

> These steps assume that you have the tutorial installed on only one of the two systems you are using.

> To edit, update, and run the programs, perform the following steps on the system where you first ran them:

> 1. In both `SimpleProducer.java` and `SimpleSynchConsumer.java`, change the line that looks up the connection factory so that it refers to the new connection factory:

> `connectionFactory = (ConnectionFactory)
> jndiContext.lookup("jms/JupiterQueueConnectionFactory");`

> 2. Recompile the programs:

> `asant build`

> 3. In `deploytool`, choose ToolsUpdate Module Files to add the recompiled source files to the `SimpleProducer.jar` and `SimpleSynchConsumer.jar` files.
> 4. Save the changed JAR files.
> 5. Run `SimpleProducer` on `earth`:

> `appclient -client SimpleProducer.jar jms/Queue 3`

> 6. Run `SimpleSynchConsumer` on `jupiter`:

> `appclient -client SimpleSynchConsumer.jar jms/Queue`

> Because both connection factories have the same name, you can run either the producer or the consumer on either system.

> For examples showing how to deploy J2EE applications on two different systems, see An Application Example That Consumes Messages from a Remote J2EE Server and An Application Example That Deploys a Message-Driven Bean on Two J2EE Servers.

> #### Deleting the Connection Factory and Stopping the Server

> You will need the connection factory `jms/JupiterQueueConnectionFactory` in Chapter 34. However, if you wish to delete it, perform the following steps in the Admin Console:

> 1. Expand the Java Message Service node and click Connection Factories.
> 2. Select the checkbox next to `jms/JupiterQueueConnectionFactory` and click Delete.

> Remember to delete the connection factory on both systems.

> You can also use the Admin Console to delete the destinations and connection factories you created in Creating JMS Administered Objects. However, we recommend that you keep them, because they will be used in most of the examples in Chapter 34. After you have created them, they will be available whenever you restart the Application Server.

> Delete the class files for the programs as follows:

>     asant clean

> If you wish, you can manually delete the client JAR files.

> You can also stop the Application Server, but you will need it to run the sample programs in the next section.

Download PDF Home History |  | API Search Feedback FAQ

All of the material in The J2EE(TM) 1.4 Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.