Skip to main content

IBM WebSphere Developer Technical Journal: Developing Java Message Service Applications with WebSphere Studio Application Developer, Integration Edition, Version 4.1

Paula McMillan (paulamcm@ca.ibm.com), WebSphere Training and Technical Enablement, IBM Toronto Lab
Paula McMillan is a member of WebSphere Training and Technical Enablement specializing in developing advanced-level training for the WebSphere family of products. Under her previous name, Paula Lumby, she is co-author of Computer Science textbooks, including Java Programming: Advanced Topics, published by Thomson Learning. You can reach Paula at paulamcm@ca.ibm.com.
Sandy Minocha (minocha@ca.ibm.com), Staff Software Engineer, Middleware Tooling, IBM Toronto Lab
Sandy Minocha is a software developer at the IBM Toronto Lab. He is currently working on IBM's WebSphere Studio Adapter Toolkit, the next-generation of Enterprise Access Builder tools. You can reach Sandy at minocha@ca.ibm.com.

Summary:  This article discusss how to develop, test, and deploy JMS applications using current versions of WebSphere products, including WebSphere Studio Application Developer, Integration Edition 4.1, which provides support for Extended Messaging Support.

Date:  18 Apr 2002
Level:  Introductory
Activity:  862 views

Introduction

Distributed applications can use message-oriented middleware (MOM) to asynchronously send and receive messages between components of an enterprise application. MOM systems give great flexibility due to the asynchronous nature of messaging; they do not conform to the request-response pattern of almost all other communications interfaces.

MOM systems let the sender, or message producer, pass a message to the messaging middleware and then forget about it. The MOM system guarantees delivery. The receiver, or message consumer, has no control over the time or sequence of message delivery. Rather than poll or wait for incoming messages, the consumer is notified when a message arrives. Much of this article deals with designing a consumer so that it can always handle messages that arrive asynchronously.

The Java™ 2 platform Enterprise Edition (J2EE) defines a standard API and architecture, the Java Message Service (JMS), for Java programs to interface with MOM systems. J2EE Version 1.3 introduces a new kind of Enterprise JavaBean™ (EJB), the Message Driven Bean (MDB), which is specifically designed to accept asynchronous incoming messages. The strategic approach for developing new and converting existing applications that use messaging is to adopt these J2EE standards.

This article discusses how to develop, test, and deploy JMS applications using current versions of WebSphere® products. It cannot cover all aspects of this large field, so the article focuses on solutions for asynchronously consuming messages and configuring the test environment.

You can use these products in the WebSphere product family to develop and run applications that use messaging.

  • WebSphere MQ, Version 5.2.1 is the IBM messaging software. WebSphere MQ was formerly called MQSeries® and has a large user base. Many applications are moving from proprietary WebSphere MQ API to JMS.
  • WebSphere Application Server, Version 4.0.2 supports the JMS API in both Advanced Edition (AE) and Advanced Single Server Edition (AEs). With Extended Messaging Support (EMS) of Enterprise Extensions v4.0 (EEX), you can delegate the task of accepting messages that arrive asynchronously to the application server. EEX can be added to AE and AEs. Version 5 of Application Server will support Message Driven Beans (MDBs).
  • WebSphere Studio Application Developer, Version 4.0.2 provides an environment for developing and testing Java applications. The WebSphere test environment included in Application Developer is AEs Version 4.0.2.
  • WebSphere Studio Application Developer, Integration Edition, Version 4.1 supports EMS. The WebSphere test environment included in Application Developer IE is AEs Version 4.0.2 plus some of the EEX features.

The JMS API

JMS defines two modes of messaging: point-to-point (P2P) and publish-subscribe (pub/sub). In point-to-point mode, a message producer sends a message to a specific message consumer through a queue. There is a one-to-one relationship between the P2P producer and consumer. Point-to-point maps most directly onto traditional use of messaging and is2 therefore the more likely migration path of existing WebSphere MQ applications. All the samples in this article use P2P messaging.

Publish-subscribe is new to traditional WebSphere MQ users, and offers great potential. In pub/sub, any number of message producers can send messages to topics rather than queues. Any number of consumers can subscribe to topics and receive the messages. Therefore, pub/sub allows for broadcast messages and an m-to-n relationship between producers and consumers. This article's discussion of techniques for creating listeners applies equally to topic subscribers in pub/sub mode.

For a good introduction to the JMS API, see:

J2EE mandates that applications refer to JMS destinations (queues or topics) and connection factories by logical names mapped to objects and classes in a JNDI namespace. WebSphere MQ Java support includes the JMSAdmin utility, a Java application for defining WebSphere MQ-administered objects such as queues and topics, and binding them to a JNDI namespace. The JNDI context may be provided by an application server such as WebSphere Application Server, an LDAP server, or the native file system.


Installation and verification

Before running the samples, you should test your WebSphere MQ installation and connect to it from Application Developer IE. Install the following products on Windows® 2000 service pack 1 or Windows NT® service pack 6a or higher.

  • WebSphere MQ, Version 5.1 or later. For pub/sub, you need WebSphere MQ 5.2 or later.
  • WebSphere MQ product extension MA88. Product extension MAOC is also required by pub/sub, but not used by the samples in this article.
  • Application Developer IE Version 4.1. Note that the installation verification program and first JMS sample in this article can be run on Application Developer Version 4.0.2 or later.

Tips on WebSphere MQ installation:

  • Be sure to set up a default configuration. The samples in this article use the default queue manager and some default system queues.
  • Select a custom installation for WebSphere MQ and MA88 so that you can specify the installation directory. Otherwise, you may find that WebSphere MQ installs to C:\Program Files\MQSeries and MA88 installs to C:\Program Files\IBM\MQSeries\Java.
  • Make sure the ..\MQSeries\Java\lib directory is on your PATH environment variable so that the system can find DLLs located there.

After you install Application Developer IE, set up a global Java classpath variable.

  1. Select Windows => Preferences from the main toolbar. Then, expand Java and select Classpath Variables to the left of the Preferences window. Click New on the right.
  2. Name the new variable MQ_JAVA_INSTALL_PATH and set its value to the absolute path where MA88 is installed. Usually the path ends with ..\MQSeries\Java.
  3. Click OK twice to save the variable.

Further instructions in this article assume that you are familiar with using Application Developer.


Running the IVT verification program

Before running any program that uses administered objects not yet bound to a JNDI namespace, run JMSAdmin. Usually, instructions for running JMSAdmin describe launching it from a batch file in a command line window. Running JMSAdmin from inside Application Developer IE simplifies setting up the Java environment, especially the classpath.

A JAR file containing the following classes is included in the download file below.

Class Description
IVTRunNoJndiRuns the MQJMSIVT test program without using JNDI.
IVTRunFSJndiRuns the MQJMSIVT test program using the file system JNDI to look up administered objects.
IVTRunWASJndiRuns the MQJMSIVT test program using the WebSphere test environment JNDI namespace to look up administered objects.
JMSAdminFSRuns the JMSAdmin utility with a configuration file, JMSAdminFS.config, that specifies the file system JNDI context.
JMSAdminWASRuns the JMSAdmin utility with a configuration file, JMSAdminWAS.config, that specifies the WebSphere JNDI context.

These and some additional files are contained in the package verification. If you look at the code of these classes, you can see that they call the reflection API to load the MQJMSIVT class, specify command line arguments, and then run the main method.

These instructions use the file system JNDI context, which means that binding information is written to a Windows directory specified in the JMSAdmin configuration file. Therefore, you do not need to start the WebSphere test environment to verify WebSphere MQ connectivity to the JMS API.

  1. Download and unzip the samples file below. Be sure to preserve folder names when you unzip.
  2. Create a directory D:\temp, if you do not already have one. The JMSAdminFS.config file that is provided sets D:\temp as the directory in which to store JNDI bindings.
  3. Open Application Developer IE and go to the Java perspective. Create a Java project to hold the classes in this sample. You can call the project JMSsamples.
  4. Set the Java build path for your project.

    1. From the project Properties sheet, select Java Build Path, click the Libraries tab, and select Add Variable.
    2. Use defined classpath variables to add:
      MQ_JAVA_INSTALL_PATH\lib\com.ibm.mq.jar 
      MQ_JAVA_INSTALL_PATH\lib\com.ibm.mqjms.jar 
      MQ_JAVA_INSTALL_PATH\lib\fscontext.jar 
      MQ_JAVA_INSTALL_PATH\lib\providerutil.jar 
      WAS_PLUGINDIR\lib\j2ee.jar 
      WAS_PLUGINDIR\lib\websphere.jar

  5. Import the file verification.jar into your project to add the verification package to your project. If any classes have errors, check your build path.
  6. Run the class JMSAdminFS as a Java application. In the Console, see the prompt InitCtx>.
  7. Enter JMSAdmin commands. JMSAdmin commands and keywords are not case-sensitive but names (enclosed in parentheses) are.
    define qcf(ivtQCF) 
    define q(ivtQ) queue(SYSTEM.DEFAULT.LOCAL.QUEUE) 
    display ctx


    Check that objects ivtQCF and ivtQ are listed in the display of the JNDI context
    end


    The keyword qcf defines a com.ibm.mq.jms.MQQueueConnectionFactory object. Keyword queue, shortened to q, defines a com.ibm.mq.jms.MQQueue object. See the WebSphere MQ product InfoCenter (Start => Programs => IBM MQSeries => Information Center) for a complete list of the JMSAdmin keywords and commands.
  8. Run IVTRunFSJndi as a Java application. Any exception or any output other than the following indicates a problem with your installation or setup.
    5648-C60 (c) Copyright IBM Corp. 1999. All Rights Reserved. 
    MQSeries classes for Java(tm) Message Service 5.200 
    Installation Verification Test 
    ... lines omitted 
    Sending the message to SYSTEM.DEFAULT.LOCAL.QUEUE  
    Reading the message back again 
    ... lines omitted 
    Reply string equals original string 
    ... lines omitted 
    IVT completed OK 
    IVT finished


The sample scenario

The examples in this article are based on a single scenario, coded in stages that implement different design patterns for asynchronous receipt of messages. The first stage demonstrates the JMS API. The second uses Application Server and the WebSphere test environment, and the third exploits the EMS features of Application Developer IE. The final stage considers the transition to Message Driven Beans.

In generic terms, the scenario looks like Figure 1 below. The business application is called Count and a Count client communicates with it through queued messages. The message bodies are text. The business task is to count the characters in a message. The Count application then puts a response message into a different queue. This is a request-response scenario from the client side. This simple client sends a request message and then waits on the reply queue until either a response message becomes available or the receive operation times-out. In contrast, the business side should always be ready to consume a message from a client, and its activities start with an asynchronous receive.


Figure 1. The generic scenario
Graphic of the generic scenario

On the Count client:

  • A message producer sends to Q1.
  • A message consumer receives the reply from Q2.

On the Count application:

  • A listener receives asynchronous messages from Q1.
  • Something must start the listener and keep it running
  • A message consumer receives the message from the listener and passes it to the application.
  • The Count logic performs the business task.
  • A message producer puts response on Q2.

To run the scenario, start the CountApplication class before CountClient. Otherwise, the client times out leaving the response message on the queue. You can always clear the queues manually from MQ Explorer. For demonstration purposes, the Count application consumes one message, produces one reply, and then stops.


Stage 1. Coding your own listener

The Count client calls the QueueSender.send and QueueReceiver.receive methods to set and get messages from the queues. More interesting is the Count application because it must be activated by the arrival of a message. The API includes a MessageListener interface. A first stage solution is to code your implementation of this interface, to produce a design that looks like Figure 2 below. When you have a listener, your program is driven by the external event of a message arrival. The JVM automatically calls your listener method and passes it messages, one at a time, as they arrive.


Figure 2. Coding a listener
Graphic depicting coding a listener

On the count client:

  • A message producer sends to Q1.
  • A message consumer receives the reply from Q2.

On the Count application:

  • The JVM moves messages from Q1 to the listener.
  • The Count main method starts the listener.
  • A method consumes messages passed to it by the listener and gives them to the logic method.
  • A logic method counts characters.
  • A method sends a response to Q2.

A program may have several listeners. Each inbound queue requires a separate listener, and multiple listeners for one queue may be using different message selectors. Each listener becomes an entry point into the application. To code a listener:

  • Create a class that implements the interface javax.jms.MessageListener. You must provide one method with signature: public void onMessage(Message message).
  • Register this class with a QueueReceiver object by passing it as an argument of the createQueueReceiver method.
  • Devise a way to keep the listener active as long as the program should be ready to consume messages.

There are several options for implementing MessageListener including creating a separate class and defining an anonymous inner class in the argument of createQueueReceiver. The Count application is so small and simple that its one main class can implement the message listener, as shown in this code snippet.

public class CountApplication implements MessageListener { 
public void onMessage(Message message) { // pass text messages only on to the application try { // the cast throws an exception is the message is not a Text Message consumeMessage((TextMessage) message); } catch (ClassCastException e) { System.out.println("Got message of type " + message.getClass()); e.printStackTrace(System.err); } } }

In the Count application, the consumeMessage method logs receipt of the message and passes the body of the message to the doCountLogic method. Method doCountLogic determines the length of the string and then calls the produceResponseMessage method to produce the response.

The trickiest part of this solution is managing the lifespan of the listener. A common technique is to launch a thread for every listener in your program. But you still must generate some activity to keep the listener threads alive. The Count application handles this problem in the method startListener as shown in the next code excerpt. The Count application needs only one thread, but it exploits a technique that is common in multithreaded applications; it keeps threads in suspended animation until called upon by an external event.

public class CountApplication implements MessageListener { 
// define a condition to stop listening private boolean messageReceived = false; public static void main(String [] args) { // ... CountApplication app = new CountApplication(); app.startListener(); } void startListener() { try { // start the connection, create the session // ... qReceiver = qSession.createReceiver(requestQueue); qReceiver.setMessageListener(this); // loop to keep listener alive while (! messageReceived ) { try { // pause for a second to share the CPU Thread.currentThread().sleep(1000); } catch (InterruptedException ie) {} } } catch (JMSException e ) { e.printStackTrace(System.err); } }

Running the first count sample

Before you run this sample, define the queues in WebSphere MQ. Open a command line window and issue the following commands to define the queues. If you are familiar with MQ Explorer, you can create the queues there instead.

WebSphere MQ command Description
startmqsc Start the default queue manager if it is not already running.
runmqsc Start a command sequence (expect no prompts).
define qlocal(Q1)
put(enabled)
get(enabled)
Create the queue for requests (enter on one line).
define qlocal(Q2)
put(enabled)
get(enabled
Create the queue for responses (enter on one line).
display qlocal(Q*) List queues starting with Q to verify.
end stop runmqsc.

The source code for this stage of the Count Sample is provided in a JAR file in the download ZIP file below.

  1. Open Application Developer to the Java perspective.
  2. Import Stage1.jar into a Java project. You can add it to the JMSsamples project and see a new package samples.count.stage1.
  3. If you created a new project, set up the project build path exactly as you did to run the verification program.
  4. Run JMSAdminFS as before to bind more objects to the JNDI namespace. At the InitCtx prompt, enter:
    define queue(JMSQ1) queue(Q1) 
    define queue(JMSQ2) queue(Q2) 
    define QCF(JMSQCF) 
    display ctx 
    end

  5. Run the CountApplication class as a Java application. The expected output is:
    getting jndi context 
    Retrieving JMS objects from JNDI 
    Listening to: queue:///Q1 
    Unable to load message catalog - mqji


    Ignore the spurious message about the message catalog.
  6. Run the CountClient class as a Java application. The expected output is:
    getting jndi context 
    Retrieving JMS objects from JNDI 
    sending message <Hello World!> to queue:///Q1 
    Unable to load message catalog - mqji 
    message sent 
    Getting message from queue:///Q2 
    Message <Hello World!: Contains 12 characters> received

  7. Now if you look again at the output for Count application, you will see new lines of output:
    Message <Hello World!> received 
    sending message <Hello World!: Contains 12 characters> to queue:///Q2 
    message sent

  8. By running client and application an unequal number of times, you can build up unconsumed messages in the queues. You can use MQ Explorer to browse messages currently in the queues and clear the queues.

Not included in this sample are exception listeners. An exception listener is called when a problem prevents delivery of a message, a situation that cannot be detected by a MessageListener. To create an exception listener, create a class that implements javax.jms.ExceptionListener, provide an onException method, and register the exception listener by calling setExceptionListener on the queue connection object.

At this stage, you have a working JMS application. The client side still conforms to the request-replay pattern of usage. Provided that no user is waiting for the reply to appear on a UI, the client could be rewritten to delegate receiving the response to a listener. This is a significant redesign, but it makes the complete application more robust and much less sensitive to timing. The application side is first an asynchronous consumer and a producer in the send-and-forget pattern.

The WebSphere test environment is not required to run this sample. However, the next stage is to investigate whether the application server can provide a solution to the problem of managing the lifespan of a listener.


Stage 2. Using WebSphere Application Server to run your listener

So far, the Count client and application are both Java applications that run from their main methods. More realistically, the application should run on a server, perhaps with a Web interface for the user.

This stage considers redesigning the Count application so that it can be deployed to Application Server. The logical approach is to enclose the JMS API in beans that consume messages and produce messages. The business logic can remain in the CountApplication class. Whether these beans become EJBs or simple Java beans depends upon the requirements of your application. To wrap a listener in an Application Server-supported transaction, you must use EMS, as described in Stage 3. For the Count application, Java beans are sufficient. In this scenario, the Count logic has little to do but count the characters in a string passed to a doCountLogic method and call the JMS producer bean to put the response on the reply queue.

All applications that you install on Application Developer must be deployed as J2EE enterprise applications with EJB modules deployed to EJB JARs and Web modules deployed to WAR files. The JMS and Count logic Java beans can be included in the Web module. What starts the listener? One suggestion is to add a JMS message listener servlet to perform this task. The result is shown in Figure 3 below.


Figure 3. Using Application Server to run the listener
Graphic depicting using Application Server to run the listener

Elements of the Count application:

The count client is as before. It is very loosely coupled with the Count application because all communication is through queues. The servlet is loaded and initialized when the Web application that contains it is loaded. The servlet contains code to create and then start a listener thread. The count application becomes a Web application consisting of:

  • A JMS message producer and consumer beans
  • A count logic bean

The init method of the servlet starts the listener, with code similar to:

public class JMSMessageListenerServlet 
   extends javax.servlet.http.HttpServlet { 
     
   // class MessageListenerThread implements MessageListener and Runnable 
   private MessageListenerThread messageListener;  
     
   public void init() throws javax.servlet.ServletException { 
      // ... 
      messageListener = new MessageListenerThread( /*...*/); 
      Thread thread = new Thread(messageListener); 
      thread.start(); 
      // ... 
   } 
}

Before developing such a solution, check Section 4.6.3.4 of the WebSphere InfoCenter for restrictions on calling some of the JMS API from a user application installed on Application Server. You may be able to create a similar effect by developing a custom service. Section 4.10 in the WebSphere InfoCenter explains that it is possible to create a custom service configuration for an application server so that the custom service class is initialized and started when the application server is started.

Because this solution requires an application server, it makes sense to use the Application Server JNDI context for binding and lookup. Unfortunately, JMSAdmin does not update the AEs configuration file and the bindings that JMSAdmin defines are lost each time the WebSphere test environment stops. Rather than run JMSAdmin each time you start the server, you can continue to run JMSAdmin using the file system. On the other hand, the file system JNDI service has trouble resolving names qualified by subcontexts. For example, name "Sample/JMS/Q1" refers to object Q1 in subcontext JMS of subcontext Sample. Looking up "Sample/JMS/Q1" works in Application Server, but you may have to flatten the name, for example to "JMSQ1," for the file system.

This listings shows how to retrieve a queue from the file system and Application Server JNDI contexts:

Using File System JNDI context

try { 
   // set up a JNDI File system context 
   Hashtable p = new Hashtable(); 
   p.put(Context.PROVIDER_URL, "file://d|/temp"); 
   p.put(Context.INITIAL_CONTEXT_FACTORY, 
"com.sun.jndi.fscontext.RefFSContextFactory"); 
   Context ctx = new InitialContext(p); 
   // Obtain the connection factory from JNDI 
   queue = (Queue)ctx.lookup("Sample/JMS/Q1"); 
} catch (NamingExceptin ne) { /* ... */ }

Using Application Server JNDI context

try { 
   // set up a WebSphere Application Server JNDI context 
   // defaults to  
   //  URL = "iiop://localhost:900" 
   //  INITIAL_CONTEXT_FACTORY = com.ibm. 
   //   websphere.naming.WsnInitialContextFactory 
   Context ctx = new InitialContext( ); 
   // Obtain the connection factory from JNDI 
   queue = (Queue)ctx.lookup("JMSQ1"); 
} catch (NamingExceptin NE) { /* ... */ }

A sample program for demonstrating this solution is not provided. The reason is that the EMS provides a service that not only performs the function of the initializer servlet, but also does so in a more robust way, and provides a migration path to the MDB. The initializer servlet is proposed as an interim solution if you do not have EEX or Application Developer IE. It requires no EJBs, and may be appropriate for applications that do not need to access the MOM system in a Java transactional context, or for development teams who are not yet skilled in developing EJBs.

If you develop a solution following this pattern, then build your program as a Web module in Application Developer or Application Developer IE. To deploy, export the enterprise application containing your Web module to an EAR file and install this EAR on Application Server AEs or AE as you would any other EAR. Except for running JMSAdmin with the Application Server JNDI service, there are no JMS-related considerations on deployment or installation.


Stage 3. Using the EMS listener

The next stage in improving the Count application requires Application Developer IE for development and Application Server AEs or AE with EEX for production. The EMS features include a listener that is initiated and managed by Application Server. When the EMS listener replaces the servlet of Stage 2, the application design looks like Figure 4 below.


Figure 4. Using an EMS listener
Graphic depicting using an EMS listener

Elements of the Count application:

An EMS listener receives messages and passes them to a session EJB called the message bean. The message bean replaces the message consumer. The count logic is a session EJB. The response producer could be another session EJB. For simplicity, the sample implements as a method in the logic EJB, so there are two EJBs:

  • The message bean
  • The Count bean

The listener is provided by Application Server according to descriptors you provide in a configuration file. It passes messages to a message bean that you write, so the message bean becomes the entry point into your code.

To create listeners for an application:

  1. The easiest approach is to start with a sample configuration XML file and edit it in your choice of flat text editor. Include a stanza, delimited by <listener> ... </listener> tags for each listener. For a detailed description of this file, see the "JMS Listener" in the Application Server EEX InfoCenter.
  2. Store the configuration file on the file system. In the Application Server administration console, you provide the name and absolute path to this file when you configure EMS.

The configuration file for the sample Count application defines only one listener and is shown below. The HomeJNDIName tag gives the JNDI name of the message bean. The connection factory and destination (source of messages) are also specified by JNDI names. These names are bound to the Application Server JNDI namespace when you configure EMS.

<Config> 
<!--Pooling set MQ connection pooling options--> 
<Pooling> 
   <!--milliseconds 
   <Timeout>100636</Timeout> 
   <Threshold>36</Threshold> 
</Pooling> 
<Listener> 
   <HomeJNDIName>samples/jms/CountMessageHome</HomeJNDIName> 
   <JMSConnectionFactory>jms/Sample/QCF</JMSConnectionFactory> 
   <JMSDestination>jms/Sample/Q1</JMSDestination> 
   <MaxRetries>6</MaxRetries> 
   <MaxSessions>1</MaxSessions> 
</Listener> 
</Config>

To create a message bean:

  1. Write a session EJB according to the guidelines listed in the Application Server EEX InfoCenter. The main criteria is that the message bean be a valid session bean and have a method with prototype:
    public void onMessage( javax.jms.Message)


    The onMessage method should perform only the task of receiving the message, and pass the message on to a business logic EJB.
  2. Do not create a client or write any code that accesses this message bean because the only client of this class should be the listener.

The Count message bean looks like:

public class CountMessageBean implements javax.ejb.SessionBean { 
 
// usual EJB methods create(), getSessionContext(), ... 
 
   public void onMessage(javax.jms.Message msg) { 
      System.out.println("onMessage()"); 
      try { 
         String text = ((TextMessage) MSG).getText(); 
         System.out.println("MSG received: " + text); 
//call the business logic 
         Context ctx = new InitialContext(); 
         Object objref = ctx.lookup("java:comp/env/Count"); 
         CountHome home = (CountHome) PortableRemoteObject.narrow( 
            objref,CountHome.class); 
         Count bean = home.create(); 
         bean.doCountLogic(text); 
      } catch (JMSException je) { 
         // handle exception 
      } 
   }  
}

The Count logic bean is an ordinary session EJB. To reduce the size of the sample, the message response producer is coded as a method in the same EJB. The response message producer looks up the connection factory and reply queue in the JNDI namespace. Global or local JNDI names are allowed, as shown in the code excerpt below. The lookup uses local names, and deployment descriptors ejb-jar.xml and ibm-ejb-jar-bnd.xmi define the mapping onto global names.

Context ic = new InitialContext(); 
// local names 
QueueConnectionFactory qcf = (QueueConnectionFactory) ic.lookup( 
   "Java:comp/env/QCF"); 
Queue ioQueue = (Queue) ic.lookup("Java:comp/env/Q2"); 
// global names 
// QueueConnectionFactory qcf = (QueueConnectionFactory) ic.lookup( 
//   "jms/Sample/QCF"); 
// Queue ioQueue = (Queue) ic.lookup("jms/Sample/Q2");

Queue Q1 is not mentioned in the message bean or any EJB deployment descriptor because it is used only by the listener and the name mapping is managed by WebSphere Application Server.

Listeners and namespaces

Names for JMS connection factories and destinations may appear in the EMS namespace, the application EJB local namespace, Application Server global namespace, and an external namespace such as the file system.

Figure 5 below shows the names in the Count application. The count client is included because it uses the same JNDI server as the count application. However, in a real world scenario, the originator of the message received by the listener can be any process that puts messages on queue.

Setting the names so that they map onto the namespaces may be the most error-prone aspect of configuring the EMS listener. The file system namespace has shown some unexpected behavior, including the problem of resolving names in subcontexts. Some trial and error may be required.


Figure 5. JNDI bindings for EMS
Graphic depicting JNDI bindings for EMS

At this stage, using the file system namespace may seem unnecessary. You should eliminate it when you deploy to AE. The catch for the WebSphere test environment is that JMSAdmin bindings must be defined when Application Server starts. Because JMSAdmin bindings are not persistent in AEs, you need to store these binding in an external namespace. Fortunately, when Application Server accesses the file system namespace for EMS, it resolves subcontexts properly and you no longer have to flatten JNDI names to remove subcontexts. Create the JNDI context with the default constructor. The JNDI lookup goes first to the Application Server namespace and then may be redirected to the external namespace specified when you configured the JMS provider.

Running the message bean sample

The CountApp.ear file that accompanies this article contains the deployed count listener and count application bean. The listener configuration file, jmsconfig.xml, and a client JAR file, Stage3.jar, are also in the download ZIP file.

The class samples.count.stage3.CountClient is not an EJB client application, but is a client of WebSphere MQ. It differs from the stage 1 client because it uses the Application Server JNDI namespace. If you ran the stage 1 sample, you can also test the listener with samples.count.stage1.CountClient. You can even use MQ Explorer to put the message on queue Q1 and see the response appear in queue Q2.

If you did not run the Stage 1 solution, create the queues in WebSphere MQ as described in Running the first count sample. You need to define additional objects in JNDI because this sample uses JNDI subcontexts.

  1. Open Application Developer IE to the Java perspective.
  2. Run JMSAdminFS from the JMSsamples project. At the InitCtx prompt, enter:
    define ctx(Sample/JMS) 
    chg ctx(Sample/JMS) 
    define queue(Q1) queue(Q1) 
    define queue(Q2) queue(Q2) 
    define QCF(QCF) 
    display ctx 
    end

  3. Open the J2EE perspective and import CountApp.ear. You can name the Enterprise Application project Stage 3. The import gives you an EJB project CountAppEJB containing two EJBs CountMessage and Count.
  4. Open the Java perspective and import the Stage3.jar into your JMSsamples project. See a new package samples.count.stage3.
  5. Open the Server perspective. Create a new server instance and configuration of the WebSphere test environment. You can call the server Stage3 and put it in a server project called TestServers.
  6. Add the project Stage3 to the Stage3 server configuration.
  7. Double-click on Sever Configuration Stage3 to edit its configuration in the editor view. On the General tab, Make sure the Enable Administrative Client check box is selected. Save, and close the editor.
  8. Start the server.

To configure EMS, you must use the thin administrative client, just as you would with a stand-alone installation of AEs. By default, the administrative application listens on port 9090. To modify a configuration, specify the server configuration file when you call the administrative client. This file, server-cfg.xml, is located in your Application Developer IE workspace in the server project's server configuration folder.

  1. Open the Web browser in the Server perspective. Go to the following URL, where <WSADIE> is the directory in which Application Developer IE is installed. If there are any spaces in the <WSADIE> path, substitute %20 for them, as in Program%20Files.

    http://localhost:9090/admin/edit?configFile=
    <WSADIE>workspace/TestServers/Stage3.wsc/server-cfg.xml


    Provide any user ID when prompted.
  2. Expand Resources on the left of the administrative client, and click JMS Providers.
  3. Click New. Select resource provider type IBM MQSeries (Local WebSphere Naming Context). Click Next.
  4. Fill in the form and click OK when done.
    Field Value Comment
    Server Class Path <MQJAVA>\lib\com.ibm.mq.jar;
    <MQJAVA>\lib\com.ibm.mqjms.jar;
    <MQJAVA>\lib\fscontext.jar;
    <MQJAVA>\lib\providerutil.jar;

    Enter on one line.

    <MQJAVA> is the value of your MQ_JAVA_INSTALL PATH variable.

    If the classpath exceeds 256 characters, copy the JAR files to a directory to shorten it.

    Name IBM MQSeries (External File System Naming Context)
    External Initial Context Factory com.sun.jndi.fscontext.RefFSContextFactory
    External Provider URL file://<directory ><dir> is the directory used in JMSAdmin.config, for example, file://D:/temp
  5. Under JMS Providers, select IBM MQSeries, JMS Connection Factory, and click New.
  6. Fill in the form and click OK when done.
    Field Value
    Name QCF
    JNDI Name jms/Sample/QCF
    External JNDI Path Sample\JMS\QCF
    Connection Type queue
  7. Similarly, create two JMS destinations.
    Field Value
    Name Q1
    JNDI Name jms/Sample/Q1
    External JNDI Path Sample\JMS\Q1
    Connection Type queue
    Field Value
    Name Q2
    JNDI Name jms/Sample/Q2
    External JNDI Path Sample\JMS\Q2
    Connection Type queue
  8. Configure the Extended Message Service as a custom service. First, extract jmsconfig.xml from the ZIP file and note where it resides.
  9. Expand the tree:

    Stage3
    Nodes
    Localhost
    Application Servers
    Default Server
    Select Custom Services and click New.

  10. Fill in the form and click OK when done:
    Field Value
    Display Name Extended Message Service
    Enable selected
    Classname com.ibm.cmm.listener.JMSListenerStub
    External Configuration URL <Absolute Path>/jmsconfig.xml
  11. Save your server configuration to <WSADIE> workspace/TestServers/Stage3.wsc/server-cfg.xml.
  12. Close the browser view.

Now you are ready to test the listener.

  1. Stop the Stage 3 server and restart it. Expect lines similar to the following in the Console:
    *** Starting the server *** 
    ... 
    ************* End Display Current Environment ************* 
    xxx Server U Version : 4.0.2 
    xxx Server U Edition: Advanced Single Server Edition 
       for Multiplatforms 
    ... 
    xxx ResourceBinde I WSVR0049I: Binding QCF as jms/Sample/QCF 
    xxx ResourceBinde I WSVR0049I: Binding Q1 as jms/Sample/Q1 
    xxx ResourceBinde I WSVR0049I: Binding Q2 as jms/Sample/Q2 
    xxx Server I Setting JNDI name for JMS in resRef binding 
       as local:jms/jms/Sample/QCF 
    xxx Server I Setting JNDI name for JMS in resRef binding 
       as local:jms/jms/Sample/Q2 
    xxx EJBEngine I WSVR0037I: Starting EJB jar: CountAppEJB 
    ... 
    xxx JMSListenerSt I WMSG0042I: MDB Listener 
       samples/jms/CountMessageHome started successfully  
       for JMSDestination jms/Sample/Q1 
    xxx JMSListenerSt A WMSG0001I: JMS Listener started successfully 
    xxx Server A WSVR0023I: Server Default Server open for e-business

  2. Go to the Java perspective and run the samples.count.stage3.CountClient application in the JMSsamples project. Expect the output:
    Retrieving JMS objects from JNDI 
    sending message <Hello World!> to queue:///Q1 
    Unable to load message catalog - mqji 
    message sent 
    Getting message from queue:///Q2 
    Message <Hello World!: Contains 12 characters> received

  3. In the Debug perspective, you can select the Server launcher process to see the trace output from the Count application. It should include lines:
    onMessage() 
    MSG received: Hello World!  
    Putting message: Hello World!: Contains 12 characters on queue:///Q2

Installing the Message Bean Sample on WebSphere Application Server

You can install the sample on AE or AEs upon which EEX has been installed. Briefly, the process is as follows:

  1. Define the queues in WebSphere MQ.
  2. Run JMSAdmin to bind the queues and connection factory to a JNDI namespace. Use the Application Server JNDI server AE or the file system for AEs. You can run the JMSAdminWAS or JMSAdminFS program in Application Developer IE, provided you set the PROVIDER_URL value in the associated configuration file.
  3. Use the CountApp.ear provided. If you want to add security settings or modify the EAR file, you can pass it through the Application Assembly Tool.
  4. Install the EAR file as you would any EAR on AE or AEs.
  5. Put the listener configuration file on the file system so that Application Server can access it.
  6. Use the administrative console of AE or the admin client of AEs to define and configure the JMS provider and EMS. On AEs, repeat the steps described above for the WebSphere test environment. The user interface is organized differently on AE, but the sequence is the same for AE and AEs.
  7. Restart the admin server to apply the changes to its configuration.
  8. Test by running a process that puts a message in the queue connected to the listener.

Stage 4. Preparing for Message Driven Beans

When listeners and message beans were included in EMS, the MDB specification was at a formative stage. Indeed, listeners and message beans were intended to perform the role of MDBs until the standard became solid. In anticipation of a J2EE standard, the IBM solution was designed to facilitate conversion to MDBs. Therefore, the final iteration on the Count application will be to replace the listener and message bean with an MDB.

Version 5 of WebSphere Application Server AE and AEs will support MDBs in conformance with the J2EE 1.3 specification. An MDB will perform the task of both the listener and the message bean. The fourth stage of the Count application will look like Figure 6 below.


Figure 6. Message Driven Beans
Graphic depicting Message Driven Beans

Elements of the Count application:

An MDB replaces the EMS listener and message bean. It calls the business logic class directly. The logic should be encapsulated in a session EJB. Use a separate sender bean to produce the response message. The conversion from message bean to MDB is expected to be straightforward, provided you adopt recommended practices when designing a message bean. Essentially, the differences are:

  • An MDB has no home or remote interface, and has just the bean class.
  • An MDB implements interface MessageDrivenBean instead of SessionBean.
  • No listener configuration file is required.
  • Installation on Application Server will be different from listeners and message beans.

Brief notes about transactions

You may want to encapsulate your JMS code in session EJBs, and as soon as you do so, you provide a transactional context in which to produce and consume messages. Transactions is too large a topic to cover here. However, some points are too important to omit.

  • If a session EJB produces a message within a transaction scope, then this scope extends only to passing the message to the MOM system. The consumer who receives the message must do so in a separate transaction.
  • If a message is sent to a queue in a transaction that is later rolled back, the message is removed from the queue. Similarly, if a message is read from a queue in a transaction that is later rolled back, the message is returned to the queue.
  • Rolling back a message consuming operation can produce a circular problem: if something in the message caused the transaction to fail, then pushing the message back onto the queue means it will be read again and break the transaction again, repeatedly. Set maxtries value in the listener configuration file to terminate such a loop.
  • WebSphere MQ can participate in local transactions, in which WebSphere MQ controls transaction commit or rollback, or global transaction managed by Application Server. Global transactions enable two-phase commit across different XA-enabled enterprise information systems such as DB2® or CICS®.
  • To include WebSphere MQ connections in a transaction, just use a queue connection factory of type JMSWrapXATopicConnectionFactory. Define such an object in JMSAdmin by a using keyword WSQCF, as in:
    Define WSQCF(FactoryName)

  • EMS listeners and MDBs can initialize a transaction scope. Therefore, in one transaction, you can consume the message, perform a business task, and act upon another EIS system such as CICS or DB2. Figure 7 below shows a global transaction involving WebSphere MQ and DB2 that could be added to the Count application.
    Figure 7. Logging receipt of a message
    Graphic depicting the logging of a message

Conclusion

This article discusses developing JMS applications with Application Developer IE with WebSphere MQ as the MOM system. It presents solutions to the problem of how to design for asynchronous consumption of messages. Four designs are presented, and sample code is provided for two of them.

  • The Stage 1 solution, writing your own listener, is simple to implement and does not require Application Server unless you include a Web application or add EJBs. However, it is limited: management of the listener lifespan is awkward and you cannot set up a transactional context for consuming a message so that transaction rollback returns the message to the queue. This solution can be developed with Application Developer.
  • The Stage 2 solution, using WebSphere Application Server to start your listener, attempts to improve on management of the listener lifespan. This solution can be developed and tested in Application Developer, and installed on Application Server AEs or AE.
  • The Stage 3 solution, using an EMS listener from Application Server EMS, can be developed now with Application Developer IE. It allows for a much more robust solution than Stages 1 or 2: the application server manages the listener and a message can be consumed in a fully functional transaction context. The price for this great improvement in robustness is some additional complexity: you must develop the message bean as a session EJB and complete additional steps when installing an application on Application Server AE or AEs with EEX.
  • The Stage 4 solution, using MDBs, will be implementable in the later half of 2002. It does not add functionality or robustness to Stage 3, but conforms to the J2EE 1.3 specification. The structure of the application is simplified because the MDB replaces both the listener and message bean, and installation may also be simplified because listener configuration files will not be required.

The emergence of the JMS standard and finalization of the MDB specification is increasing the use of MOM systems in distributed enterprise applications, and encouraging existing MOM users to convert legacy applications to conform to the standards. You can now start building applications that use JMS. This article suggests strategy for designing the applications to facilitate converting to MDBs.



Download

NameSizeDownload method
JMSsamples.zip80 KBFTP|HTTP

Information about download methods


Resources

About the authors

Paula McMillan is a member of WebSphere Training and Technical Enablement specializing in developing advanced-level training for the WebSphere family of products. Under her previous name, Paula Lumby, she is co-author of Computer Science textbooks, including Java Programming: Advanced Topics, published by Thomson Learning. You can reach Paula at paulamcm@ca.ibm.com.

Sandy Minocha is a software developer at the IBM Toronto Lab. He is currently working on IBM's WebSphere Studio Adapter Toolkit, the next-generation of Enterprise Access Builder tools. You can reach Sandy at minocha@ca.ibm.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14341
ArticleTitle=IBM WebSphere Developer Technical Journal: Developing Java Message Service Applications with WebSphere Studio Application Developer, Integration Edition, Version 4.1
publish-date=04182002
author1-email=paulamcm@ca.ibm.com
author1-email-cc=
author2-email=minocha@ca.ibm.com
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers