You are on page 1of 9

Asynchronous Messaging Made Easy With Spring JMS

Asynchronous process communication is an important part of a service-oriented architecture (SOA), since many
system communications in an enterprise, especially those with external organizations, are asynchronous in nature.
Java Message Service (JMS) is the API used to write JEE applications using asynchronous messaging. A
traditional messaging implementation using the JMS API involves steps like JNDI lookups for the queue
connection factory and Queue resources and creating a JMS session before actually sending or receiving a
message.
The Spring framework simplifies the task of working with JEE components, including JMS. It provides a template
mechanism to hide the details of a typical JMS implementation so developers can concentrate on the actual task of
processing messages instead of worrying about how to create, access, or clean up JMS resources.
This article provides an overview of the Spring JMS API and how to use it to process (send and receive) messages
asynchronously in a sample web application running in a JBoss MQ server. I will compare the traditional way of
implementing JMS with a Spring JMS implementation to show how easy and flexible it is to process messages
using Spring JMS.

Asynchronous Messaging and SOA


In the real world, most web requests are processed synchronously. For example, when a user logs into a website,
he or she enters a user name and password and the server authenticates the login credentials. If authentication is
successful, the program lets the user into the website. Here, the login request is processed immediately after it's
received from the client. Credit card authorization is another example of a synchronous process; a customer is
allowed to proceed only after the server verifies that the entered credit card number is valid and the customer has
sufficient credit in the account. But let's consider the payment settlement step in an order processing system. Once
the system verifies that the user's credit card information is accurate and there are sufficient funds in the account,
it doesn't need to wait until all of the payment details are finalized and fund transfer is completed. The payment
settlement is done in an asynchronous manner so the customer can proceed with the checkout process.
Asynchronous processing is used for the requests that take a longer time to process than a typical synchronous
request. Another example of an asynchronous process is the credit request process submitted to an Automated
Underwriting System (AUS) in a home loan processing application. After a borrower submits the loan application,
the mortgage company sends a request to AUS for credit history information. Since this request is for a
comprehensive credit report with details such as borrower's current and past credit accounts, late payments, and
other financial details, it usually takes longer time (hours or sometimes even days) to get a response for these
requests. It doesn't make sense for the client program (application) to open a connection to the server and wait that
long for the results. So the communication occurs asynchronously; i.e., once the request is submitted, it is placed
in a queue and the client disconnects from the server. Then the AUS service picks up the request from the
specified queue, processes it, and puts the result message in another message queue. Finally, the client program
will pick up the result from the queue and continue with processing the credit history results.

JMS
If you have worked with JMS, it's very similar to writing JDBC or JCA code. It involves boilerplate code to create
or retrieve JMS resource objects that make the task more code-intensive and repetitive every time you need to
write a new class to send or receive a message. The following list shows the steps involved in a traditional JMS
implementation:
1. Create JNDI initial context.
2. Get a queue connection factory from the JNDI context.
3. Get a Queue from the queue connection factory.
4. Create a Session object.
5. Create a sender or receiver object.
6. Send or receive a message using the sender or receiver object created in Step 5.
7. Close all of the JMS resources after processing the messages.
As you can see, Step 6 is the only place where the message processing is done. The other steps are there just to
manage JMS resources, which has nothing to do with the actual business requirements, but developers have to
write and maintain code for these additional steps.
Spring JMS
The Spring framework provides a template mechanism to hide the details of Java APIs. JEE developers can use
JDBCTemplate and JNDITemplate classes to access a back-end database and JEE resources (data sources,
connection pools), respectively. JMS is no exception. Spring provides the JMSTemplate class so developers
don't have to write the boilerplate code for a JMS implementation. The following are some of the advantages
provided by Spring when developing JMS applications.
1. It provides a JMS abstraction API that simplifies the use of JMS to access the destinations (queues or
topics) and publish messages to the specified destinations.
2. JEE developers don't need to be concerned about the differences between different JMS versions (such as
JMS 1.0.2 versus JMS 1.1).
3. The developers don't need to specifically deal with JMS exceptions, as Spring provides an unchecked
exception for any JMS exception that is rethrown in JMS code.
Once you start using Spring in JMS applications, you will appreciate its simplicity for asynchronous messaging.
The Spring JMS framework offers a variety of Java classes to make it easy to work with JMS applications. Table 1
lists some of these classes.

Table 1. Spring JMS classes


Class Name Package Function
JmsException org.springframework.jms This is the base (abstract) class for any exceptions
thrown by Spring framework whenever there is a
JMS exception.
JmsTemplate, org.springframework.jms. These are helper classes used to simplify the use
JmsTemplate102 core of JMS by handling the creation and release of
JMS resources like connection factories,
destinations, and sender/receiver objects.
JmsTemplate102 is a subclass of
JmsTemplate that uses the JMS 1.0.2
specification.
MessageCreator org.springframework.jms. This is a callback interface used by the
core JmsTemplate class. It creates a JMS message
for a specified session.
MessageConverter org.springframework.jms. This interface acts as an abstraction to convert
support.converter between Java objects and JMS messages.
DestinationResol org.springframework.jms. This is an interface used by JmsTemplate for
ver support.destination resolving destination names.
DynamicDestinationResolver and
JndiDestinationResolver are the default
implementations of this interface.

In the following sections, I will explain some of the classes listed in Table 1 (such as JmsTemplate,
DestinationResolver, and MessageConverter) in more detail.

JMSTemplate
JmsTemplate provides several helper methods to perform basic operations. To get started using
JmsTemplate you will need to know which JMS specification is supported by the JMS provider. JBoss AS
4.0.2 and WebLogic 8.1 servers support the JMS 1.0.2 specification. WebLogic Server 9.0 includes support for the
JMS 1.1 specification. JMS 1.1 unifies the programming interfaces for Point-to-Point (PTP) and
Publish/Subscribe (Pub/Sub) domains. As result of this change, developers can create a transacted session, then
receive a message from a Queue (PTP) and send another message to a Topic (Pub/Sub) within the same JMS
transaction. JMS 1.1 is backwards-compatible with JMS 1.0 so the code written for the JMS 1.0 specification
should still work with version 1.1.
JmsTemplate provides various methods to send and receive the messages. Table 2 shows a list of some of these
methods.

Table 2. JMS template methods

Method Name Function


send Send a message to the default or specified destination. JmsTemplate contains
send methods that specify the destination using a javax.jms.Destination
object or using JNDI lookup.
receive Receive a message from the default or specified destination, but only wait until a
specified time for delivery. We specify the timeout via the receiveTimeout
attribute.
convertAndSend This method delegates the conversion process to an instance of
MessageConverter interface and then sends the message to the specified
destination.
receiveAndConvert Receive a message from the default or specified destination and convert the
message to Java object.

Destinations are stored and retrieved using a JNDI context. When configuring a Spring application context, we
use the JndiObjectFactoryBean class to get references to JMS destinations. The
DestinationResolver interface is used to resolve a destination name to a JMS destination, which is helpful
when the application has lot of destinations. DynamicDestinationResolver (a default implementation of
DestinationResolver) is used to resolve dynamic destinations.

The MessageConverter interface defines a contract to convert Java objects into JMS messages. By using the
converter, application code can focus on the business objects and not bother with the inner details of how it's
represented as a JMS message. SimpleMessageConverter (and SimpleMessageConverter102) are
default implementations of MessageConverter. They are used to convert a String to a JMS
TextMessage, a byte array (byte[]) to a JMS BytesMessage, a Map to a JMS MapMessage, and a
Serializable object to a JMS ObjectMessage, respectively. You can also write your own custom
implementations of MessageConverter to convert XML documents into a TextMessage object using an
XML binding framework such as JAXB, Castor, Commons Digester, XMLBeans, or XStream.

Sample Application
I will use a sample loan application processing system (called LoanProc) to demonstrate how to use Spring in a
JMS application. As part of the loan processing, LoanProc sends loan details (loan ID, borrower name, borrower's
SSN, loan expiration date, and loan amount) to request the credit history details from the AUS system. To keep it
simple, we will get credit history details based on two loan parameters: credit score (also known as FICO score)
and loan amount. Let's assume that the business rules for processing a credit check request are as follows:

1. If the loan amount is equal to or less than $500,000, then the borrower must have at least a "Good" credit
(i.e., the borrower's FICO score is between 680-699).
2. If the loan amount is greater than $500,000, the borrower must have a "Very Good" credit, meaning
his/her credit score is more than 700.

Loan Application Use Case


The use case for the credit request process consists of the following steps:

1. The user enters loan details on the loan application web page and submits the loan application.
2. Then the program sends loan details to the AUS system to get credit history details. This is done by
sending the request to a message queue called CreditRequestSendQueue.
3. The AUS system picks up loan details from the queue and uses loan parameters to retrieve credit history
information from its database.
4. Then AUS creates a new message with its findings on the borrower's credit history and sends it to a new
message queue called CreditRequestReceiveQueue.
5. Finally, LoanProc picks up the response message from receive queue and processes the loan application to
determine if the application can be approved or denied.

In the sample application, both message queues are configured in the same JBoss MQ server. The use case is
represented in the Sequence Diagram in Figure 1.

Technologies
Table 3 below shows the different technologies and open source frameworks I used in the sample application
listed by the application tier.

Table 3. Frameworks used in the JMS application

Tier Technology/Framework
MVC Spring MVC
Service Spring Framework (version 2.1)
JMS API Spring JMS
JMS Provider JBoss MQ (version 4.0.2)
JMS Console Hermes
IDE Eclipse 3.1

JMS Resource Setup Using Hermes


To process a message asynchronously, first we need the message queues to send and receive messages. We can
create a new message queue using the configuration XML file in JBoss and then browse the queue details using a
JMS console. Listing 1 shows the XML configuration snippet for the JMS configuration. (This should be added to
the jbossmq-destinations-service.xml file located in the %JBOSS_HOME%\server\all\deploy-hasingleton\jms
directory.)

Listing 1. Configuration of the JMS queue in JBoss MQ Server

<!-- Credit Request Send Queue -->


<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=CreditRequestSendQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>

<!-- Credit Request Receive Queue -->


<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=CreditRequestReceiveQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>

Now, let's look at how to browse a message queue using a JMS tool called Hermes. Hermes is a Java Swing
application that can be used to create, manage, and monitor JMS destinations in JMS providers such as JBossMQ,
WebSphereMQ, ActiveMQ and Arjuna servers. Download Hermes from its website and extract the .zip file to a
local directory (for example, c:\dev\tools\hermes). Once it's installed, double-click the file hermes.bat (located in
the bin directory) to launch the program.

To configure JBossMQ server in Hermes, refer to this demo on the Hermes website. It has excellent step-by-step
visual instructions for JBoss MQ configuration. Enter the following information when configuring a new JNDI
initial context.

• providerURL = jnp://localhost:1099
• initialContextFactory = org.jnp.interfaces.NamingContextFactory
• urlPkgPrefixes = org.jnp.interfaces:org.jboss.naming
• securityCredentials = admin
• securityPrincipal = admin

Enter queue/CreditRequestSendQueue and queue/CreditRequestReceiveQueue when you


create new destinations. Figure 2 shows the main screen of the JMS console with the new message queues created
for the sample JMS application.

Figure 3 below shows a screenshot of the Hermes JMS console with message queue details after sending a few
messages to CreditRequestSendQueue from the message sender class. You can see there are five messages
in the queue and the console shows the message details such as message ID, message destination, time stamp, and
the actual message.

These queue names and other JMS and JNDI parameters used in the sample application are shown in Table 4.

Table 4. Spring JMS Configuration Parameters

Parameter Name Parameter Value


Initial Context Factory org.jnp.interfaces.NamingContextFactory
Provider URL localhost:8080
Initial Context Factory URL Packages org.jnp.interfaces:org.jboss.naming
Queue Connection Factory UIL2ConnectionFactory
Queue Name queue/CreditRequestSendQueue, queue/CreditRequestReceiveQueue

Spring Configuration
Now that we have the JMS destinations required to run the sample application, it's time to get into the details of
wiring the JMS components using an XML Spring configuration file (called spring-jms.xml). The components are
wired with JMS object instances using the setter injection principle in the Inversion of Controller (IOC) design
pattern. Let's look at the components in detail, showing a XML configuration snippet for each JMS component.

JNDI context is the starting point in getting the JMS resources, so first we configure a JNDI template. Listing 2
shows a Spring bean named jndiTemplate with the usual parameters required to get the JNDI initial context.

Listing 2. JNDI context template

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">


<property name="environment">
<props>
<prop key="java.naming.factory.initial">
org.jnp.interfaces.NamingContextFactory
</prop>
<prop key="java.naming.provider.url">
localhost
</prop>
<prop key="java.naming.factory.url.pkgs">
org.jnp.interfaces:org.jboss.naming
</prop>
</props>
</property>
</bean>

Next, we configure the queue connection factory. Listing 3 shows the configuration for the queue connection
factory.

Listing 3. JMS queue connection factory configuration

<bean id="jmsQueueConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>UIL2ConnectionFactory</value>
</property>
</bean>

We define two JMS destinations to send and receive the messages. Listings 4 and 5 show these details.

Listing 4. Configuration for send queue

<bean id="sendDestination"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>queue/CreditRequestSendQueue</value>
</property>
</bean>

Listing 5. Configuration for receive queue

<bean id="receiveDestination"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>queue/CreditReqeustReceiveQueue</value>
</property>
</bean>

Then we configure the JmsTemplate component. We use JmsTemplate102 for the sample application. We
use the defaultDestination attribute to specify JMS destination.

Listing 6. JMS template configuration

<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate102">
<property name="connectionFactory">
<ref bean="jmsQueueConnectionFactory"/>
</property>
<property name="defaultDestination">
<ref bean="destination"/>
</property>
<property name="receiveTimeout">
<value>30000</value>
</property>
</bean>

Finally we configure sender and receiver components. Listings 7 and 8 show the configuration of Sender and
Receiver objects.

Listing 7. JMS Sender configuration

<bean id="jmsSender" class="springexample.client.JMSSender">


<property name="jmsTemplate">
<ref bean="jmsTemplate"/>
</property>
</bean>

Listing 8. JMS Receiver configuration

<bean id="jmsReceiver" class="springexample.client.JMSReceiver">


<property name="jmsTemplate">
<ref bean="jmsTemplate"/>
</property>
</bean>

Testing & Monitoring


I wrote a test class called LoanApplicationControllerTest to test the LoanProc application. We use this
class to set the loan parameters and call the credit request service class.

Let's look at the message sender implementation using the traditional JMS development approach without the
Spring JMS API. Listing 9 shows the sendMessage method in MessageSenderJMS class with all the steps
required to process a message using the JMS API.

Listing 9. Traditional JMS implementation

public void sendMessage() {

queueName = "queue/CreditRequestSendQueue";
System.out.println("Queue name is " + queueName);

/*
* Create JNDI Initial Context
*/
try {
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.put("java.naming.provider.url","localhost");
env.put("java.naming.factory.url.pkgs",
"org.jnp.interfaces:org.jboss.naming");

jndiContext = new InitialContext(env);


} catch (NamingException e) {
System.out.println("Could not create JNDI API " +
"context: " + e.toString());
}

/*
* Get queue connection factory and queue objects from JNDI context.
*/
try {
queueConnectionFactory = (QueueConnectionFactory)
jndiContext.lookup("UIL2ConnectionFactory");

queue = (Queue) jndiContext.lookup(queueName);


} catch (NamingException e) {
System.out.println("JNDI API lookup failed: " +
e.toString());
}

/*
* Create connection, session, sender objects.
* Send the message.
* Cleanup JMS connection.
*/
try {
queueConnection =
queueConnectionFactory.createQueueConnection();
queueSession = queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queue);
message = queueSession.createTextMessage();
message.setText("This is a sample JMS message.");
System.out.println("Sending message: " + message.getText());
queueSender.send(message);

} catch (JMSException e) {
System.out.println("Exception occurred: " + e.toString());
} finally {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e) {}
}
}
}

Now, let's look at the message sender implementation using Spring. Listing 10 shows the code for the send
method in the MessageSenderSpringJMS class.

Listing 10. JMS implementation using Spring API

public void send() {


try {
ClassPathXmlApplicationContext appContext = new
ClassPathXmlApplicationContext(new String[] {
"spring-jms.xml"});

System.out.println("Classpath loaded");

JMSSender jmsSender = (JMSSender)appContext.getBean("jmsSender");

jmsSender.sendMesage();

System.out.println("Message sent using Spring JMS.");


} catch(Exception e) {
e.printStackTrace();
}
}

As you can see, all the steps related to managing JMS resources are handled by the Spring container using the
configuration file. We just need to get a reference to a JMSSender object and then call sendMessage on this
object.

Conclusions
In this article, we looked at how the Spring framework simplifies working with asynchronous messaging
applications using the JMS API. Spring takes away all of the boilerplate code required to process a message using
JMS, such as getting a queue connection factory and creating queue and session objects from Java code and
wiring them at runtime using a configuration file. We can swap the JMS resource objects dynamically without
having to modify any Java code, thanks to the power of the Inversion of Control (IOC) principle.

Since async messaging is an integral part of a SOA framework, Spring fits nicely into the SOA toolset. Also, a
JMS management tool such as Hermes makes it easy to create, manage, and administer the JMS resources,
especially for system administrators.

Resources
• Sample code for this article
• Spring JMS documentation
• "1-2-3 Messaging with Spring JMS"
• JBoss MQ wiki

Srini Penchikala is an information systems subject matter expert at Flagstar Bank.

You might also like