Asynchronous Messaging Made Easy With Spring JMSby Srini Penchikala
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.
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:
- Create JNDI initial context.
- Get a queue connection factory from the JNDI context.
- Get a
Queuefrom the queue connection factory.
- Create a
- Create a sender or receiver object.
- Send or receive a message using the sender or receiver object created in Step 5.
- 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.
The Spring framework provides a template mechanism to hide the details of Java APIs. JEE developers can use
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.
- 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.
- 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).
- 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
||This is the base (abstract) class for any exceptions thrown by Spring framework whenever there is a JMS exception.|
||These are helper classes used to simplify the use of JMS by handling the creation and release of JMS resources like connection factories, destinations, and sender/receiver objects.
||This is a callback interface used by the
||This interface acts as an abstraction to convert between Java objects and JMS messages.|
||This is an interface used by
In the following sections, I will explain some of the classes listed in Table 1 (such as
MessageConverter) in more detail.