CEP Breaking News, Articles, Feature Stories and Blog Posts

CEP on Ulitzer

Subscribe to CEP on Ulitzer: eMailAlertsEmail Alerts newslettersWeekly Newsletters
Get CEP on Ulitzer: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn

cep Authors: Tony Shan, Liz McMillan, Bob Gourley, Charles Rich, Jyoti Bansal

Related Topics: Java EE Journal, CEP on Ulitzer

J2EE Journal: Article

Event-Driven Enterprise JavaBeans

Event-Driven Enterprise JavaBeans

An event is an asynchronous notification from an external source, such as a thread or another application. Commands from a user, a timer signaling completion of a specified time period, and the arrival of a message in a queue are examples of events. Two components that are generally involved in event-based processing are the event producer and the event consumer.

The event producer generates events; the event consumer processes them by taking an action. Event-consumer functionality can be divided into two subcomponents: the listener and the consumer. The listener continuously waits for an event to occur. The moment the event occurs, the listener passes it to the consumer for processing.

The event listener's relationship to the consumer can be either one-to-one or one-to-many. The one-to-many relationship is used mostly in scenarios in which the listener listens for multiple types of events and, based on type, dispatches events to the appropriate consumers. Application requirements determine the choice of one model over the other.

Whatever the model, the event-based system allows asynchronous processing, in which the event producer doesn't have to wait for the consumer to finish processing. Listeners can collect events in a queue and process them later. The producer component doesn't have to worry about the availability of the consumer.

Figure 1 shows a basic event-producer/event-consumer model. There are two message producers; one produces messages of Type A and the other of Type B, and both put messages into the message queue Q1. One listener listens for multiple types of messages, and there are separate consumers for each message type. The listener dispatches messages to the appropriate consumers.

EJB and Event-Driven Processing
Invocation of an enterprise bean in EJB 1.1 specifications is limited to synchronous calls from a servlet or standalone Java client. EJB 1.1 standards lack the support for asynchronous invocation of EJBs on an event such as the arrival of a message or a timer alarm. This limits the use of EJB technology for back-end systems where there's no user interaction, and event-based invocation of EJBs is desirable. This takes the power of asynchronous processing away from the J2EE environment. Although EJB 2.0 specifications provide support for message events, support for timer events is missing. In addition, many application servers on the market still aren't fully compliant with EJB 2.0 standards.

In this article, I'll discuss message and timer events in detail, along with how to implement EJBs based on these two event models.

Timer-Driven Bean
Many applications need to do processing at regular intervals. An example is a system that periodically processes orders from a database and sends invoice messages to a queue for printing. This kind of application uses a timer-based processing model.

You can easily implement this in a non-EJB environment by starting a timer thread that generates timer events at regular intervals. Additional threads act as the event listener and the consumer that processes the orders. The only disadvantage is that the database and the message queue operations can't be put in a single transaction because there is no Distributed Transaction Coordinator (DTC). A great feature of J2EE application servers is the support for distributed transactions. It's advisable to use an EJB hosted under an application server to do the job and maintain transactional integrity.

To implement the same functionality in an EJB environment, the most common approach is to write a Java client that acts as the event producer and listener, running outside the application server and invoking an EJB, which consumes the event by taking an action (see Figure 2). Moving the client functionality inside the application server is generally not advised because EJB specifications don't recommend spawning user threads inside an EJB container.

This model would work fine, except that if 50 different consumer EJBs are required, this system will need a client application with 50 threads running outside the application server to act as listeners. You might wonder why the listener can't be a single thread that queues all the events and consumes them one by one by invoking the event-consumer EJB serially. Actually, this can be done, but with the loss of the parallel consumption of the events. This wouldn't be acceptable in most real-world applications. The only practical solution is to have 50 listener threads, but this too has limitations. It adds another chance for failure because each call from the client JVM is a remote call to the JVM hosting the consumer EJBs. Also, the system has to run an extra JVM.

The easiest way to avoid having a client running outside the application server in a separate JVM is to use an EJB as a user thread. Each EJB runs in a separate thread. EJB specifications prevent creation of user threads inside an EJB container. The trick is using an EJB as a user thread. Whatever functionality is provided by the external client thread can easily be moved inside the application sever using an EJB as a thread.

You can easily implement this solution by developing the Timer-Driven Bean (TDB) component. This comprises two EJBs, one acting as the timer event producer and the other acting as the consumer (see Figure 3). In this solution, the external Java client is required only to start and stop the TDB component and doesn't need to be running all the time. All remaining components run inside an application server.

Timer-Producer Bean
This EJB acts as timer-event producer (see Listing 1). (Listings 1-11 are available for download at www.sys-con.com/websphere/sourcec.cfm.). On a timer event, it invokes the onTimer() method of the timer-consumer bean. After the onTimer() method returns, it sleeps for a specified time period to generate the next event and invokes the onTimer() method on the consumer bean again. The timer-producer bean is a simple component that doesn't contain any business logic. It can be safely deployed as a stateful session bean with the transaction attribute set to TX_NOT_SUPPORTED because it has no interaction with transactional systems.

Timer-Consumer Bean
This EJB acts as timer-event consumer and contains the actual business logic (see Listing 2). In the example scenario, it would be responsible for processing orders from a database and placing invoice messages in a message queue. Because this bean may interact with transactional systems such as databases and message queues, it should be deployed as a stateful session bean with transaction attributes based on application requirements such as TX_BEAN_MANAGED.

Controller Bean
This EJB is required to shut down the timer-driven component gracefully (see Listing 3). It holds the running state of the TDB component. The timer-producer bean checks its state by querying the controller bean after every timer-event handling. If the state is set to shut down, the timer-producer bean exits the while loop gracefully.

It's important to stop the system gracefully - abruptly shutting down the application server may leave some transactions in an in-doubt state. The correct procedure is to shut down all TDB components gracefully before stopping the application server. Listing 3 shows a single static variable controlling the state of all TDB components. If you have different kinds of consumer beans and want to selectively shut them down based on type, you can maintain bean types and their corresponding state in a hashtable with the controller bean. Since the controller bean isn't involved with any transactional systems, it can be deployed as a stateful session bean with the transaction attribute set to TX_NOT_SUPPORTED.

TDB Launcher and TDB Invoker
These two classes are required to start the TDB component (see Listings 4 and 5).The TDB Launcher requires two parameters: the number of beans to start and the timer frequency. The TDB Launcher spawns multiple TDB invoker threads. The TDB Invoker creates an instance of the timer-producer bean and invokes the startBean() method on the producer. Each invoker must run as a separate thread since the startBean() method is a blocking call. Because the timer-producer bean goes into an infinite while loop, there's no reason for the invoker to wait for the startbean() method to return, so the TDB launcher exits the JVM.

TDB Shutdown Agent
This class invokes the shutdown() method on the controller bean, which sets the static variable Controller.running to false (see Listing 6). After processing each event, the timer-producer bean checks the value of Controller.running by invoking the checkRunning() method on the controller bean. As the variable is a static, the TDB shutdown agent only needs to create a single instance of the controller bean and set this variable to false in order to shut down all the TDBs.

Although the sample listings and description of TDB are based on a separate timer-producer and consumer bean, you can merge the functionality of the consumer and the producer into a single EJB.

Message-Driven Bean
Message-driven technology is most commonly used in areas where the complete processing can be broken down into smaller asynchronous processing units, each interacting with another through message queues.

A typical example is the system shown in Figure 4, in which a timer-based component (such as TDB) picks up orders from Database 1 and puts them in Q1. A message-handler component picks up order messages from Q1, generates invoices, and puts them in Q2. Another message-handler component reads invoice data from Q2, sends confirmation e-mails to customers, and updates order status in Database 2.

You can easily implement a message handler in a non-EJB environment by writing a listener and a consumer. The listener thread waits for messages and on arrival passes them to the consumer for processing. If the processing requires interaction with another transactional resource, such as a database, then the consumer running outside the J2EE environment lacks the transactional integrity provided by a DTC. To achieve transactional integrity, the message consumer should be implemented as an EJB that uses the DTC provided by a J2EE application server.

The most common approach to implementing message-driven bean (MDB) functionality in EJB 1.1-based application servers is to provide an external listener to the message queue and an EJB as a message consumer. The listener waits for a message to arrive in the queue. On message arrival, it invokes the message-consumer EJB, passing on the messageID as a parameter (see Figure 5). The listener doesn't process the message itself, but depends upon the consumer EJB to process it. Disadvantages of this type of implementation are:

  • Each message is handled twice: first when it's picked up by the listener for browsing, and then again when it's actually processed and removed from the queue by the message-consumer EJB.
  • The message-consumer EJB has to select messages from a queue based on messageID. The selectors put additional load on the messaging system and are generally slow.
  • The listener has to make a remote call to the message-consumer EJB for each message. This adds an additional opportunity for failure.
  • There is always an extra JVM for message-listener threads running on the machine.

    To solve these problems, an MDB component can be developed in a fashion similar to the TDB implementation, where an external client is required only to start and stop the component (see Figure 6). The main difference between the TDB component and the MDB component is that the latter doesn't need an event producer because the events (messages in the queue) can be generated by other applications. A message-consumer bean (MCB) can also act as a message producer, as shown in Figure 4, where the MDB sitting between Q1 and Q2 acts as message consumer as well as message producer. The components are described below.

    Message-Invoker Bean
    The message-invoker bean (see Listing 7) continuously invokes the processMessage() method of the MCB until the shutdown flag is set to true. The moment the processMessage() method returns, the invoker calls it again. This bean doesn't contain any business logic. Since it doesn't interact with any transactional systems, it can safely be deployed as a stateful session bean with the transaction attribute set to TX_NOT_SUPPORTED.

    Message-Consumer Bean
    The MCB (see Listing 8) is an EJB that acts as a message event listener and consumer; it contains the business logic of handling messages. The processMessage() method processes one message every time it's invoked and then returns. This method can also return when the receive (long timeout) method of the queueReceiver object times out because no message arrives in the queue during the specified time period. Use the receive(long timeout) method instead of the receive() method without any wait time specified, because the latter blocks forever and it isn't possible to shut down the system gracefully with no messages in the queue. When the processMessage() method returns, the message-invoker bean first checks the shutdown flag before calling the processMessage() method again. If the acceptable shutdown time for the system is 15 seconds, the timeout for the receive call should be set to 15 seconds. Note that when shutdown is called while the consumer bean is processing a message, the shutdown time will be governed by the message-processing time and not the receive timeout. The MCB interacts with external transactional systems such as databases and should be deployed as a stateful session bean with the transaction attribute based on application requirements such as TX_BEAN_MANAGED.

    Note: The timer-producer bean sleeps for some time to generate timer events, whereas the message-invoker bean doesn't need to do this. If there are no messages in the queue, the receive(long waitTime) call on the queueReceiver object blocks for the specified time period before returning. This prevents the MDB system from polling the message queues continuously without any delay, which would otherwise result in a high CPU utilization.

    Controller Bean
    The controller bean (see Listing 3) is an EJB that is required to shut down the message-driven component gracefully. It holds the running state of the MDB components, and its functionality is exactly the same as that of the TDB controller bean.

    MDB Launcher and MDB Invoker
    The functionality of MDB Launcher and MDB Invoker (shown in Listings 9 and 10) is exactly the same as that of the TDB Launcher and TDB Invoker.

    MDB Shutdown Agent
    The functionality of the MDB shutdown agent (shown in Listing 11) is exactly the same as that of the TDB shutdown agent.

    As with the TDB system, it's possible to merge the functionality of the consumer bean and the invoker bean into a single EJB for the MDB system.

    Using an EJB as a user thread, you can develop event-based systems in any EJB application server. TDB and MDB systems are two examples of event-based EJBs. The concept can be extended to more event types. Upgrades and migration to application servers based on EJB 2.0 specifications for MDB functionality can be avoided or delayed because the MDB component achieves the same functionality in EJB 1.1-based application servers.

    Additional Features
    More features can be added to this system, such as:

  • Shutdown control: This can be specific for each individual TDB/MDB or a group of TDBs/MDBs responsible for a specific task.
  • Monitoring capabilities: The system can be monitored with little enhancements. Each bean can report its statistics, such as the number of events processed, any exceptions encountered, and so on, to the controller bean, which can be displayed by a JSP.
  • Code: The code for the launcher, controller, and shutdown agent can be merged for the TDB and MDB systems.
  • Timer accuracy: If the time to process the timer event is high in the TDB system, the timer frequency will be affected by the event-processing time. To keep the timer accurate, the events can be sent to a separate queue. The queue can be either in memory or on a messaging system. In this case, the timer-producer bean will put a message in the queue instead of calling the onTimerEvent() method of the consumer bean. If an external queuing system is used, the TDB can be used in conjunction with the MDB system.
  • More Stories By Amit Poddar

    Amit Poddar is a principal engineer with a large Internet-based company. He has 10 years of IT experience with expertise in the development of networking protocols and server-side JAVA and has designed and implemented large-scale fault-tolerance systems using IBM MQSeries, Websphere, and SUN HA cluster.

    Comments (1) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.

    Most Recent Comments
    Pramod Kumar Rai 10/08/02 02:50:00 AM EDT

    It is very informative article. We invite few more articles from this noted young writer.