C H A P T E R  5

Scenario: Web Module and Queue-mode Message-driven Bean

FIGURE 5-1 shows a J2EE application consisting of a web module front end and an EJB module with business logic. The distinguishing feature of this application is the asynchronous communication between the modules, which is initiated by the web module and processed by the EJB module.

 FIGURE 5-1 J2EE Application With Queue-mode Message-driven Bean

Diagram of the J2EE application covered in this chapter, containing a web module and an EJB module.[ D ]


The Interactions in This Application

This scenario looks at a J2EE application that implements the kind of interaction shown in FIGURE 5-1. This application is part of a retail web site. Within the web site, the asynchronous communication with a message-driven bean is used in the check-out process. The asynchronous interaction is used as follows:

1. An online shopper interacts with the front end provided by the web module. The user searches for merchandise and adds items to an online shopping cart managed by the web module. This activity opens a number of static HTML pages, servlets, and JSP pages in the module. Some of these components invoke the business methods of an EJB module, especially to obtain or record persistent data, but all of this done is synchronously--you user requests some information and then waits for the application to return before continuing. The web module covered in demonstrates this kind of application logic.

2. Eventually, your user does something that you want to handle asynchronously. A typical example is completing the checkout procedure. At this point your user has already reviewed the contents of the shopping cart, selected a shipping method, and provided a credit card number. Your application needs to complete the order process and then notify the user via email that the items are in stock and will be shipped.

3. The web module initiates this asynchronous processing by sending a message that identifies the customer and the order to a message queue. (The actual customer and order details are in an order database.) The message queue is outside the application--it is maintained by the application server.

4. A message-driven enterprise bean reads this message off the queue. The container takes the message from the queue, and relays it to the message-driven bean by invoking the message-driven bean's onMessage method. The container passes the message as an onMessage parameter.

5. The message-driven bean does not contain all the business logic to process the order. It just examines the message and initiates the necessary processing. In this scenario, the MDB initiates processing by invoking the business methods of other enterprise beans in the module. This is likely to be a typical strategy. If you are the developer of the EJB module you may well develop the session and entity enterprise beans (write their business methods) that perform the actual processing, as well as the message-driven bean.

There are other programming issues in this application, such as mapping URLs to web module resources and programming the interactions between the session and entity enterprise beans. These are covered in other scenarios that focus on those issues.


Programming the Message-driven Communication

Now that you have seen how queue-based message-driven communication can be used in a business application, you can see the programming that you, that application developer, need to do to make it work.

TABLE 5-1 Programming Required by This Application

Application Element

Setup Needed

Application Server

Set up a Queue and a QueueConnectionFactory. You do this outside the IDE, using the application server's administration tools.

Web Module

On the web component that will send the message, you declare references to the queue and queue connection factory. In the component source, you write code that uses JNDI lookup to obtain references to the queue. You also write code that formats a message and sends it.

EJB Module with Message-driven Enterprise Bean

On the message-driven bean, set up references that make the bean the destination for the queue and queue connection factory. Code the bean's onMessage method.


The sections that follow use the scenario to explain each of these items in detail.

Setting up the Application Server

The front end of your message-driven application sends messages to a message queue, and the back end, where most of the business logic resides, reads these messages from the queue. The queue itself is created and maintained by the application server. In a production environment, system administration will probably define, configure, and manage the queues.

Setting up a Queue

You can use the application server's default queue, but to be certain that there is no contention for messages, you may want to create a separate queue for you application.

In a development or test environment, you can create and manage the queue. For an example of the set up you need to perform, the steps for using the J2EE reference implementation's administration tool to add a queue to the Reference Implementation server are provided below:

1. Use the J2EE RI admin tool from the command line. Your working directory should be <j2sdkee1.3_home>/bin.

2. To add the queue:

j2eeadmin -addJMSDestination jms/MyQueue queue

3. To confirm that you have added the queue:

j2eeadmin -listJmsDestination

4. Start the RI server:

j2ee -verbose

The startup messages should show the presence of jms/MyQueue.

When your application is deployed into a production environment or a managed test environment, its queue and queue connection factory references can be linked to the queue designated by the system administrators.

Setting up a QueueConnectionFactory

To use a queue, your application needs to open a queue connection. It does this by calling the methods of a queue connection factory. (A queue connection factory is a driver for the messaging system--the application calls the JMS API methods of the connection factory, which interprets them for the particular implementation of JMS API that is installed. Each application server is likely to have its own queue connection factory, for its own implementation of the JMS APIs.)

Application servers may have default queue connection factories that are suitable for development and testing. The J2EE reference implementation, for example, comes with a default queue connection factory named jms/QueueConnectionFactory. It should be suitable for development and testing purposes, and it is used by the code in this scenario.

In a production environment, system administrators will probably configure queue connection factories that are configured for a specific environment and its security needs. When your application is deployed into a production environment or a managed test environment, it can be configured to use the queue connection factory designated by system administration.

Programming the Web Module

In this scenario, messages are sent by a servlet in the web module. To send a message, the servlet needs to use the queue and queue connection factory designated for the application. It gets references to the queue and queue connection factory from the application server environment, by means of JNDI lookup.

Like most J2EE references, these queue and queue connection factory references consist of two parts, a declared reference in the web module's deployment descriptor and JNDI lookup code in the servlet. This section looks first at the declared references and then at how the code uses the references.

The Reference Declaration for the Queue

In the Sun ONE Studio 4 IDE, reference declarations are set up as properties of the servlet. The queue reference is a resource environment reference which is set up on the web module's resource environment property editor. FIGURE 5-1 shows the values used in this scenario.

 FIGURE 5-2 Resource Environment Reference for a Queue

Screenshot of the web module node's Add Resource Environment Reference dialog.[ D ]

Notice that there is a layer of indirection here. The name set up on the Standard tab of this property editor is the name used in the JNDI lookup. But this is the name of the reference, and not the actual JNDI name. The JNDI name is set up on one of the server-specific tabs of this property editor. FIGURE 5-3 shows the reference name, "QueueName," mapped to the JNDI name "MyQueue." If you turn back to Setting up the Application Server, you will see that this is the queue created in the J2EE RI and designated for this application. When the servlet's JNDI code performs a lookup on "QueueName," it is automatically mapped to the JNDI name "MyQueue," and application server returns a reference to that queue.

 FIGURE 5-3 The JNDI Name for the Queue Reference

Screenshot of the web module Add Resource Environment Reference dialog's J2EE RI tab. The JNDI Name field has the value jms/MyQueue.

The Reference Declaration for the QueueConnectionFactory

Declaring the reference for the queue connection factory references is similar. It is a Resource Reference, and you set it up on the servlet's resource reference property editor. FIGURE 5-4 shows the values used in this scenario.

 FIGURE 5-4 Resource Reference for QueueConnectionFactory

Screenshot of the web module's Add Resource Reference dialog.[ D ]

For information on the other authorization types, see the coverage of message-driven beans in Building Enterprise JavaBeans Components.

This reference uses the same indirection as the queue references. FIGURE 5-5 shows the "defaultconnectionfactory" references mapped to the JNDI name for the J2EE RI's default queue connection factory.

 FIGURE 5-5 JNDI Name for the QueueConnectionFactory Reference

Screenshot of the Add Resource Reference dialog's J2EE RI tab.[ D ]

The JNDI Lookup Code

Like other types of J2EE references, the declared references for a JMS queue and a JMS connection factory are used in application code. The application performs JNDI lookups to obtain references to objects named in the declared references, then uses the references to request services. In this scenario, a servlet in the web module performs the JNDI lookups and uses the queue and queue connection factory references to open a connection to the queue and send a message to it.

CODE EXAMPLE 5-1 shows the code that performs the JNDI lookup, creates a message, and sends it. It is in the servlet's processRequest method. The code is commented to identify each of the operations it performs.

Note that any type of client would use similar code to perform the same operations. You could use similar code in an application client, or in an enterprise bean that was acting as a message provider.

CODE EXAMPLE 5-1 Servlet's processRequest Method

import java.io.*;
import javax.jms.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
...
 
protected void processRequest(HttpServletRequest request, HttpServletResponse				response) throws ServletException, java.io.IOException {
 
	//Delete the default method body and insert the following lines:
 
	response.setContentType("text/html");
	java.io.PrintWriter out = response.getWriter();
	Context jndiContext = null;
	javax.jms.TextMessage msg = null;
	QueueConnectionFactory queueConnectionFactory = null;
	QueueConnection queueConnection = null;
	QueueSession queueSession = null;
	Queue queue = null;
	QueueSender queueSender = null;
	TextMessage message = null;
 
	out.println("<html>");
	out.println("<head>");
	out.println("<title>Servlet</title>");
	out.println("</head>");
	out.println("<body>");
 
	try {
			// Connect to default naming service -- managed by app server
			jndiContext = new InitialContext();
	} 
`	catch (NamingException e) {
			out.println("Could not create JNDI " + "context: " + e.toString());
	}
 
	try {
			// Perform JNDI lookup for default QueueConnectionFactory and the
			// Queue created in this scenario.
			// Notice that the generic reference names are used here.
			queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup				("java:comp/env/jms/DefaultConnectionFactory");
			queue = (Queue) jndiContext.lookup("java:comp/env/jms/QueueName");
	} 
	catch (NamingException e) {
			out.println("JNDI lookup failed: " + e.toString());
	}
 
	try {
			// Use references to connect to the queue and send a message.
			queueConnection = queueConnectionFactory.createQueueConnection();
			queueSession = queueConnection.createQueueSession(false,				Session.AUTO_ACKNOWLEDGE);
			queueSender = queueSession.createSender(queue);
			message = queueSession.createTextMessage();
			message.setText("Hello World!");
			queueSender.send(message);
			}
		} 
	catch (JMSException e) {
			out.println("Exception occurred: " + e.toString());
	} 
	finally {
			if (queueConnection != null) {
				try {
						queueConnection.close();
				} 
				catch (JMSException e) {}
			}
	} // end of finally 
		// and the end of code to insert
 
 
} // end of processRequest()

For more information on creating and sending messages, see Building Enterprise JavaBeans Components.

Programming the EJB Module

In this scenario, the business logic for processing a shopper's checkout request is in the EJB module. It is initiated by a message from the web module front end. For this to work, the EJB module needs to receive the message that the web module sends to the queue.

To do this, it you create a message-driven enterprise bean that reads from the queue. This section shows you how to program a message-driven bean so that it reads from the designated queue. When the application is deployed, the container uses the queue connection factory you specify for the bean to open a connection to the queue you specify. You use references to specify the queue and queue connection factory.

The Message Driven Destination Property

The first thing you need to do is configure your message-driven bean to read messages from a specific queue. In the Sun ONE Studio 4 IDE you do this on the bean's property sheet. FIGURE 5-6 shows the property sheet for the message-driven bean used in this scenario. The Message Driven Destination property configures this bean as the consumer of a queue.

 FIGURE 5-6 Message-driven Bean Property Sheet

Screenshot of the message-driven bean's property sheet. The Message-Driven Destination property is set to Queue.

The Queue and QueueConnectionFactory References

You also need to identify the queue and the queue connection factory that will be used to open a queue connection. In the Sun ONE Studio 4 IDE, you do this on the server-specific tab for the application server you will be using. FIGURE 5-7 shows the J2EE RI properties tab for the same message-driven bean. The Connection Factory Name property has been set to the JNDI name for the default J2EE connection factory (jms/QueueConnectionFactory), and the Destination JNDI Name property has been set the JNDI name for the J2EE RI queue that you created for this application (jms/MyQueue).

 FIGURE 5-7 Message-driven Bean's J2EE RI Property Tab

Message-driven bean property sheet, J2EE RI server-specific tab.[ D ]

When this message-driven bean is deployed, the RI container will automatically open a connection to the queue you've specified, using the queue connection factory you've specified.

The tabs for the other application servers have similar properties for specifying queues and connection factories.

The onMessage Method

You add business logic to a message-driven bean by completing its onMessage method. This method is automatically invoked when the container delivers a message to the message-driven bean. In this example, the message-driven bean immediately calls the appropriate business method of another enterprise bean in the module. This is likely to be typical onMessage behavior. For more information about writing onMessage methods, see Building Enterprise JavaBeans Components.

Assembling the J2EE Application

Figure FIGURE 5-1 shows both the servlet (in a web module) and the message-driven bean (in an EJB module) in a J2EE application To put these modules into a single application, you simply create the application and add both modules to it. The two modules contain all of the information needed to deploy and execute the message-driven communication. There is no need to open the J2EE application's property sheet and perform any additional assembly work.

For information about creating an application and adding modules, see Assembling the J2EE Application.