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.
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:
- JMS tutorial (from Sun)
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.
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\MQSeriesand MA88 installs toC:\Program Files\IBM\MQSeries\Java. - Make sure the
..\MQSeries\Java\libdirectory 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.
- 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.
- Name the new variable
MQ_JAVA_INSTALL_PATHand set its value to the absolute path where MA88 is installed. Usually the path ends with ..\MQSeries\Java. - 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 |
| IVTRunNoJndi | Runs the MQJMSIVT test program without
using JNDI. |
| IVTRunFSJndi | Runs the MQJMSIVT test program using
the file system JNDI to look up administered objects. |
| IVTRunWASJndi | Runs the MQJMSIVT test program using
the WebSphere test environment JNDI namespace to look up
administered objects. |
| JMSAdminFS | Runs the JMSAdmin utility with a
configuration file, JMSAdminFS.config,
that specifies the file system JNDI context. |
| JMSAdminWAS | Runs 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.
- Download and unzip the samples file below. Be sure to preserve folder names when you unzip.
- Create a directory
D:\temp, if you do not already have one. TheJMSAdminFS.configfile that is provided setsD:\tempas the directory in which to store JNDI bindings. - 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. - Set the Java build path for your project.
- From the project Properties sheet, select Java Build Path, click the Libraries tab, and select Add Variable.
- 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
- Import the file
verification.jarinto your project to add theverificationpackage to your project. If any classes have errors, check your build path. - Run the class
JMSAdminFSas a Java application. In the Console, see the promptInitCtx>. - Enter
JMSAdmincommands. 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 objectsivtQCFandivtQare listed in the display of the JNDI context
end
The keywordqcfdefines acom.ibm.mq.jms.MQQueueConnectionFactoryobject. Keywordqueue, shortened toq, defines acom.ibm.mq.jms.MQQueueobject. See the WebSphere MQ product InfoCenter (Start => Programs => IBM MQSeries => Information Center) for a complete list of the JMSAdmin keywords and commands. - Run
IVTRunFSJndias 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 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
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
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
QueueReceiverobject by passing it as an argument of thecreateQueueReceivermethod. - 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 { |
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 { |
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)
| Create the queue for requests (enter on one line). |
define qlocal(Q2)
| 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.
- Open Application Developer to the Java perspective.
- Import
Stage1.jarinto a Java project. You can add it to theJMSsamplesproject and see a new packagesamples.count.stage1. - If you created a new project, set up the project build path exactly as you did to run the verification program.
- Run
JMSAdminFSas 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
- 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. - 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
- 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
- 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
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
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:
- 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. - 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:
- 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)
TheonMessagemethod should perform only the task of receiving the message, and pass the message on to a business logic EJB. - 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.
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
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.
- Open Application Developer IE to the Java perspective.
- Run
JMSAdminFSfrom theJMSsamplesproject. 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
- Open the J2EE perspective and import
CountApp.ear. You can name the Enterprise Application project Stage 3. The import gives you an EJB projectCountAppEJBcontaining two EJBsCountMessageandCount. - Open the Java perspective and import the
Stage3.jarinto yourJMSsamplesproject. See a new packagesamples.count.stage3. - 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.
- Add the project Stage3 to the Stage3 server configuration.
- 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.
- 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.
- 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%20for them, as inProgram%20Files.
http://localhost:9090/admin/edit?configFile=
<WSADIE>workspace/TestServers/Stage3.wsc/server-cfg.xml
Provide any user ID when prompted. - Expand Resources on the left of the administrative client, and click JMS Providers.
- Click New. Select resource provider type IBM MQSeries (Local WebSphere Naming Context). Click Next.
- 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 inJMSAdmin.config, for example, file://D:/temp - Under JMS Providers, select IBM MQSeries, JMS Connection Factory, and click New.
- 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 - 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 - Configure the Extended Message Service as a custom service. First,
extract
jmsconfig.xmlfrom the ZIP file and note where it resides. - Expand the tree:
Stage3
Nodes
Localhost
Application Servers
Default Server
Select Custom Services and click New. - 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 - Save your server configuration to
<WSADIE>workspace/TestServers/Stage3.wsc/server-cfg.xml. - Close the browser view.
Now you are ready to test the listener.
- 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
- Go to the Java perspective and run the
samples.count.stage3.CountClientapplication in theJMSsamplesproject. 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
- 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:
- Define the queues in WebSphere MQ.
- Run
JMSAdminto 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 theJMSAdminWASorJMSAdminFSprogram in Application Developer IE, provided you set the PROVIDER_URL value in the associated configuration file. - Use the
CountApp.earprovided. If you want to add security settings or modify the EAR file, you can pass it through the Application Assembly Tool. - Install the EAR file as you would any EAR on AE or AEs.
- Put the listener configuration file on the file system so that Application Server can access it.
- 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.
- Restart the admin server to apply the changes to its configuration.
- 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
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
maxtriesvalue 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
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.
| Name | Size | Download method |
|---|---|---|
| JMSsamples.zip | 80 KB | FTP |
Information about download methods
- WebSphere Application Server documentation: WebSphere
Application Server Enterprise Services InfoCenter
=> Enterprise Services => Extended Messaging
Support
(downloadable
PDF)
- Book: Java Message Service by Heafel & Chappell, ISBN:
0-596-00068-5
- Sun technology page:
Java
Message Service
- IBM developerWorksTM:
Java
technology zone,
search on "JMS"
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)





