Add concurrent processing with message-driven beans

Process a request concurrently in the J2EE framework

A concurrent program is one that can execute multiple tasks simultaneously. Concurrency improves a program's throughput, execution speed, and responsiveness. On a single processor system, concurrent programs efficiently use computer resources by overlapping slow I/O (input/output) operations with computational tasks; on a multiprocessor system, concurrency helps maximize throughput by executing tasks in parallel on different CPUs.

Concurrency can be achieved in multiple ways. In Java, concurrency is easily achieved by multithreading. Threads have a lower overhead as compared to independent processes, and Java provides built-in support for threads. Thus, concurrent programming is an integral and desirable feature for Java programs.

Many real-world applications find parallel processing either necessary or highly desirable. One such example is an application that displays the best price and availability of an item by searching multiple retailers. This article discusses various implementations of this example application, called Price Buster, in a J2EE (Java 2 Platform, Enterprise Edition) framework and describes how message-driven beans (MDBs) can effectively achieve parallel processing of a single user request.

Need for concurrent processing

To justify the need of concurrent processing for the Price Buster application, let us discuss what this application does, the desirable features, and the best way to implement it in the J2EE framework. Our sample application takes an item name or model number as user input over the Web, invokes a backend process that searches the item's price and availability from multiple retailers, formats the data, and presents it to the user.

This application's ideal implementation is one that can search many retailers in the shortest possible time. Out of the total time required to process one user request, the backend process that connects to multiple retailer systems will spend the maximum time to gather pricing information. Let's assume it takes a minimum of 15 seconds to search an item's details from a retailer. Hence, searching 10 retailers requires a minimum of 150 seconds, assuming the retailers are searched serially, one after the other. From the user's viewpoint, a response time of 150 seconds is completely unacceptable. To ensure an acceptable user response time while searching numerous retailers, the implementing technology must search in parallel rather than serially.

EJB and concurrent processing

Now let us discuss the different implementation options available if the Price Buster application needs to be implemented in the J2EE framework. The typical and most common implementation in a J2EE environment would consist of a Web module (servlets and JavaServer Pages, or JSP) that handles the session and presentation, and a J2EE module with EJB (Enterprise JavaBeans) components containing the business logic to connect and search the retailers' legacy systems, as illustrated in Figure 1. The user invokes the JSP/servlet over the Web, which, in turn, invokes a method on the search EJB component. The search EJB component gets the item's price and availability from three retailers A, B, and C by invoking retailer EJB components, one after the other. The important point to note here is that the retailers are searched sequentially. Because of the serial invocation, a minimum of 45 seconds will pass before the results display to the user.

Figure 1. Typical implementation of Price Buster application in J2EE framework. Click on thumbnail to view full-size image.

In this implementation, the user response time depends on the number of retailers searched and the time each retailer takes to respond. Although this solution is functionally correct, it has a serious design flaw. The response time increases linearly with the number of retailers searched and is a minimum of 300 seconds (5 minutes) for 20 retailers, which is not at all user friendly!

To reduce the user response time to an acceptable value and still maintain the ability to search numerous retailers, the application needs to be redesigned to use a technology that allows searching of retailer systems in parallel rather than serially. In other words, we need to add concurrency to the above implementation to get a faster response time. As mentioned earlier, in Java programs, this is easily achieved by multithreading, i.e., assigning independent tasks to multiple threads. This technique is effective when the tasks are I/O intensive instead of CPU intensive, which is the case for the Price Buster application. The retailer EJB component makes an I/O call to the retailer system, which responds in 15 seconds, at a minimum. During this time, the retailer EJB component is not doing any processing, just waiting for the response from the retailer. Hence, this application is a perfect target for a multithreaded implementation.

Here are some ways to add concurrency:

  1. The application's Web module can be modified to spawn multiple threads—let's call them worker threads—inside the JVM hosting the servlets. Each worker thread can invoke the retailer EJB component directly, with one worker thread dedicated per retailer. When invoked, the servlet breaks the input request into multiple tasks, one for each retailer, and assigns them to multiple worker threads. After assigning the tasks, the servlet thread can wait either for a specified time or until all the worker threads finish processing. This solution works perfectly fine and gives the desired results, but it deviates from J2EE's fundamental design philosophy, which targets application developers to focus on developing the business logic rather than concentrating on complex multithreading and synchronization issues.
  2. The search EJB component can be redesigned to spawn multiple threads that can invoke the retailer EJB components in parallel. Unfortunately, this is not advisable at all, because the EJB specification restricts the creation of user threads inside an EJB container. Note that the EJB server itself is multithreaded and can handle multiple client requests concurrently. The reason behind this restriction lies in the fact that J2EE is a framework for building highly scalable server-side components and is responsible for providing concurrency and other services. This architecture removes the burden from enterprise bean developers to understand complex multithreaded code. Moreover, the EJB container can manage resource allocation more effectively by controlling thread creation.
  3. Another solution implements the retailer EJB components as MDBs. This allows the search EJB component to assign tasks to multiple retailer MDBs simultaneously by sending multiple message events, which enables the retailer MDBs to process the requests in parallel. Session EJB components cannot be used this way because they are limited to synchronous invocation and must be invoked serially. On the other hand, MDBs can be invoked asynchronously on message events and hence can process requests in parallel.

Keeping in mind the EJB specification's limitations and ease of implementation, the best solution would be Solution 3, i.e., to use asynchronous invocation of retailer MDBs. Messaging is the most common and reliable mechanism for providing asynchronous communication among different system components and becomes the natural choice for J2EE applications because it is well integrated with the EJB framework with the support of JMS (Java Messaging Service) and MDBs. The rest of the article covers how concurrency can be added to EJB using MDBs.

Concurrency in an EJB environment using message-driven beans

Now I discuss in detail how we can use MDBs to add concurrency to the Price Buster application. An MDB-based solution entails two primary requirements: first, an EJB 2.0-compliant application server that supports message-driven beans and, second, a messaging software with a JMS interface, such as IBM MQSeries. It is wise to select a messaging system that is well integrated with the application server being used. The combination of WebSphere and MQSeries is one good example.

Note: If your application server is not EJB 2.0-compliant or does not support MDBs, please refer to my article, "Event-Driven Enterprise JavaBeans," in WebSphere Developer's Journal, which discusses how to develop MDBs in any EJB application server, including EJB 1.x-based servers.

In our implementation, the retailer EJB components deploy as MDBs so they can be invoked on a message event, as shown in Figure 2, instead of exposing their methods through a session EJB interface. Below, I describe this implementation's complete flow.

Figure 2. Add concurrency in a J2EE framework using MDBs. Click on thumbnail to view full-size image.

To start, the user invokes the JSP/servlet over the Web, providing the item name or model. The servlet, in turn, invokes a method on the search EJB component. The search EJB component constructs three distinct JMS messages from the input request, one for each retailer to be searched, and places them on the request queue. Then the search EJB component enters a wait mode, waiting for the response messages to arrive on the response queue (Step 2, in Figure 2). The arrival of messages in the request queue triggers the invocation of retailer MDBs. One retailer MDB is invoked per message, and all invoked MDBs start processing their messages simultaneously (3). After searching the retailer systems for price and availability information, the retailer MDBs embed the results in JMS messages and put them on the response queue (4). Remember that all the retailer MDBs process in parallel; hence, they all return the results in approximately 15 seconds, which is the minimum time to search a single retailer. The waiting search EJB component picks up all three messages from the response queue, assembles the result, and sends the data back to the invoking servlet/JSP (5), which, in turn, displays it to the user (6).

In this implementation, the minimum response time to return to the user depends only on the time required for each retailer to respond, unlike our first implementation, where the response time also depended on the number of retailers searched. Hence, even if the number of retailers increases to 10, the minimum response time will be around 15 seconds. Few milliseconds will be spent in assembling and disassembling the messages and interacting with the messaging system. This represents a major improvement over the serial processing time of 150 seconds for 10 retailers.

Design considerations

You should be aware of some design considerations when designing a concurrent application based on MDBs. When multiple users make simultaneous requests to the Price Buster application, multiple instances of the search EJB component will be active at any given time, each one serving one servlet thread. In that case, a mechanism must map the response messages from a group of retailer MDBs to the corresponding invoking search EJB component. You can achieve this in multiple ways:

1 2 Page 1
Page 1 of 2