Skip to main content

JMS Messaging Using GlassFish

January 22, 2008

{cs.r.title}







Communication between distributed heterogeneous systems has
become an inevitable requirement for today's e-business. The advent
of messaging standards like "http://java.sun.com/products/jms/">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 "https://glassfish.dev.java.net/">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.

Messaging

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.

JMS Messaging

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:

  • Point-to-point (PTP)
  • Publish-subscribe

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:

  • One consumer per message.
  • No timing dependency between sender and receiver.

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:

  • Multiple consumers per message.
  • A timing dependency between sender and receiver; i.e., the
    consumer must be active to receive messages. However, the JMS API
    allows subscribers to create durable subscriptions to
    receive messages even if subscriber is inactive. With a durable
    subscription, the JMS provider retains the subscription's messages
    until they are received by the subscription or until the message
    expires.
Message Types

JMS supports five message types:

  • Text: A simple text message or a
    java.lang.String object.
  • Object: A Serializable Java object.
  • Bytes: A simple stream of bytes.
  • Map: A set of name-value pairs.
  • Stream: A stream of primitive values.

Message-Driven Beans

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.

JMS Support in GlassFish

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:

  • Connection factory: Used by point-to-point as well as
    publish-subscribe messaging models.
  • Queue connection factory: Used by the point-to-point
    messaging model.
  • Topic connection factory: Used by the publish-subscribe
    messaging model.

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:

  • Queue: The queue is the destination for point-to-point
    communication.
  • Topic: The topic is the destination for publish-subscribe
    communication.

Following are some of the JMS connection features supported by
GlassFish.

Connection Pool

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:

  1. 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.

  2. Maximum Pool Size: The maximum number of connections
    available in the pool.

  3. Pool resize quantity: The number of
    connections to be removed when the pool reaches idle timeout.

  4. Idle timeout: The maximum time that a connection
    may remain idle in the pool.

  5. Max wait time: The maximum wait time before a
    connection timeout is sent.

  6. Failure action: In case of a failure, the connection can be
    closed and reconnected.

  7. Transaction support: The level of transaction
    support. Supported transaction types are "local transaction," "XA
    transaction," and "no transaction."

  8. Connection validation: If this property is selected,
    connections will be validated before passing them on to
    the application.

Connection Failover

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.

Accessing JMS Resources from Application

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.

JNDI Lookup

JMS clients can use the JNDI API to look up connection factories
and message destinations.

[prettify]
InitialContext jndi = new InitialContext();
// Lookup queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
                lookup("webTrackerConnFactory");
// Lookup queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
[/prettify]

Annotations

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.

[prettify]
@Resource(name="connFactory", mappedName="webTrackerConnFactory")
private QueueConnectionFactory qFactory;

@Resource(name="jmsQueue", mappedName="webTrackerQueue")
private Queue queue;
[/prettify]

Put Messaging to Work

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.

Solution

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.

System Requirements
  1. Java EE 5
  2. GlassFish Application Server
  3. Other library files: commons-beanutils.jar,
    commons-collections.jar, and commons-logging.jar from Apache

Servlet Filter

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.

[prettify]
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);    
}
[/prettify]

Configuring Servlet Filter

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 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
element. This causes the
WebTrackerFilter to be called for all requests whose URL pattern is
*.html. The following elements show how to configure
WebTrackerFilter.

[prettify]
<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>
[/prettify]

Asynchronous Messaging

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.

[prettify]
// 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);
[/prettify]

Message-Driven Beans

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:

[prettify]
public void onMessage(Message message) {        
    if (message instanceof ObjectMessage) {
       RequestData data = (RequestData)((ObjectMessage)message).
                           getObject();
       // persist data
    }
}
[/prettify]

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:

[prettify]
@MessageDriven(mappedName = "webTrackerQueue")
public class WebTrackerEJB implements MessageListener {

/** Context for the MDB. */
@Resource
private MessageDrivenContext mdbContext;
.........
[/prettify]

Java Architecture for XML Binding (JAXB)

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:

  1. Prepare the XML structure of data store.
  2. Create the XML Schema Definition (XSD). Refer to the
    webAccess-sample.xml and webAccess.xsd files provided
    in the webtracker-src/config directory to find more details
    about the XML data store and schema definitions used in this
    sample.
  3. Use JAXB's XJC command to generate the required
    Java files for the supplied XSD. The XJC.bat file is included in
    the /bin directory.
Unmarshal XML Document

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:

[prettify]
 // 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();
[/prettify]

Marshal XML Document

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:

[prettify]
// 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));
[/prettify]

Configuring JMS Resources in GlassFish

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.

Create JMS Connection Factory

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:

  1. Log on to the GlassFish admin console.
  2. Expand the Resources -> JMSResources menu from the left-hand
    navigation bar.
  3. Select the Connection Factories node.
  4. Click the New button to create a new connection factory.
  5. Enter the following details:
    • JNDI Name as "webTrackerConnFactory". This is the unique
      JNDI name of the connection factory. JMS clients use this name to
      look up the connection factory. A JNDI name can be up to 255 characters,
      and must contain only alphanumeric, underscore, dash, or dot
      characters.
    • Resource Type as "javax.jms.QueueConnectionFactory". In
      the sample application, we have selected a point-to-point messaging
      model, so the resource type will be
      QueueConnectionFactory.
    • Description as "Webtracker Connection Factory".
  6. Click OK.
Create JMS Message Queue

The JMS destination serves as the repositories for messages. Use
the following steps to configure the message queue using the admin
console:

  1. Log on to the GlassFish admin console.
  2. Expand the Resources -> JMSResources menu from the left-hand
    navigation bar.
  3. Select Destination Resources.
  4. Click the New button to create a destination.
  5. Enter the following details:
    • JNDI Name as "webTrackerQueue". This is the unique JNDI
      name of the destination. JMS clients use this name to look up
      the message queue.
    • Physical destination name as "webTrackerQueue". This is
      the destination name in the message broker.
    • Resource Type as "javax.jms.Queue". The resource type will
      be Queue, as we are using a point-to-point messaging
      model.
    • Description as "Webtracker Queue Destination".
  6. Click OK.

Deploying Application in GlassFish

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.

  1. Log on to GlassFish admin console.
  2. Select Applications -> Enterprise Applications from the
    left-hand navigation bar.
  3. Click the Deploy button to deploy the webtracker application.
  4. Browse the "return (false);">webTracker.ear file and upload
    it.
  5. Click OK.
  6. Copy the webAccess.xml file provided in
    webtracker-src/config to any local directory. Add
    a system property called 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.

Deployment Descriptors

ejb-jar.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.

[prettify]
<?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>
[/prettify]

sun-ejb-jar.xml

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.

[prettify]
<?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>
[/prettify]

Testing the Application

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://: number>/webTracker

A snapshot of the home page is shown in Figure 1.

<br "Home page of Web Access Tracker" />
Figure 1. Home page of Web Access Tracker

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.

Web Access Report
Figure 2. Web access report

Conclusion

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.

Resources


width="1" height="1" border="0" alt=" " />
Deepa Sobhana is a Technical Architect currently associated with UST Global, a service based company in Kerala, India.
Related Topics >> Programming   |   

Comments

Want do download the source

Want do download the source file, but the link seems broken. Can someone provide the right link?

Bugfix needed in ...

Bugfix needed in demo.webtracker.util.MessageUtil.prepareWebAccessData()
avoid string out of bounds exception in string
userAgent=Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1|#]
int secondIndex = userAgent.indexOf(USER_AGENT_SEPARATOR,firstIndex);
NEW if (secondIndex == -1) {
NEW secondIndex = userAgent.length();
NEW }

Also, tip for begineers using Eclipse:
1) Import the webTracker.ear and allow it to create modules 'webTracker', 'webTrackerEJB' and 'webTrackerWeb'.
2) Add the missing demo source code from the downloadable zip
3) Add 'javaee.jar' from C:\glassfish3\glassfish\lib to projects as required
4) Let glassfish automatically load 'commons-beanutils-1.5.jar', 'commons-collections-1.0.jar' and 'commons-logging-1.0.3.jar'
by copying the jars into an "ext" folder such as
C:\glassfish3\glassfish\domains\domain1\lib\ext
5) Export the EAR, deploy and test as per instructions
6) Review glassfish logs at C:\glassfish3\glassfish\domains\domain1\logs