Fotolia_76400607_XS.jpg

The Goal

Message Queues can be used for a vast amount of problems, from establishing worker processes to managing load on expensive resources. But, how can you effectively use WebSphere MQ with WebSphere Application Server? Let’s find out!

This tutorial assumes you already have WebSphere Application Server installed, and either have WebSphere MQ installed or have it accessible. If you still need WebSphere Application Server, click here to grab WebSphere Application Server for Developers. If you need WebSphere MQ, grab WebSphere MQ v8 for Developers from here.

The Example

To demonstrate this, we’ll start with a simple “Hello World” with a basic servlet and an MDB, then move to a full blown Spring application.

Preparing WebSphere MQ

To simply the tutorial, I am running WebSphere MQ locally. If you are going to leverage an instance that is already configured and running, just skip ahead.

I already have the mqm user and group, but I will add my user to the mqm group as well:

1
sudo usermod -a -G mqm craig

Next, I’ll create a new Queue Manager called TUTORIALQM and start the TCP listener. You can use MQExplorer to do this, or do the following:

1
2
3
4
newgrp mqm
/opt/mqm/bin/crtmqm -u DLQ TUTORIALQM
/opt/mqm/bin/strmqm TUTORIALQM
/opt/mqm/bin/runmqlsr -m TUTORIALQM -t TCP &

Creating the Queues for the Tutorial

To keep it simple, I’ll just start with two queues: REQ.HELLO and RESP.HELLO (for my Hello World requests and responses). You may choose to use MQExplorer to do this, or do the following:

queuedefs.conf
1
2
3
4
5
DEFINE QLOCAL ('REQ.HELLO') +
REPLACE
DEFINE QLOCAL ('RESP.HELLO') +
REPLACE
1
/opt/mqm/bin/runmqsc TUTORIALQM < queuedefs.conf

Preparing WebSphere Application Server

Now that the queues are ready to go, we can startup WebSphere and configure our queues.

Defining the Queue Connection Factory

  1. Launch the WebSphere Admin Console (typically http://localhost:9060/ibm/console)
  2. Expand Resources -> JMS -> Queue connection factories
  3. Adjust the Scope drop down to Server=server1
  4. Click New
  5. Select WebSphere MQ messaging provider and click OK
  6. Enter a name and a JNDI name, I will use HelloConnectionFactory and jms/HelloConnectionFactory
  7. Click Next
  8. Select “Enter all the required information into the wizard” and click Next
  9. Enter the Queue Manager name (TUTORIALQM) and click Next
  10. Enter the hostname and port for MQ (localhost, port 1414) and finish the wizard
  11. Save the WebSphere configuration

Defining the Queues

Next, we have to define the Queues within WebSphere:

  1. Expand Resources -> JMS -> Queues
  2. Adjust the Scope drop down to Server=server1
  3. Click New
  4. Select WebSphere MQ messaging provider, click OK
  5. Enter a name and JNDI name for the request queue, as well as the Queue name.
    I will use:
    • Name: Request_HelloQueue
    • JNDI Name: jms/Request_HelloQueue
    • Queue name: REQ.HELLO
  6. Click OK
  7. Click New and repeat for the response queue:
    • Name: Response_HelloQueue
    • JNDI Name: jms/Response_HelloQueue
    • Queue name: RESP.HELLO
  8. Save the WebSphere configuration

Defining the Activation Specification

Lastly, we need to define an Activation specification to invoke our Message-Driver Bean. Within the Admin Console:

  1. Expand Resources -> JMS -> Activation specification
  2. Adjust the Scope drop down to Server=server1
  3. Click New
  4. Select WebSphere MQ messaging provider, click OK
  5. Enter a name and JNDI name, I will use Activation_Hello and jms/Activation_Hello
  6. Click Next
  7. Enter the destination queue’s JNDI name (the request queue JNDI name we defined earlier, jms/Request_HelloQueue) and click Next
  8. Select “Enter all the required information into the wizard” and click Next
  9. Enter the Queue Manager name TUTORIALQM and click Next
  10. Enter the hostname and port for MQ (localhost, 1414) and complete the wizard
  11. Save the WebSphere configuration
  12. Restart WebSphere

A Basic Example Application

Before we get into a full application, let’s create a simple JSP + servlet and a Message-Driven Bean to demonstrate the basics.

To get started, create an EAR in Eclipse/RAD/RSA called BasicExample, a Dynamic Web Project called BasicExampleWeb, and an EJB Project called BasicExampleEjb (or clone my GitHub repository for the basic example and follow along).

Preparing the Web Application

First, we need to add our Queues and Connection factory to the IBM Web Binding XML file (ibm-web-bnd.xml in WEB-INF). The goal is to have our JNDI names available to the web resource, but allow us to change them in the application server if necessary.

  1. Open ibm-web-bnd.xml
  2. Click Add and select “Message Destination Reference”
  3. Enter jms/Request_HelloQueue into both Name and Binding Name
  4. Click Add and select “Message Destination Reference”
  5. Enter jms/Response_HelloQueue into both Name and Binding Name
  6. Click Add and select “Resource Reference”
  7. Enter jms/HelloConnectionFactory into both Name and Binding Name

Or, if you prefer XML:

ibm-web-bnd.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd
xmlns="http://websphere.ibm.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_2.xsd"
version="1.2">
<virtual-host name="default_host" />
<message-destination-ref name="jms/Request_HelloQueue" binding-name="jms/Request_HelloQueue" />
<message-destination-ref name="jms/Response_HelloQueue" binding-name="jms/Response_HelloQueue" />
<resource-ref name="jms/HelloConnectionFactory" binding-name="jms/HelloConnectionFactory" />
</web-bnd>

Next, we need to add them into web.xml:

  1. Open web.xml
  2. Click Add and select “Resource Reference” and configure:
    • Name: jms/HelloConnectionFactory
    • Type: javax.jms.QueueConnectionFactory
    • Authentication: Container
    • Sharing scope: Shareable
  3. Click Add and select “Message Destination Reference” and configure:
    • Name: jms/Request_HelloQueue
  4. Click “New Destination”
    • Name: jms/Request_HelloQueue
  5. Select jms/Request_HelloQueue in the list and click Next
  6. Configure as such:
    • Type: javax.jms.Queue
    • Usage: Consumes
  7. Click Finish
  8. Click Add and select “Message Destination Reference” and configure:
    • Name: jms/Response_HelloQueue
  9. Click “New Destination”
    • Name: jms/Response_HelloQueue
  10. Select jms/Response_HelloQueue in the list and click Next
  11. Configure as such:
    • Type: javax.jms.Queue
    • Usage: Consumes
  12. Click Finish

Or, if you prefer XML:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>BasicExampleWeb</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<resource-ref>
<res-ref-name>jms/HelloConnectionFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<message-destination-ref>
<message-destination-ref-name>jms/Request_HelloQueue</message-destination-ref-name>
<message-destination-type>javax.jms.Queue</message-destination-type>
<message-destination-usage>Consumes</message-destination-usage>
<message-destination-link>jms/Request_HelloQueue</message-destination-link>
</message-destination-ref>
<message-destination-ref>
<message-destination-ref-name>jms/Response_HelloQueue</message-destination-ref-name>
<message-destination-type>javax.jms.Queue</message-destination-type>
<message-destination-usage>Consumes</message-destination-usage>
<message-destination-link>jms/Response_HelloQueue</message-destination-link>
</message-destination-ref>
<message-destination>
<message-destination-name>jms/Request_HelloQueue</message-destination-name>
</message-destination>
<message-destination>
<message-destination-name>jms/Response_HelloQueue</message-destination-name>
</message-destination>
</web-app>

Preparing the EJB

First, we can just delete ejb-jar.xml, we don’t need it.

We do however want the ibm-ejb-jar-bnd.xml.

  1. Right click the EJB project
  2. Select Java EE Tools -> Generate WebSphere Bindings Deployment Descriptor

We cannot populate ibm-ejb-jar-bnd.xml with IBM’s tooling until after we’ve created the MDB, so let’s do that now.

  1. Right click the EJB project
  2. Select New -> Message Driven Bean (EJB 3.x)
  3. Configure the wizard as such:
    • Java package: com.craigstjean.mqtut.basic.mdb
    • Class name: HelloMDB
  4. Finish the wizard

Next, we can implement HelloMDB as such:

HelloMDB.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package com.craigstjean.mqtut.basic.mdb;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
/**
* Message-Driven Bean implementation class for: HelloMDB
*/
@MessageDriven(
activationConfig = { @ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class HelloMDB implements MessageListener {
@Resource
private MessageDrivenContext mdCtx;
@Resource(name = "jms/HelloConnectionFactory")
private ConnectionFactory connectionFactoryRef;
@Resource(name = "jms/Response_HelloQueue")
private Destination resourceDestRef;
/**
* Default constructor.
*/
public HelloMDB() {
}
/**
* @see MessageListener#onMessage(Message)
*/
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
String name = null;
try {
// Retrieve request
name = ((TextMessage) message).getText();
} catch (JMSException e) {
e.printStackTrace();
mdCtx.setRollbackOnly();
}
try {
// Process request
String responseData = "Hello, " + name;
// Create and send response message
sendResponse(message, responseData);
} catch (JMSException e) {
e.printStackTrace();
// TODO error handling goes here
// consider producing a copy of the message onto a dead letter queue
// (the same way a message is produced onto the response queue, but without expiry)
mdCtx.setRollbackOnly();
} catch (Exception e) {
// NOTE: Do not allow the MDB the throw an exception or it will stop processing messages
e.printStackTrace();
// TODO error handling goes here
// consider producing a copy of the message onto a dead letter queue
// (the same way a message is produced onto the response queue, but without expiry)
}
}
private void sendResponse(Message requestMessage, String responseData) throws JMSException {
Connection jmsConnection = null;
Session session = null;
try {
jmsConnection = connectionFactoryRef.createConnection();
session = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
TextMessage responseMessage = session.createTextMessage();
// ! Set correlation id from our original message id
responseMessage.setJMSCorrelationID(requestMessage.getJMSMessageID());
responseMessage.setText(responseData);
MessageProducer mp = session.createProducer(null);
// use 30 minute expiry
mp.setTimeToLive(1000 * 60 * 30);
mp.send(resourceDestRef, responseMessage);
} finally {
try {
if (session != null) {
session.close();
}
} catch (JMSException e) {
}
try {
if (jmsConnection != null) {
jmsConnection.close();
}
} catch (JMSException e) {
}
}
}
}

As you can see, when a message comes in it simply takes the input, creates a response, and pushes the response onto the response queue. To tie the request queue to HelloMDB:

  1. Open ibm-ejb-jar-bnd.xml
  2. Click Add and select “Message Driven”
  3. Select HelloMDB and click Next
  4. Select “Resource Reference” and click Next
  5. Select “jms/HelloConnectionFactory” and click Next
  6. Enter a binding name of “jms/HelloConnectionFactory” and click Finish
  7. Select “Message Driven (HelloMDB)” and click Add
  8. Select “JCA Adapter” and click OK
    Configure as following:
    • Activation Spec Binding Name: jms/Activation_Hello
    • Destination Binding Name: jms/Request_HelloQueue
  9. Select “Message Driven (HelloMDB)” and click Add
  10. Select “Message Destination Reference” and click OK
  11. Select “jms/Response_HelloQueue” and click Next
  12. Enter a binding name of “jms/Response_HelloQueue”

In all, the resulting XML is:

ibm-ejb-jar-bnd.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar-bnd
xmlns="http://websphere.ibm.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ejb-jar-bnd_1_2.xsd"
version="1.2">
<message-driven name="HelloMDB">
<jca-adapter activation-spec-binding-name="jms/Activation_Hello" destination-binding-name="jms/Request_HelloQueue"/>
<resource-ref name="jms/HelloConnectionFactory"
binding-name="jms/HelloConnectionFactory" />
<message-destination-ref
name="jms/Response_HelloQueue"
binding-name="jms/Response_HelloQueue" />
</message-driven>
</ejb-jar-bnd>

Finishing the Web Application

With our MDB complete, all we need to do is update the web application to take a name, post a message onto the request queue, wait for a response, then return it to the user.

It’s not pretty, but it demonstrates the point:

index.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE HTML><%@page language="java"
contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>index</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<form action="servlet/hello" method="POST">
<div>
Name:
<input type="text" name="name">
</div>
<div>
<input type="submit">
</div>
</form>
<div>
${message}
</div>
</body>
</html>
HelloServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.craigstjean.mqtut.basic.servlet;
import java.io.IOException;
import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/servlet/hello")
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = -8211165987639677828L;
@Resource(name = "java:comp/env/jms/HelloConnectionFactory")
private ConnectionFactory connectionFactory;
@Resource(name = "java:comp/env/jms/Request_HelloQueue")
private Destination requestQueue;
@Resource(name = "java:comp/env/jms/Response_HelloQueue")
private Destination responseQueue;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
Connection jmsConnection = null;
Session session = null;
String response = null;
try {
jmsConnection = connectionFactory.createConnection();
session = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
TextMessage requestMessage = session.createTextMessage();
requestMessage.setText(name);
MessageProducer mp = session.createProducer(null);
// use 30 minute expiry
mp.setTimeToLive(1000 * 60 * 30);
mp.send(requestQueue, requestMessage);
String messageSelector = "JMSCorrelationID='" + requestMessage.getJMSMessageID() + "'";
MessageConsumer mc = session.createConsumer(responseQueue, messageSelector);
jmsConnection.start();
Message responseMessage = mc.receive(5 * 1000);
if (responseMessage != null) {
response = ((TextMessage) responseMessage).getText();
} else {
response = "No message found within 5 seconds...";
}
req.setAttribute("message", response);
req.getRequestDispatcher("/index.jsp").forward(req, resp);
} catch (JMSException e) {
e.printStackTrace();
} finally {
try {
if (session != null) {
session.close();
}
} catch (JMSException e) {
}
try {
if (jmsConnection != null) {
jmsConnection.close();
}
} catch (JMSException e) {
}
}
}
}

Up Next

Now that the basic example is complete, my next post will be a more fully fledged application!

Comment and share

  • page 1 of 1

Craig St. Jean

Father, programmer, constant learner, @pluralsight author


Software Architect