Develop a J2EE application to use JMS

Use this task to develop a J2EE application to use the JMS API directly for asynchronous messaging.

 

Overview

This topic gives an overview of the steps needed to develop a J2EE application (servlet or enterprise bean) to use the JMS API directly for asynchronous messaging.

This topic only describes the JMS-related considerations; it does not describe general J2EE application programming, which you should already be familiar with. For detailed information about these steps, and for examples of developing a J2EE application to use JMS, see the Java Message Service Documentation

Details of JMS resources that are used by J2EE applications are defined to WAS and bound into the JNDI namespace by the WebSphere administrative support.

To use JMS, any method of a J2EE application completes the following general steps:

 

Procedure

  1. Import JMS packages. A J2EE application that uses JMS starts with a number of import statements for JMS, which should include at least the following

    import javax.jms.*;       //JMS interfaces 
    import javax.naming.*;    //Used for JNDI lookup of administered objects
    

  2. Get an initial context.

       try     {
            ctx = new InitialContext(env);
    ...
    

  3. Retrieve administered objects from the JNDI namespace. The InitialContext.lookup() method is used to retrieve administered objects (a JMS connection factory and JMS destinations); for example, to receive a message from a queue
           qcf = (QueueConnectionFactory)ctx.lookup( qcfName );
    ...
           inQueue = (Queue)ctx.lookup( qnameIn );
    ...
    
    

An alternative, but less manageable, approach to obtaining administratively-defined JMS destination objects by JNDI lookup is to use the Session.createQueue(String) method or Session.createTopic(String) method. For example

Queue q = mySession.createQueue("Q1");
creates a JMS Queue instance that can be used to reference the existing destination Q1.

In its simplest form, the parameter to these create methods is the name of an existing destination. For more complex situations, applications can use a URI-based format, which allows an arbitrary number of name value pairs to be supplied to set various properties of the JMS destination object.

  • Create a connection to the messaging service provider. The connection provides access to the underlying transport, and is used to create sessions. The createQueueConnection() method on the factory object is used to create the connection

           connection = qcf.createQueueConnection();
    
    

    The JMS specification defines that connections should be created in the stopped state. Until the connection starts, MessageConsumers that are associated with the connection cannot receive any messages. To start the connection, issue the following command

           connection.start();
    

  • Create a session, for sending or receiving messages. The session provides a context for producing and consuming messages, including the methods used to create MessageProducers and MessageConsumers. The createQueueSession method is used on the connection to obtain a session. The method takes two parameters:

    • A boolean that determines whether or not the session is transacted.

    • A parameter that determines the acknowledge mode.

           boolean transacted = false;
           session = connection.createQueueSession( transacted,
                                                   Session.AUTO_ACKNOWLEDGE);
    
    

    In this example, the session is not transacted, and it should automatically acknowledge received messages. With these settings, a message is backed out only after a system error or if the application terminates unexpectedly.

    The following points, as defined in the EJB specification, apply to these flags:

    • The transacted flag passed on createQueueSession is ignored inside a global transaction and all work is performed as part of the transaction. Outside of a transaction the transacted flag is used and, if set to true, the application should use session.commit() and session.rollback() to control the completion of the work. In an EJB2.0 module, if the transacted flag is set to true and outside of an XA transaction, then the session is involved in the WebSphere local transaction and the unresolved action attribute of the method applies to the JMS work if it is not committed or rolled back by the application.

    • Clients cannot use Message.acknowledge() to acknowledge messages. If a value of CLIENT_ACKNOWLEDGE is passed on the createxxxSession call, then messages are automatically acknowledged by the application server and Message.acknowledge() is not used.

  • Send a message.

    1. Create MessageProducers to create messages. For point-to-point messaging the MessageProducer is a QueueSender that is created by passing an output queue object (retrieved earlier) into the createSender method on the session. A QueueSender is normally created for a specific queue, so that all messages sent using that sender are sent to the same destination

             QueueSender queueSender = session.createSender(inQueue);
      
      

    2. Create the message. Use the session to create an empty message and add the data passed.

      JMS provides several message types, each of which embodies some knowledge of its content. To avoid referencing the vendor-specific class names for the message types, methods are provided on the Session object for message creation.

      In this example, a text message is created from the outString property

             TextMessage outMessage = session.createTextMessage(outString);
      
      

    3. Send the message.

      To send the message, the message is passed to the send method on the QueueSender

             queueSender.send(outMessage);
      
      

  • Receive replies.

    1. Create a correlation ID to link the message sent with any replies. In this example, the client receives reply messages that are related to the message that it has sent, by using a provider-specific message ID in a JMSCorrelationID

             messageID = outMessage.getJMSMessageID();
      

      The correlation ID is then used in a message selector, to select only messages that have that ID

             String selector = "JMSCorrelationID = '"+messageID+"'";
      

    2. Create a MessageReceiver to receive messages. For point-to-point the MessageReceiver is a QueueReceiver that is created by passing an input queue object (retrieved earlier) and the message selector into the createReceiver method on the session

             QueueReceiver queueReceiver = session.createReceiver(outQueue, selector);
      
      

    3. Retrieve the reply message. To retrieve a reply message, the receive method on the QueueReceiver is used

             Message inMessage = queueReceiver.receive(2000);
      

      The parameter in the receive call is a timeout in milliseconds. This parameter defines how long the method should wait if there is no message available immediately. If you omit this parameter, the call blocks indefinitely. If you do not want any delay, use the receiveNoWait()method. In this example, the receive call returns when the message arrives, or after 2000ms, whichever is sooner.

    4. Act on the message received. When a message is received, one can act on it as needed by the business logic of the client. Some general JMS actions are to check that the message is of the correct type and extract the content of the message. To extract the content from the body of the message, it is necessary to cast from the generic Message class (which is the declared return type of the receive methods) to the more specific subclass, such as TextMessage. It is good practice always to test the message class before casting, so that unexpected errors can be handled gracefully.

      In this example, the instanceof operator is used to check that the message received is of the TextMessage type. The message content is then extracted by casting to the TextMessage subclass.

             if ( inMessage instanceof TextMessage )
      
      ...
                 String replyString = ((TextMessage) inMessage).getText();
      
      

  • Closing down. If the application needs to create many short-lived JMS objects at the Session level or lower, it is important to close all the JMS resources used. To do this, you call the close() method on the various classes (QueueConnection, QueueSession, QueueSender, and QueueReceiver) when the resources are no longer required

           queueReceiver.close();
    ...
           queueSender.close();
    ...
           session.close();
           session = null;
    ...
           connection.close();
           connection = null;
    
    

  • Publishing and subscribing to messages. To use JMS Publish/Subscribe support instead of point-to-point messaging, the general actions are the same; for example, to create a session and connection. The exceptions are that topic resources are used instead of queue resources (such as TopicPublisher instead of QueueSender), as shown in the following example to publish a message

    // Creating a TopicPublisher
           TopicPublisher pub = session.createPublisher(topic);
    ...
           pub.publish(outMessage);
    ...
           // Closing TopicPublisher
           pub.close();
    
    

  • Handling errors Any JMS runtime errors are reported by exceptions. The majority of methods in JMS throw JMSExceptions to indicate errors. It is good programming practice to catch these exceptions and display them on a suitable output.

    Unlike normal Java exceptions, a JMSException can contain another exception embedded in it. The implementation of JMSException does not include the embedded exception in the output of its toString()method. Therefore, we need to check explicitly for an embedded exception and print it out, as shown in the following example:

        catch (JMSException je)
        {
           System.out.println("JMS failed with "+je);
           Exception le = je.getLinkedException();
           if (le != null)
           {
               System.out.println("linked exception "+le);
           }
        }
    
    

     

    What to do next

    After you have packaged your application, one can next deploy the application into WebSphere Application Server, as described in Deploying a J2EE application to use JMS.


     

    Related Tasks


    Developing a JMS client
    Developing an enterprise application to use message-driven beans

     

    See Also