JMS Messaging Using GlassFishCommunication between distributed heterogeneous systems has become an inevitable requirement for today's e-business. The advent of messaging standards like Java Messaging Service (JMS) has now made it easy to develop loosely coupled, distributed systems communicating synchronously or asynchronously to exchange business data and events.
This article outlines details of messaging in general and enterprise messaging in particular using JMS and Message-Driven Beans (MDB). The messaging capabilities of the GlassFish application server as well as its configuration setup are covered in this article. In order to better explain these technologies, a simple yet realtime use case and its implementation details are also discussed here.
In simple terms, messaging is the communication between two parties. Enterprise messaging can be defined as the communication between two software components or even two applications. Messaging is as simple as sending a physical mail, where the mail sender prepares the message, gives the correct postal address, and selects the right postal service. In enterprise messaging, also, there will be a message sender who will send messages in specific format to a destination. The destination will be message-oriented middleware or a message queue that helps to exchange messages. Also, there will be message consumers that receive messages from the destination, either synchronously or asynchronously. It is not mandatory that the sender and the receiver be available at the same time; nor do they need to know each other to exchange messages.
One of the key benefits of a messaging system is that it keeps distributed systems loosely coupled. Loose coupling makes messaging solutions different from other tightly coupled communication solutions like Remote Method Invocation (RMI), CORBA, etc.
Java Messaging Service is a messaging API based on the Java 2 Platform, Enterprise Edition (Java EE). It defines a set of common interfaces for creating, sending and receiving messages.
JMS supports two messaging models:
The point-to-point messaging model relies on the concept of message queues, wherein messages will be addressed to a specific destination called a queue. A receiver consumes a message from this queue, processes it, and acknowledges its receipt. Key features of the point-to-point messaging model are:
In the publish-subscribe model, messages will be addressed to a destination called a topic. Here, the message producer publishes messages, and consumers subscribe to messages. The key features of this messaging model are:
JMS supports five message types:
java.lang.String object.Serializable Java object.A message-driven bean is an enterprise that which helps to
processes messages asynchronously. An MDB acts as a listener for JMS
messages. JMS clients can't locate MDBs and invoke methods
directly; instead the client sends messages to the destination to
which the MDB is listening. The EJB container calls an MDB's
onMessage method upon receiving a message. This method
normally casts the message to one of the five JMS message types and
handles it as per the application's business logic. MDBs work in
asynchronous mode and are stateless and transaction-aware. These
features make MDBs highly scalable and offer a robust solution for
enterprise messaging.
GlassFish is an open source application server for developing and deploying Java EE applications and web services. This server is compliant with Java Enterprise Edition 5 (Java EE 5), and is in fact the reference implementation of Java EE 5.
The GlassFish server offers tremendous support for JMS messaging, by offering a fully integrated JMS provider. The Java Message Service API is implemented by integrating Sun Java System Message Queue software into GlassFish, providing transparent JMS messaging support.
GlassFish supports two JMS resources: the connection factory and destination resources.
A connection factory is an object used by JMS clients to create a connection to a JMS provider. Connection factories can be of three types:
A destination is the object used by JMS message producers to post the message to and the source from which JMS message consumers consume the message. Supported destination types are:
Following are some of the JMS connection features supported by GlassFish.
A GlassFish server automatically pools JMS connections. The user
can set connection pool properties using the GlassFish admin
console or asadmin commands. Connection pool details
are configured while creating connection factories. Some of the
connection pool parameters supported by GlassFish are:
Initial and minimum pool size: Indicates the number of initial connections in the pool. This is also the minimum number of connections set for the pool.
Maximum Pool Size: The maximum number of connections available in the pool.
Pool resize quantity: The number of connections to be removed when the pool reaches idle timeout.
Idle timeout: The maximum time that a connection may remain idle in the pool.
Max wait time: The maximum wait time before a connection timeout is sent.
Failure action: In case of a failure, the connection can be closed and reconnected.
Transaction support: The level of transaction support. Supported transaction types are "local transaction," "XA transaction," and "no transaction."
Connection validation: If this property is selected, connections will be validated before passing them on to the application.
This feature enables the application server to reconnect to the message broker if the connection is lost. If reconnection is enabled and if the primary message broker goes down, then the application server will try to reconnect to another available broker. The user can configure the number of retries and the time interval between retries.
In GlassFish, the connection factory and destination can be accessed in two ways: by using Java Naming and Directory Interface (JNDI) lookup or using annotations.
JMS clients can use the JNDI API to look up connection factories and message destinations.
InitialContext jndi = new InitialContext();
// Lookup queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Lookup queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
Annotations are a declarative style of programming introduced in Java SE 5.0. Annotations are like metatags that can be applied to classes, constructors, methods, variables, etc.
The annotation @Resource is used for looking up connection
factories and destinations. In a web application, if this
annotation is placed on a variable, then the servlet container will
inject the requested resource; i.e., the annotated variable will be
pre-populated with an appropriate value before serving the
request.
@Resource(name="connFactory", mappedName="webTrackerConnFactory")
private QueueConnectionFactory qFactory;
@Resource(name="jmsQueue", mappedName="webTrackerQueue")
private Queue queue;
So far we have discussed how JMS and MDB work together to facilitate asynchronous messaging. We have also seen GlassFish's capabilities and the JMS support it provides. Now we will see how we can make these technologies work with the help of a realtime use case called "Web Access Tracker."
Web Access Tracker helps site administrators or business users to monitor user request statistics like the number of visitors on a day, frequently accessed pages, page/service requested time, request serving time, etc. In the following section, I will explain a solution to collect the web access information for each page accessed by the user, without compromising on the performance of the actual application. You can download the full source code of this example, as well as its deployable binary file, from the link in the Resources section.
A JMS-based messaging solution has been selected to capture and process web access data with the help of other technologies like Java Architecture for XML Binding (JAXB), Java Server Faces (JSF), servlet filters, etc. GlassFish provides support for JMS messaging and also for deploying the application.
The demo application uses a servlet filter to intercept user requests and to extract required information from the request header. This information will be then posted to a message queue as a JMS message. Messaging is done asynchronously through a message-driven bean, which is the consumer for JMS messages. The data collected from the message will be persisted to an XML data store. JAXB is the technology of choice for accessing and storing data into XML files. User interfaces are developed using JSF.
A servlet filter performs filtering tasks on either the request
to a resource, the response from a resource, or both. You can
write your own filter by implementing the Filter
interface. The main method in this interface is the
doFilter method, which performs the core filtering
action. You should also implement the init and
destroy methods in your filter class. These methods
are invoked by the servlet container to initialize and destroy
filters.
The sample application uses a servlet filter named
WebTrackerFilter, which captures information pertaining to
the request; say, the URL of requested page, host IP, requested time,
etc. The information collected from the request will be sent as a
Data Transfer Object (DTO) to the message dispatcher for further
processing.
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws java.io.IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)
request;
HttpServletResponse httpServletResponse = (HttpServletResponse)
response;
// Extract required header parameters and set it in DTO
RequestData data = new RequestData();
data.setQueryString(request.getQueryString());
data.setRemoteAddress(request.getRemoteAddr());
data.setRequestURI(request.getRequestURI());
data.setResponseTime(elapsedTime);
data.setUserAgent(request.getHeader("User-Agent"));
// send the data to message dispatcher
messagedispatcher.sendRequestData(data);
chain.doFilter(httpServletRequest, httpServletResponse);
}
We have seen how to program the custom filter, so now we need to
configure the filter class in your web application deployment
descriptor; i.e., the web.xml file. The filter class can be
configured by adding the <filter> element in the
deployment descriptor file. This element creates the name of the
filter as well as the name of filter implementation class. The
servlet/URL patterns can be mapped to the filter using the
<filter-mapping> element. This causes the
WebTrackerFilter to be called for all requests whose URL pattern is
*.html. The following elements show how to configure
WebTrackerFilter.
<filter>
<filter-name>WebTrackerFilter</filter-name>
<filter-class>demo.webtracker.filter.WebTrackerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WebTrackerFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
The request information is now available with the
WebTrackerFilter; the next step is to develop a JMS client
to post this data to the message queue as a JMS message. A
point-to-point model has been selected for messaging.
The code snippet for developing a typical JMS client is shown
below. You need to perform the JNDI lookup of connection factory
and destination using the InitialContext, which
retrieves the JMS-administered objects from the JNDI tree.
Alternatively, you can use annotations to inject JMS
resources during runtime. The webTrackerConnFactory and
webTrackerQueue used in the code are the JNDI names of the
connection factory and destination, respectively. The client assumes
that these resources are already configured in GlassFish, hence the
configuration needs to be done prior to the execution of this code.
Refer to the section "Configuring JMS
Resources in GlassFish" to see how to configure JMS resources
in GlassFish.
A Connection to the JMS service provider can be created
using the createQueueConnection method on the connection
factory. The connection provides access to the underlying message
transport.
The next task is to create a JMS Session using the
createSession method on the Connection object. A session is
a single threaded context for producing and consuming messages. The
first argument to createSession decides whether the
JMS session is transacted or not and the second argument indicates
the message acknowledgment mode. In this sample, the session is
not transacted and it automatically acknowledges on receiving
messages.
Also, you need to create a MessageProducer for sending
messages to the destination. The final step is to create the JMS
ObjectMessage and set the request data as the message.
This message is then dispatched to the message queue.
// Get queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Get queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
// Get queue connection
QueueConnection qConn = (QueueConnection)qFactory.
createConnection();
// Get session
Session session = qConn.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Set the JMS message
ObjectMessage msg = session.createObjectMessage();
msg.setObject(data);
// Send JMS message
session.createProducer(jmsLoc.getMessageQueue()).send(msg);
A message-driven bean will be the listener for JMS messages.
When the messages reach the queue, the container calls the MDB's
onMessage() method. This method casts the message to
the original message type (i.e., ObjectMessage), which
will be then formatted and persisted to the data store. The MDB
implements the MessageDrivenBean interface and optionally,
the MessageListener interface for the message type it
supports; i.e., if the bean supports JMS, the interface to be
implemented will be javax.jms.MessageListener.
Here's the code that receives a JMS message:
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
RequestData data = (RequestData)((ObjectMessage)message).
getObject();
// persist data
}
}
If you are using Java EE 5, the MDB can be annotated with the
@MessageDriven annotation. This annotation contains a
mappedName element that specifies the JNDI name of the message
queue to which the bean will listen. The MDB can use a
@Resource annotation to inject a
MessageDrivenContext resource. In this case, you don't
have to declare the bean details in the EJB deployment descriptor.
The first few lines of a MDB are shown below:
@MessageDriven(mappedName = "webTrackerQueue")
public class WebTrackerEJB implements MessageListener {
/** Context for the MDB. */
@Resource
private MessageDrivenContext mdbContext;
.........
The data captured from the user request can be persisted to the data store. In this sample, an XML data store has been selected because of its simplicity. JAXB is the technology of choice for unmarshalling and marshalling the XML document. JAXB provides a convenient way to access and process XML content using Java objects by binding XML schema to a Java representation. The Java object representation of XML can be created using the following steps:
XJC command to generate the required
Java files for the supplied XSD. The XJC.bat file is included in
the <GlassFish_Root>/bin directory.Unmarshalling is the process of creating an object tree from an
XML document. To unmarshal an XML document, you need to create a
JAXBContext. The JAXBContext provides an
abstraction for managing the XML/Java binding information. The Java
package containing the schema-derived classes should be passed as
an argument to create JAXBContext. The XML unmarshaller created
using JAXBContext can be used for retrieving the root
element of the XML document.
Here's the code for unmarshalling the XML data file:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
//create UnMarshaller
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement rootElement = (JAXBElement)unmarshaller.unmarshal
(new FileInputStream(xmlDataStorePath));
// Get the root element
WebAccess access = (WebAccess)rootElement.getValue();
Marshalling creates an XML document from the content tree. To
marshal an XML document, you need to create a
JAXBContext.
Here's the code snippet for marshalling the XML data file:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
//create a Marshaller and marshal to webAccessLog.xml
Marshaller marshaller = jc.createMarshaller();
// Converts java object to XML data
marshaller.marshal(accessData, new File(xmlDataStorePath));
If a JMS client wants to send messages to a destination, the
resources like connection factory and destination need to be
configured in GlassFish. These resources can be created either
through the admin console of GlassFish or through the
asadmin commands.
JMS connection factories allow an application to create other JMS objects programmatically. Use the following steps to configure a connection factory using the admin console:
QueueConnectionFactory.The JMS destination serves as the repositories for messages. Use the following steps to configure the message queue using the admin console:
Queue, as we are using a point-to-point messaging
model.The application is packaged as an Enterprise Application Archive (EAR) file, which consists of a web module and an EJB module. The web module deals with the user interfaces of the application. The message-driven bean and other data access classes are packaged as the EJB module. The following steps explain the deployment of the sample application in GlassFish.
xmlstore.path and point it to this
directory. You can set the system property through the GlassFish admin
console by selecting Application Server -> JVM Settings ->
JVM Options -> Add JVM Option. A sample entry looks like
-Dxmlstore.path=C:/temp/webAccessLog.xml.The ejb-jar.xml deployment descriptor used in the sample
application is shown below. You need to declare
WebTrackerEJB as a message-driven bean and the message
destination type as javax.jms.Queue. You can ignore
this configuration if you have annotated the MDB in Java EE 5
style.
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar 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/ejb-jar_3_0.xsd">
<display-name>Web Tracker App</display-name>
<enterprise-beans>
<message-driven>
<display-name>WebTrackerEJB</display-name>
<ejb-name>WebTrackerEJB</ejb-name>
<ejb-class>demo.webtracker.ejb.WebTrackerEJB</ejb-class>
<transaction-type>Container</transaction-type>
<message-destination-type>
javax.jms.Queue
</message-destination-type>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>WebTrackerEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
The sun-ejb-jar.xml deployment descriptor used in the sample application is shown below. The JNDI names of the connection factory and queue will be declared in this descriptor. You can ignore this configuration if you have annotated the MDB in Java EE 5 style.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.
//DTD Application Server 9.0 EJB 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>WebTrackerEJB</ejb-name>
<jndi-name>webTrackerQueue</jndi-name>
<mdb-connection-factory>
<jndi-name>webTrackerConnFactory</jndi-name>
</mdb-connection-factory>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
Now the application is deployed successfully and it is ready for testing. The home page of the application can be accessed using the following URL:
http://<host name>:<port number>/webTracker
A snapshot of the home page is shown in Figure 1.

The web access report can be viewed by clicking the Web Access Info link provided on the left navigation bar. This report is shown in Figure 2.

In this article we have explored JMS messaging and GlassFish's messaging capabilities. There is no doubt that JMS has earned wide industry support and emerged as a powerful solution for enterprise messaging. The use case explained here truly demonstrates the beauty of JMS to solve realtime issues using messaging solutions.
Deepa Sobhana is a Technical Architect currently associated with UST Global, a service based company in Kerala, India.
|
|