This article addresses many queries I received on subjects that weren't covered in my previous articles on JMS programming with WebSphere MQ and WebSphere Studio Application Developer (Application Developer). See Resources for links to those articles. Part 1 of this article described how to provide asynchronous messaging support for Enterprise JavaBeans (EJB) applications in WebSphere Application Server (Application Server) Version 4.x. It showed how Message-Driven Beans (MDBs), though not supported by Java 2 Enterprise Edition (J2EE) version 1.2, which the Application Server implements, can be simulated in the Application Server.
This article covers how to perform JMS message processing and database access as part of the same transaction. The instructions in this article assume you have read and carried out the instructions in Part 1, because they build on the configurations created and tested in that article.
Before we get into the sample code and configuration instructions, below is a quick overview of transactions, how they work, and how they are provided in a J2EE-compliant application server.
An application uses a transaction to ensure that work done as part of an atomic process using a resource manager, such as a relational database or an enterprise messaging system, is treated as a single unit of work. For example, if a program transfers funds between accounts, using a transaction ensures that both the withdrawal account and the deposit account are updated properly. If everything goes as planned, a commit is done on the transaction and both accounts are updated -- the withdrawal account with a smaller balance and the deposit account with a larger balance. If an error occurs when the application tries to update either account, the transaction does a rollback, and both accounts are returned to the balances they had before the transaction began.
In most cases, a unit of work is performed using a single resource manager, and the resource manager provides the transactional capabilities required by applications. However, sometimes the need arises to coordinate the activity of two or more resource managers, so that work done by all involved resource managers is treated as part of the same transaction. The process that does this coordination is called a transaction manager. Additionally, the resource managers must be capable of being coordinated. Such a resource manager is called an XA resource manager. XA is a distributed transaction processing standard established by the Open Group, a consortium of software vendors focused on standards for interoperability and integration.
The J2EE specification defines the Java Transaction API (JTA) for providing distributed transaction support within an application server that complies with the XA standard. It describes the interfaces between a transaction manager, the resource managers, and applications that perform transactional processing. J2EE also provides the Java Transaction Service (JTS) specification for a transaction manager that implements the JTA. Every J2EE-compliant application server provides a JTS implementation.
In a J2EE environment, transactions are typically performed in the processing done by EJB components. The EJB specification lets developers declare the transactional support required for an EJB component in the deployment descriptor, eliminating the need to programmatically begin, commit, or rollback transactions. The EJB container, using the information in the deployment descriptor, handles the transaction requirements, beginning and committing or rolling back transactions as required.
Typically, EJB transactions deal exclusively with a relational database, so there is no need for a JTA-enabled resource manager (an XA resource manager). If JTA transactions are required, the database and database drivers must be configured to support that requirement; the transactions are still handled by the EJB container and no change is needed to the application code.
Programmatic JMS transaction capability is provided through the Session interface. When a Session is created, it is declared as transacted or not. If it is defined as transacted, application programs call methods on the Session to commit and rollback transactions.
There is no method on Session to begin a transaction. If the Session is transacted, all messages received or sent are part of a transaction and a commit or rollback ends a transaction and automatically begins another.
JMS also has a set of interfaces specifically for support of JTA transactions. These interfaces are meant to be used by an application server, and their use is transparent to JMS clients. When the transactions are handled by the application server, the application code does not use the Session methods for transaction processing.
Installing sample code and configuration files
The sample code is a simple J2EE enterprise application that combines JMS messaging with relational database access. It consists of a Web
application that delivers a text string to a Session bean through a JavaBeans component. The text is used to create a message that is placed on a JMS Queue, then the message ID and text are stored in a relational database using an Entity bean.
You should download and install the sample code and configuration files now. Later in the article, when each of the files is used, I'll give a detailed description of the file and its contents.
First, download the code and copy the configuration files into the appropriate WebSphere MQ folder.
- Download the sample code and configuration files (see link above). This file is named i-mqejb2.zip.
- Extract the zip file to the C:\ folder on your system. Be sure to preserve folder names when extracting. This will create a C:\MQEJB2 folder on your system that contains the sample code and configuration files.
- Copy C:\MQEJB2\setenv.bat to C:\WSMQ\Java\bin, overwriting the setenv.bat file that currently exists there.
- Copy C:\MQEJB2\setup.ejb to C:\WSMQ\Java\bin.
Next, import the sample code into Application Developer.
- Launch Application Developer from the Start menu. From now on, the instructions assume that Application Developer is running.
- Select File>Import... from the menu.
- Select EAR file and click Next.
- Type
C:\MQEJB2\transmq.earin the EAR File field. - Type
TransMQEARin the Enterprise application project name: field. Click Next. - Select TransMQWeb.war in the Module files in EAR: listbox.
- Check TransMQEJBs.jar in the Available dependent JARs: listbox.
- Click Finish.
The MessageHandler Session bean
The majority of the processing for the sample application takes place in the MessageHandler Session bean. In the ejbCreate() method, shown below, the local environment is used to look up the JMS administered objects needed for JMS processing. Then a MessageDataFactory instance is created for accessing the MessageData Entity bean.
public void ejbCreate() throws CreateException {
try {
Context context = new InitialContext();
Context environment = (Context) context.lookup("java:comp/env");
factory =
(QueueConnectionFactory) environment.lookup(QUEUE_CONNECTION_FACTORY);
queue = (Queue) environment.lookup(QUEUE);
messageDataFactory = new MessageDataFactory();
} catch (NamingException ne) {
throw new CreateException(ne.getMessage());
}
} |
As shown below, the handleMessage() method is where the real action takes place. First, the text parameter is used to create a message, and the message is sent to a Queue. Then the MessageDataFactory is used to create a MessageData Entity bean.
public void handleMessage(String text) {
QueueConnection connection = null;
try {
connection = factory.createQueueConnection();
QueueSession session =
connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender sender = session.createSender(queue);
TextMessage message = session.createTextMessage(text);
sender.send(message);
if (text.equalsIgnoreCase("JMS exception")) {
throw new EJBException("Thrown to simulate JMS error");
}
messageDataFactory.create(message.getJMSMessageID(), text);
if (text.equalsIgnoreCase("DB exception")) {
throw new EJBException("Thrown to simulate DB error");
}
} catch (CreateException c) {
throw new EJBException(c);
} catch (RemoteException r) {
throw new EJBException(r);
} catch (JMSException j) {
throw new EJBException(j);
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException j) {
throw new EJBException(j);
}
}
}
} |
Notice the if statements in handleMessage() above, where an exception is thrown if the condition is true. These statements let you test the transactional capability of this code once it is deployed. By passing in a text string of "JMS exception," you can test how the code behaves when an error occurs during JMS processing. Likewise, you can pass in "DB exception" to test how the code behaves when an error occurs accessing the relational database (through the MessageData
Entity bean).
Note that there is no code in here for transactions -- no begin, no commit, no rollback. Even the code that creates the QueueSession passes in false to indicate that the QueueSession is not transacted. As you will see below, everything needed for transactional processing is handled in configuration.
The MessageBean class is used within the Web module of
the enterprise application to access the MessageHandler
Session bean. This class contains a write-only text
property and a read-only results property. In the Web
module, the text property is set through an HTML
form, the execute() method is called, and then the
results property is diplayed using a JavaServer Pages
(JSP) component.
The execute() method, shown below, simply creates
a MessageHandler Session bean from an EJB factory
instantiated in the constructor, calls the Session bean's handleMessage() method, passes in the text property as the parameter, then sets the results property to a success message if no errors occurred. If an error occurs, results is
set to an error message.
public void execute() {
try {
MessageHandler ejb = ejbFactory.create();
ejb.handleMessage(text);
ejb.remove();
results = "Message successfully processed";
} catch (Exception e) {
e.printStackTrace();
results = "Error processing message";
}
} |
The other Java components that make up the sample code are the:
MessageDataEntity bean- A very simple Entity bean that uses container-managed persistence (CMP) to
store its attributes in a relational database. Those attributes are
id, theMessageIDfrom the JMS message, andtext, theStringbody of the message. TheMessageDataEntity bean was created almost entirely using the Create an Enterprise bean wizard in Application Developer. It is very straightforward, so I don't include the source code here. MessageControllerservlet- Was created with an Application Developer Create Web Pages from a Java bean wizard, which also created the HTML input form for passing in text and the JSP to display the results. I don't review the source code of these components here, but if you look at them, you'll see that they perform the processing described above in the
MessageBeandiscussion.
As you saw above, there is nothing in the source code of the sample application that has anything whatsoever to do with transactions. So how do you make sure that the work performed occurs within the scope of a transaction? You do that within the EJB deployment descriptor. In this section you'll look at the deployment descriptor, examine where transaction requirements are set, and review some other settings that are important to the workings of the application.
- Switch to a J2EE perspective in Application Developer, and navigate within the J2EE View to the TransMQEJBs module.
- Right-click on TransMQEJBs and select Open With>EJB Editor.
- In the EJB Editor, click on the Transaction tab.
- Click the + sign next to MessageHandler.
The entry indicates that all remote methods of this EJB component must be performed within a transaction. If the client code that calls the method has not started a transaction, then the container will start one before the method is called. Once the method has completed normally, the container will commit the transaction. If the method throws an exception, the container will rollback the transaction. Recall the source code above, where no transaction programming was performed; this is how a transaction will be started in the sample application. - Click the + sign next to MessageData.
The entries there indicate that all remote and home methods of this EJB component must be performed within a transaction. Since theMessageDatacomponent is only accessed by thehandleMessage()method in theMessageHandlerSession bean, calls toMessageDatawill participate in the same transaction that the container creates before callinghandleMessage().
In the ejbCreate() method of MessageHandler, lookups are done on the local environment for the QueueConnectionFactory and Queue administered objects. References to these objects are set in the deployment descriptor.
- Click on the References tab in the EJB Editor.
- Click the Resource references radio button.
- Click the + sign next to MessageHandler.
The entries there indicate the names and types of the resource references, which match the names used inejbCreate().
At this point, you're probably wondering how those names are going to be
tied to the JMS objects bound into the Java Naming and Directory Interface (JNDI)
namespace. You might also wonder how the MessageData Entity bean knows which datasource to use to access its persistent data. You can see those entries in the EJB Extension Editor.
- Right-click on TransMQEJBs in the J2EE View and select Open With>EJB Extension Editor.
- Click on the Bindings tab in the EJB Extension Editor.
- Click the + sign next to TransMQEJBs.
- Click the + sign next to MessageHandler.
There you see the resource references defined in the EJB Editor. Click on each one, and you will see thatqueueConnectionFactoryis bound to the JNDI namejms/EJBQCFandqueueis bound to the JNDI namejms/EJBQueue. These JMS objects will be created in a later step. - Click on MessageData.
There you see that the datasource that will be used is namedjdbc/jmsdb. You will create that datasource in a later step.
Installing and configuring DB2
Now that you've looked at the sample code and the EJB deployment descriptor, you
are ready to run the application. But, first, you need to install a database to
which MessageData can persist its attributes, and
configure the database so it can participate in JTA transactions. You will
be installing and configuring IBM DB2 Universal Database (Personal Edition) version 7.2.
First, to install the product:
- Download the trial version of DB2 Universal Database (Personal Edition) version 7.2 for Windows (see Resources for a link). The file is named 100_PE_WIN_SBCS.zip.
- Extract the zip file to a folder on your system. Be sure to preserve folder names when extracting.
- After extracting the zip file, launch setup.exe in the folder to which the setup files were extracted.
- On the Select Products dialog, only check DB2 Personal Edition.
- On the Select Installation Type dialog, select Typical.
- On the Choose Destination Location dialog, change the Destination folder
to
C:\SQLLIB. - On the Enter Username and Password for Control Center Server dialog, enter
passwordin the Password field and also in the Confirm password field. - Click Yes when prompted to create the db2admin username.
- Close all the windows that open after DB2 has finished installing.
Now, you need to configure DB2 to use JDBC version 2:
- From the Windows Start button, select Settings>Control Panel>Administrative Tools>Services to start the Windows Services applet.
- Stop the following services by selecting each service, right-click, and select Stop. These services are named:
- DB2 - DB2
- DB2 - DB2DAS00
- DB2 JDBC Applet Server
- DB2 Remote Command
- DB2 Security Server
- Do not close the Services applet at this point.
- Open a Command prompt and navigate to C:\SQLLIB\java12.
- Type
usejdbc2and press Enter. - Type
exitand press Enter. - Return to the Services applet and start the services stopped above by selecting each service, right-click, and select Start.
- Close the Services applet when finished.
Next, you will create the database that will be used by the sample application:
- From the Windows Start button, select Programs>IBM DB2>Command Window to start a DB2 Command Window.
- Navigate to C:\MQEJB2.
- Type
db2 -tvf init.ddland press Enter. This will create the jmsdb database and the messagedata table to contain the attributes ofMessageData. You will see a message "The database directory cannot be found on the indicated file system" when the command first begins executing. This can be safely ignored. - Wait for the command to complete, but keep this window open once it does complete.
The final step in configuring DB2 is to enable the database to participate in JTA transactions:
- From the Windows Start button, select Programs>IBM DB2>Control Center to start the DB2 Control Center.
- Navigate the Systems tree to the DB2 instance.
- Right-click on DB2 and select Multisite Update>Configure....
- Select the Use the TP monitor named below radio button.
- In the TP monitor drop-down, select JTS.
- Click Finish.
- Close the DB2 Message window that appears, but leave Control Center running.
- Return to the DB2 Command Window opened earlier, type
db2 -tvf jtainit.ddland press Enter. This binds required classes to the database. You can safely ignore any messages about isolation levels being escalated. - Type
exitand press Enter.
Creating the JMS administered objects
With the database installed and configured, you can now create the JMS administered objects and bind them into the JNDI namespace. Take a look at the files you will use to do that, starting with setenv.bat below.
@echo off set WAS_HOME=c:\WSAD\plugins\com.ibm.etools.websphere.runtime set MQ_JAVA_INSTALL_PATH=c:\wsmq\java @rem Java runtime set JAVA_HOME=C:\WSAD\plugins\com.ibm.etools.server.jdk\jre\bin @rem MQ JMS set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\com.ibm.mq.jar set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\com.ibm.mqjms.jar set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\jms.jar set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\connector.jar set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\fscontext.jar set MQ=%MQ%;%MQ_JAVA_INSTALL_PATH%\lib\providerutil.jar set MQ=%MQ%;%WAS_HOME%\lib\resources.jar set CLASSPATH=%MQ%;%CLASSPATH% set PATH=%JAVA_HOME%;%MQ_JAVA_INSTALL_PATH%\lib;%PATH%; |
setenv.bat sets the environment variables needed by JMSAdmin to function properly. The only difference between this version of setenv.bat and the version used in Part 1 of this article is the inclusion of resources.jar from the WebSphere Application Server runtime library. This file contains the JTA-enabled WebSphere MQ classes.
The next file to examine is the script that creates the administered objects, setup.ejb, shown below.
chg ctx(mq) def wsqcf(EJBQCF) qmgr(QM2) def q(EJBQueue) qmgr(QM2) queue(Q2) end |
By now, this script should look mostly familiar, except for the line that defines the QueueConnectionFactory. This script creates a QueueConnectionFactory to be used for JMS code that participates in JTA transactions within WebSphere Application Server (a WSQCF).
Note that the administered objects point to WebSphere MQ objects that were created in Part 1 of this article.
Now you can simply run the configuration commands, using the script file, to create the administered objects. You will use the JMSAdmin configuration file, JMSAdmin.fscontext.config, that was supplied with Part 1.
- Open a command prompt.
- Change directory to C:\WSMQ\Java\bin.
- Type
setenvand press Enter. - Type
JMSAdmin<setup.ejb -cfg JMSAdmin.fscontext.configand press Enter. - Type
exitand press Enter.
Creating the Application Server JMS references
Next, you will define the JMS objects in the JMS Provider configuration in the Application Server.
- Switch to a Server perspective in Application Developer.
- In the Servers view, right-click on TestServer and select Start.
- Wait until you see the message "Server Default Server open for e-business" before proceeding.
- In the Servers view, right-click on TestServer and select Run administrative client.
- At the Login screen, click Submit.
- Click the + sign next to Resources.
- Click the + sign next to JMS Providers.
- Click the + sign next to WebSphere MQ. Here you are using the JMS Provider defined in Part 1 of this article.
- Click JMS Destinations.
- Click New.
- In the Name: field, type
EJBQueue. - In the JNDI Name: field, type
jms/EJBQueue. - Set the Destination Type: field to
QUEUE. - In the External JNDI Name: field, type
mq\EJBQueue. (Note the backslash \ instead of slash /.) - Click OK.
- Click JMS Connection Factories.
- Click New.
- In the Name: field, type
EJBQCF. - In the JNDI Name: field, type
jms/EJBQCF. - In the External JNDOI Name: field, type
mq\EJBQCF. (Note the backslash \ instead of slash /.) - Set the Connection Type: field to
QUEUE. - Click OK.
- Click Save on the menu bar across the top of the Web browser.
- Click OK to save the configuration.
- Click Exit on the menu bar across the top of the Web browser.
- Close the Web browser by clicking the X next to Web browser.
- In the Servers view, right-click on TestServer and select Stop.
The last configuration step before testing the sample application is to create the datasource that will connect MessageData to the database you created earlier. You configured the database to participate in JTA transactions, but you must also use a JTA-enabled Java Database Connectivity (JDBC) driver for everything to work properly.
- In the Server Configuration view, double-click TestServer under Server Configurations.
- Click the Data source tab.
- Click the Add... button next to the JDBC driver list listbox.
- In the Name: field, type
Db2JtaDriver. - In the Implementation class name: field, type
COM.ibm.db2.jdbc.DB2XADataSource. - In the URL prefix: field, type
jdbc:db2 - In the Class path: field, type
C:/SQLLIB/java/db2java.zip. - Click OK.
- Select the JDBC driver just created, and click the Add... button next to the Data source defined in the JDBC driver selected above: listbox.
- In the Name: field, type
JMSDB. - In the JNDI name: field, type
jdbc/jmsdb. - In the Database name: field, type
jmsdb. - Check the Enable JTA checkbox.
- Click OK.
- Save the configuration by pressing Ctrl-S.
- Close the server configuration editor by clicking the X next to TestServer.
- Stop and restart Application Developer. This allows the changes made to the operating system PATH environment variable when installing DB2 to be picked up by Application Developer.
Testing the sample application
You're now ready to test the sample application, after you've assigned it to run in the test server.
- In the Server Configuration view, right-click on TestServer under Server Configurations, and select Add Project>TransMQEAR.
- In the Servers view, right-click on TestServer and select Start.
- Wait until you see the message "Server Default Server open for e-business" before proceeding.
- Right-click on TransMQWeb in the Navigator view, and select Run on Server.
- On the Input Form, type any text in the Text: field and click Submit.
- You will see "Message successfully processed" on the Result Form.
- Use MQSeries Explorer to verify that the message arrived on queue Q2 of queue manager QM2.
- Switch to DB2 Control Center and navigate to the Tables folder under the JMSDB database.
- Right-click on the MessageData table and select Sample Contents. There you will see the database row created from the text. Click Close.
- Click Back on the Web browser, and enter
JMS exceptionin the Text: field and click Submit. You will get an "Error processing message" result on the Result Form. In the Console, you will see that anEJBExceptionwas thrown with the message "Thrown to simulate JMS error." Use MQSeries Explorer and DB2 Control Center to verify that no message was sent and no row was written. - Try typing
DB exceptionin the Text: field of the Input Form.
As you have seen, getting JMS message processing and relational database access to participate in the same transaction is a matter of configuration, not programming. The JMS side was very easy -- just one line in the script used with JMSAdmin, to create a WSQCF instead of a standard QCF. The database side took a little more effort, with both the database and the JDBC driver requiring special configuration. But now that you've seen how it's done, you can incorporate this powerful capability into your own applications.
| Name | Size | Download method |
|---|---|---|
| i-mqejb2.zip | HTTP |
Information about download methods
- Participate in the discussion forum.
- Download a zip file with the supporting files for this article.
- Read Willy's related articles: "Using WebSphere MQ in WebSphere Application Server Version 4.x, Part 1" (developerWorks, October 2002), "
Writing JMS Programs, Part 1" (developerWorks, March 2002), and "
Writing JMS Programs, Part 2" (developerWorks, April 2002).
- Download a trial version of DB2 Universal Database (Personal Edition - PE) 7.2.
- Learn more about The Open Group.
- Visit Sun's site for information about the Java Transaction API and the Java Transaction Service.

Willy Farrell works for IBM Developer Relations Technical Consulting (A.K.A. The DragonSlayers), providing education, enablement, and consulting to IBM business partners. He has been programming computers for a living since 1981, began using Java in 1996, and joined IBM in 1998. Willy holds the following technical certifications, among others: Java 2 Programmer, WebSphere Application Server Enterprise Developer, WebSphere Studio Application Developer Solution Developer, MQSeries Solutions Expert, and IBM e-business Solution Technologist. You can contact Willy at willyf@us.ibm.com.
