Add the power of asynchronous processing to your JSPs
Create custom JSP tags to use with JMS
Most JavaServer Pages (JSP) developers that I’ve worked with love JSP because it’s easy to use and a powerful way to design flexible Webpages. As passionate Web designers, most JSP developers are not interested in the details of n-tier architectures and distributed programming, let alone the intricacies and complexities of asynchronous processing with Java Messaging Service (JMS). Nevertheless, circumstances will present themselves when JSP developers may need the power of asynchronous processing. For example, suppose an online Webstore written completely with JSP needs to update its inventory after each processed sale, yet the inventory management system is across the globe. In that case, using JMS to update the inventory asynchronously may provide an attractive alternative. Situations similar to that one most likely fueled Sun to make both JSP and JMS key pieces of the Java 2 Platform, Enterprise Edition (J2EE).
So, is the answer as simple as making JSP developers master JMS? Unfortunately not. As I mentioned above, this answer will probably displease many JSP developers. Then, what is the solution?
JSP 1.1 introduced an extremely valuable new capability: the ability to define your own JSP tags, also called custom tags. You can group custom tags into tag libraries and reuse them in any number of JSP files. Custom tags allow complex programming logic to boil down to a set of simple tags, which JSP developers can easily use to develop content. Although custom tags require a bit more effort to set up than normal tags, the benefits definitely outweigh the costs. Tag libraries increase productivity by encouraging labor division between library developers and library users. Developers expert in accessing data and other services create JSP tags; Webpage authors who focus on the design of user interfaces use them.
In this article, I will design and implement two custom JSP tags that will allow JSP developers to work with JMS in the form that they are most comfortable with — tags. Plus, since all the JMS programming logic is buried deep within the custom tags, JSP developers do not need to learn JMS. That’s what I call killing two birds with one stone.
For example, using the write
custom tag that you will create in this article, a JSP developer can send/publish a message as shown below:
<jms:write destination="jms://Queue/ModiQueue" message="Hello World"/>
and can receive a message as follows:
<jms:read destination="jms://Queue/ModiQueue"/>
Background on JMS
To understand JMS, you must understand message-oriented middleware (MOM), which has grown in popularity over the last few years. Let’s take a look at two key characteristics of MOM that differentiate it from the more traditional middleware based on remote procedure call (RPC).
- MOM allows asynchronous communication between applications: When you send a letter to your friend via snail mail, you obviously do not wait for his response before doing anything else. You go on with your life. At some point, you may receive a response to your letter but, in the meantime, your actions do not depend upon it. That example demonstrates the basics of asynchronous communication and, hence, the basics of how MOM works.
- MOM allows the communicating applications to have completely separate lifetimes: Consider the following scenario: A salesperson fills in customer orders on his laptop while flying to the corporate office. As the laptop is not connected to the central computer, it cannot process the orders at that time. However, both the laptop and the central computer are equipped with MOM software. Though the laptop cannot communicate with the central computer at the time the salesperson enters the orders, the MOM on the laptop keeps track of those order messages. Once the salesperson is back in his office and hooked up to the corporate intranet, the laptop’s MOM fires off those stored messages to the MOM software on the central computer, which receives and processes those messages. The MOM ensures that the messages are not lost, delivered out of sequence, or duplicated. That second aspect of MOM makes it even more critical to successful distributed applications.
Those characteristics make MOM a popular alternative to RPC, which fails to offer either of those capabilities.
So, what does JMS have to do with MOM? JMS provides a standard Java-based interface to MOM’s message services. My simplified definition of JMS is as follows: JMS is an API used to access MOM facilities from a Java application.
Figure 1 shows the pictorial form of the above definition.
JMS supports two popular messaging styles: point-to-point messaging and publish-and-subscribe messaging.
Point-to-point messaging
In point-to-point messaging, two applications use MOM to communicate with each other, often as an asynchronous replacement for RPC. As shown in Figure 2, JMS allows multiple senders, but only one receiver in this model. A queue, which channels the messages, forms the central concept of point-to-point messaging. An application interested in sending a message begins with a queue connection factory that obtains a queue connection. That queue connection then creates a queue session, the application’s personal window into the connection. A JMS client uses that session to create a message producer, which sends messages to the queue.
On the other end, the receiving application goes through a similar sequence of obtaining a queue connection factory, a queue connection, and a queue session. The receiving application’s functions differ from the sending application in that it uses the session to create a message consumer to receive messages from the queue.
Publish-and-subscribe messaging
When multiple applications need to receive the same message(s), they use the publish-and-subscribe model. Figure 3 illustrates that model, where a many-to-many relationship exists between the message producers and the message consumers. Note, as Figure 3 illustrates, that a topic forms the central concept in that model. Instead of message senders and receivers, you have message publishers and subscribers.
A publisher uses a topic connection factory to create a topic connection. It then uses that topic connection to create a topic session, which provides the publisher with a personal window into the topic connection. The publisher can now use the topic session to create a message producer, which publishes messages to the topic.
On the other end, the subscribing application goes through a similar sequence of obtaining a topic connection factory, a topic connection, and a topic session. The subscribing application differs from the publisher in that it uses the session to create a message consumer that subscribes to messages from the topic.
Although I’ve glossed over a number of issues regarding JMS usage, what I’ve included above is all you need to know about JMS to create the custom JSP tags discussed in this article.
The JSP custom tag architecture
Many developers have written articles on creating and using custom tags, so I won’t spend much time introducing the basics of JSP custom tags. In addition, Sun provides a sample chapter on creating custom JSP tags from the book Core Servlets and JavaServer Pages, by Marty Hall. See the Resources below for references to this and other sources that cover creating custom JSP tags.
In general, a custom tag consists of three pieces:
- A tag library descriptor that maps the XML tag — JSP — to its implementation — the tag handler class. For those of you familiar with CORBA, that step resembles creating IDL interfaces.
- A tag handler class that defines the tag’s behavior. When the Web server comes across a custom tag, it relies on the tag’s handler class to complete all the work. In CORBA, that step is comparable to coding the CORBA servers that implement the IDL interfaces defined in Step 1 above.
- JSP files that use the custom tag. In CORBA, that would be analogous to creating clients that use CORBA servers’ services.
The tag developer creates the first two pieces only once. The JSP developer creates the last piece while designing the content and (re)uses the first two pieces.
Step 1: Define the tags
In this article, you will create two custom tags: write
and read
.
Write
The first tag, write
, is for sending/publishing messages to a destination (i.e., a queue or topic). The definition of this tag is shown below:
<tag>
<name>write</name>
<tagclass>JmsWriteTag</tagclass>
<info>Send/Publish a message</info>
<attribute>
<name>destination</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>message</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
The above tag definition provides the following information:
- The tag name is
write
. - The tag handler class is
JmsWriteTag
, which is thetagclass
element’s value.JmsWriteTag
must be the fully qualified class name of the tag handler implementation. I cover handler classes in the following section. - The tag has two attributes.
destination
is a mandatory attribute;message
is an optional attribute. - Both the attributes can be JSP expressions of the form
<%= expression %>
, since thertexprvalue
element’s value is true for both attributes.
The format of the destination
attribute’s value is especially noteworthy. Examine again the example I introduced earlier:
<jms:write destination="jms://Queue/ModiQueue" message="Hello World"/>
Note the format of the destination
attribute; it looks like a URL. Its value comprises three parts:
- The constant
"jms://"
- The constant queue or topic, depending on the messaging model desired
- The name of the queue or topic to use
You can specify the message to be sent/published in two ways. The first, which is obvious from the above definition, is via the message
attribute. You can also specify the message through the tag body. For example:
<jms:write destination="jms://Queue/ModiQueue">
Hello World
</jms:write>
Note that in the latter case, I did not specify the message
attribute. If I had done so, the tag handler would have ignored the body. Why? Because that’s the way I coded the handler for that tag. I’ll discuss the code later.
Read
The second tag, read
, receives messages from a destination. Its definition follows:
<tag>
<name>read</name>
<tagclass>JmsReadTag</tagclass>
<info>Receive a message</info>
<attribute>
<name>destination</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
The above definition is a simpler version of write
‘s. The handler implementation for the read
tag is JmsReadTag
. It only has one mandatory attribute, destination
. Below you will find an example of how to use the read
tag:
<jms:read destination="jms://Queue/ModiQueue"/>
Listing 1 shows the entire tag library description.
Listing 1: Tag library description
Step 2: Implement the tags
Now that you’ve defined the tags, it’s time to actually implement them. First, take a look at the tag handler class for the write
tag. When the Web server comes across the write
tag, it will call the tag handler class to complete all the work. As specified by JSP, a handler class must implement the javax.servlet.jsp.tagext.Tag
interface by making the handler extend either the javax.servlet.jsp.tagext.TagSupport
class or the javax.servlet.jsp.tagext.BodyTagSupport
class. If the handler implementation needs to manipulate the tag body — as it does in this case — it needs to extend the BodyTagSupport
class.
After creating a new instance (or reusing an old instance from a pool) of a handler class, the Web server informs the handler of all specified attributes. The handler class must follow the JavaBeans standard of naming property modifiers; that is, for an attribute called destination
, the handler class must have a setDestination()
method defined as follows:
public void setDestination(String destination);
Accordingly, the handler implementation defines a setter method for each attribute. The handler also overrides the doAfterBody()
method of the BodyTagSupport
base class. The Web server calls that method when it is ready to process the custom tag’s body. The implementation of doAfterBody()
checks to see whether the message
attribute has been specified and, if so, ignores the body. A message specified using the message
attribute takes precedence over a message specified in the body of the write
tag.
If no message
attribute was specified, then the body becomes the message as shown below:
// Was a message specified via the message attribute?
if( message == null ) {
// No.
// Get the body content
BodyContent body = getBodyContent();
// Set the message equal to this content.
message = body.getString();
}
Next, the doAfterBody()
calls the writeMessage()
private method, which sends the message off to the specified destination.
That method proceeds as follows:
writeMessage
parses the destination URL as shown below:
String[] parts = parseDestination();
To parse the URL, the writeMessage()
method calls the parseDestination()
method, which returns a string
array of two components: the destination type (queue or topic) and the destination name. If you do not format the destination according to the rules specified earlier in this article, a RuntimeException
will result.
Depending on the type of destination, writeMessage
will use the appropriate JMS messaging model (point-to-point or publish-and-subscribe).
For example, if the destination type is queue, the following code executes:
QueueConnection connection =
JMSQueueConnectionFactory.getConnection();
try
{
QueueSession session = connection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(parts[1]);
TextMessage msg = session.createTextMessage();
msg.setText(message);
QueueSender sender = session.createSender(queue);
sender.send(msg);
sender.close();
session.close();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
JMSQueueConnectionFactory.releaseConnection();
}
Note the call to the static methods getConnection()
and releaseConnection()
on the JMSQueueConnectionFactory
class. Those methods obtain and release a queue connection, respectively. Also note that the call to releaseConnection()
appears within the finally
section to ensure execution no matter what. For now, ignore those method calls. I will provide further details on the JMSQueueConnectionFactory
class in a later section.
Now that we have a queue connection, the code basically follows the point-to-point model’s standard sequence for sending a message: use the queue connection to create a new queue session; use the session to create a new queue sender; and use the sender to send the message to the queue.
Once the writeMessage()
method returns, the doAfterBody()
method returns the SKIP_BODY
constant to inform the Web server that the body has been processed.
Finally, the handler overrides the release()
method. The Web server calls release()
before reusing a handler class’s instance. release()
is responsible for cleaning up the handler instance so that the Web server can reuse the handler as a brand-new instance.
Listing 2 shows the complete implementation of the write
tag:
Listing 2: Write tag
Next, let’s take a look at the tag handler implementation for the read
tag. As this handler is similar to the write
tag’s handler, I will only discuss the differences.
Since read
‘s handler does not need to manipulate the tag body, it extends the TagSupport
class and overrides the doStartTag()
method. doStartTag()
‘s implementation simply calls the private readMessage()
method and prints the results to the page as shown below:
pageContext.getOut().print(readMessage());
readMessage()
follows the same steps as the writeMessage()
method in the write
tag handler: it parses the destination and executes some piece of code based on the destination type. For example, if the destination type is queue, the following code will execute:
String message = null;
QueueConnection connection =
JMSQueueConnectionFactory.getConnection();
try
{
QueueSession session = connection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(parts[1]);
QueueReceiver receiver = session.createReceiver(queue);
TextMessage msg = (TextMessage)receiver.receive();
message = msg.getText();
receiver.close();
session.close();
}
catch( Exception e )
{
e.printStackTrace();
}
finally
{
JMSQueueConnectionFactory.releaseConnection();
}
return(message);
The code above resembles the code executed in the writeMessage()
method, with a destination of type queue.
Again, note the calls to the static methods getConnection()
and releaseConnection()
on the JMSQueueConnectionFactory()
class. I will go into the details of those methods in the next section.
After obtaining the queue connection, the code follows the point-to-point messaging model’s standard sequence for message retrieval. After retrieving the message, readMessage()
extracts the text String
from the message and returns it to the caller — in this case, the doStartTag()
method.
After readMessage()
returns, the doStartTag()
method returns the SKIP_BODY
constant to inform the Web server to skip the tag’s body.
Listing 3 shows the complete implementation of the read
tag handler.
Listing 3: Read tag
Add a healthy dose of efficiency
Now let’s look at the mysterious JMSQueueConnectionFactory
class, which I’ve avoided so far. Two factors encouraged me to create that class:
- JMS does not specify a standard method for obtaining a queue connection factory from a JMS provider. Actually, JMS recommends that JMS providers allow the use of JNDI to gain access. Many JMS providers do not follow that recommendation yet and, as a result, most clients are forced to use provider-specific code to obtain the connection factory. I did not want that provider-specific code in the tags themselves.
JMSQueueConnectionFactory
isolates the code and, as I will discuss in the enhancements section, you can easily configure that class to allow portability across different JMS providers. - JMS specifies that obtaining a queue connection to a JMS provider may be expensive, both in terms of time and resources. That probably holds true for a majority of providers. As a result, some sort of caching of connections is prudent. The
JMSQueueConnectionFactory
class does that for you.
Also, I decided to make JMSQueueConnectionFactory
a servlet for the following reasons:
- I can instruct a Web server such as Tomcat to automatically load the servlet at startup. That ensures that the queue connection is ready when the JSP page using the custom tags needs it.
- The Web server provides me with deterministic destruction by guaranteeing to call the
destroy()
method during shutdown (unless the Web server crashes). - The JSP content developer does not have to perform any extra initialization or cleanup work.
- The custom tags do not have to complete any extra initialization or cleanup work.
The JMSQueueConnectionFactory
servlet is straightforward. The Web server calls the init()
method at startup, during which the servlet receives the queue connection factory and uses it to obtain and cache a queue connection as shown below:
public void init(ServletConfig config) throws ServletException
{
super.init(config);
try
{
fiorano.jms.rtl.FioranoInitialContext ic = null;
ic = new fiorano.jms.rtl.FioranoInitialContext();
ic.bind ();
QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory)ic.lookup("primaryqcf");
ic.dispose();
JMSQueueConnectionFactory.connection = queueConnectionFactory.createQueueConnection();
JMSQueueConnectionFactory.connection.start();
JMSQueueConnectionFactory.lockCount = new MyInteger();
shuttingDown = false;
}
.
.
.
Note that the code here is specific to Fiorano’s FioranoMQ, which is the JMS provider that I use.
Just before the Web server shuts down, it calls the destroy()
method, which will close the queue connection to ensure proper cleanup. That servlet will never invoke, so the service()
method throws a RuntimeException
.
Finally, the servlet has two static methods, getConnection()
and releaseConnection()
; the custom tags use these methods to obtain and release a queue connection, respectively. The same queue connection is reused and returned to all callers, and a simple reference-counting mechanism prevents the servlet’s premature destruction. The same connection can be returned to all callers because a JMS connection object is thread-safe, i.e., it allows multithreaded access. I have a similar servlet for topic connections called the JMSTopicConnectionFactory
.
Listings 4 and 5 show the complete implementation of those servlets.
Listing 4: JMSTopicConnectionFactory
Listing 5: JMSQueueConnectionFactory
Step 3: Test it out
To use the custom JSP tags created here, you will need a Web server that supports JSP 1.1. I generally use Tomcat 3.1, as it is a great Web server, and the price is right. I have created three JSP files, which you will need to actually use the custom tags. Test.jsp
tests the point-to-point capability, while Test2.jsp
and Test3.jsp
together test the publish-and-subscribe capability of the JMS provider — in this case FioranoMQ. If you point your browser to use Test2.jsp
, it will hang until you run Test3.jsp
in another browser because Test2.jsp
is waiting for a message that Test3.jsp
publishes. You can run Test2.jsp
in multiple browsers, and all of them will receive the message published by Test3.jsp
.
The JMSQueueConnectionFactory
and JMSTopicConnectionFactory
, servlets, and all the test JSP files are available for download in Resources. Simply download and copy a directory called javaworld under your Web apps directory. Start Tomcat and point your browser to Remember you will also need to download and install Fiorano’s FioranoMQ to run those programs. Plus you’ll have to create any queues and/or topics that you use in your JSP code. In my case, for example, I created a queue called ModiQueue
and a topic called ModiTopic
. Fiorano’s administration tool facilitates that process.
Enhancements
The tags presented here provide the basic JMS functionality you need to get your feet wet. You can add enhancements such as the ones described below to increase the capability of those tags:
- Message producers — message senders and publishers — have control over many message characteristics in JMS. Those characteristics include the message time-to-live that determines how long a message — if not delivered — stays around until it is dropped, the priority of the message, and whether the message persists in permanent storage (at least more permanent than RAM). For simplicity, the custom tags introduced in this article do not control those characteristics. However, it is not difficult to expose those characteristics as attributes of the
write
tag similar to the destination attribute. - JMS defines the concept of a transaction in both the point-to-point and publish-and-subscribe messaging models. Any application that deals with transferring data that should not be corrupted or lost should always do so under the umbrella of a transaction. As a result, both the
write
andread
tags should be enhanced to optionally use a transaction if specified. -
The
JMSQueueConnectionFactory
andJMSTopicConnectionFactory
classes contain JMS provider-specific code. Therefore, swapping out providers requires some coding. With some more design work, a properties file/XML file that allows new providers to be easily used can make those classes configurable. In essence, the provider-specific code is pulled out into yet another class, which has a well-defined interface (as defined by you). The properties file informs theJMSQueueConnectionFactory
orJMSTopicConnectionFactory
of which of those classes to use to obtain the connection factory. That is essentially the strategy pattern.
Conclusion
By using JSP content, developers can create powerful and dynamic Webpages and applications. When coupled with JMS, JSP becomes even more robust. Because the JSP architects have given us the strength of extending JMS with custom tags, using JMS with JSP is not difficult.
The next time you need to update an inventory database halfway across the world from your JSP page, and you don’t want your JSP page to wait 30 minutes for the update to finish, don’t worry. Just use the custom JSP tags that I created in this article, and welcome to the wonderful world of asynchronous programming with JMS!