Jason Westra

Subscribe to Jason Westra: eMailAlertsEmail Alerts
Get Jason Westra via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn

Related Topics: Java EE Journal, Java Developer Magazine

J2EE Journal: Article

How to Develop Message-Driven Beans

How to Develop Message-Driven Beans

This month in EJB Home I'll show you how to build a message-driven bean. Knowledge of this EJB will enhance your toolkit for developing asynchronous Enterprise Java applications - whether they're mission-critical or not.

The Enterprise JavaBeans specification 2.0 introduced another bean into the mix. One of the primary goals for the EJB 2.0 release was to define how EJB interacts with the Java Message Service (JMS), thereby defining how these Enterprise Java APIs interact within the Java 2 Enterprise Edition (J2EE) platform. Until version 2.0 of the specification was released, the defined enterprise beans, entity and session, supported only synchronous operations. However, the need to perform asynchronous processing is critical in many high-volume applications.

Years ago, for example, I worked on a messaging system for NORAD at Cheyenne Mountain in Colorado. The system needed to process thousands of messages a second to multiple subscribers. The messages needed to be in the correct order, and not a single message could be lost - not a single one! This is an example of a mission-critical messaging application.

While messaging systems are crucial to mission-critical applications, often there's less critical yet useful functionality that can be off-loaded to an asynchronous process. For instance, JMS is a practical solution for sending e-mails and logging events. Using a messaging service to perform the processing allows for quicker response time to an end user, as he or she isn't held up while the system sends the e-mail.

JMS in a Small Nutshell
Before building a message-driven bean, I'll cover some basics of the Java Message Service. JMS is an API for message-based systems. It's generic enough to allow existing messaging products like IBM's MQSeries to incorporate it into their offering, yet powerful and flexible enough to provide a rich feature set for enterprise-level messaging. JMS provides publish-and-subscribe through the "topics" and point-to-point messaging through "queues."

It also provides the ability to acknowledge that a message was received and is being processed as well as guarantee message delivery even when the receiver (e.g., durable subscriber) isn't available. Last, JMS messages can be transactional to provide reliable delivery and handling of mission-critical messages such as those in a military command-and-control system.

With that quick overview, let's dig into building our message-driven bean to see how JMS and EJB fit together.

Overview of Message-Driven Beans
A message-driven bean is a JMS MessageListener that indirectly consumes messages from its container. The container delegates a received message either to an existing method-ready instance or to a new instance allocated to handle the message. Message-driven beans are stateless; therefore, any instance may service a message equally. Likewise, similar to stateless session beans, message-driven beans are anonymous, having no identity to a client.

Why are message-driven beans so cool? Why not just use straight JMS in your EJB application? Why are message-driven beans an exceptional addition to the EJB specification? The reasons are numerous.

First, message-driven beans provide a component model around JMS messaging. They support both topics and queues, allowing publish-and-subscribe and point-to-point messaging in a component execution environment, which is particularly important for scalability and portability.

This environment promotes scalability through its efficient resource management while processing messages. When developing with message-driven beans, you take advantage of the container's ability to pool bean instances and resources such as JDBC connections. You no longer have to code your own queue and topic pooling classes.

Concerning portability, the component execution environment (e.g., container) for message-driven beans offers portable access to resource factories and resource environment entries. The container uses structural information from the bean's deployment descriptor to link references to existing resources in the environment in a portable manner. Also, with message-driven beans you don't have to develop special "start-up" classes that initialize JMS Destinations at application server start-up.

Second, message-driven beans ease the development of asynchronous systems for both clients and Bean Providers. Message-driven beans are a snap to use from clients. The client looks up the JMS Destination by using JNDI and sends the message. Since message-driven beans have no home or remote interface, they're simply represented by a topic or queue JMS Destination (see Figure 1).

Message-driven beans live by the threading restrictions imposed on all EJBs; namely, you may not start your own threads in the bean (see EJB specification 2.0 for details beyond this article). This restriction eases development of message-driven beans because the container performs concurrency control for you. You don't have to worry about developing thread-safe business logic. "How-to" Guide

Let's roll our sleeves up and build a message-driven bean. In general, developing a message-driven bean entails two steps: code the bean class and create the bean's deployment descriptor. I've chosen to use the WebLogic Server 6.0 beta (the only product offering message-driven beans at the time of this writing) to create my message-driven bean, but the component should be deployable to other EJB 2.0-compliant servers. Listing 1 contains the code of an example bean that performs remote logging. Use it as a guide as I cover the basics of coding your first bean.

Coding a Message-Driven Bean
Realizing that a message-driven bean is a JMS MessageListener, your first step should be to create a Java class that implements the javax.jms.MessageListener interface. Your bean class must be declared public and must not be final or abstract. It may implement the MessageListener interface directly or inherit from another class that implements the interface, thereby implementing it indirectly. When you implement the MessageListener interface you must code an onMessage method, which performs the business logic required to process the message. The onMessage method shouldn't throw exceptions, as they won't be handled by the client application and may cause the container to choke on ejbRemove (see below). The code in my message-driven bean example (Listing 1) is as simple as it gets. It logs to system out, which is presumably redirected to a log file of interest. Notice how onMessage has a try block that contains all code and catches java.lang.Exception to make sure nothing is thrown from the method.

Your message-driven bean must have an empty constructor, which is called from the container when it creates a new instance of the bean. Likewise, you must code an ejbCreate method that also takes no arguments. The ejbCreate method should be public and must not be static or final. These rules allow the container provider to extend your bean class, if that's how it implements its container, and allow the container to call the ejbCreate method when it creates a new instance of the bean. Finally, the ejbCreate method must declare no application exceptions in its throws clause. Clients cannot catch exceptions thrown by message-driven beans.

Last, your message-driven bean must define an ejbRemove method. The ejbRemove method follows the same rules as ejbCreate as far as the signature. If your message-driven bean has opened a resource in its ejbCreate or onMessage method, you must release the resource(s) in your bean's ejbRemove method. It's important to note that execution of ejbRemove by the container isn't guaranteed. The ejbRemove method may not be called in cases where there's an EJB server crash or a system exception is thrown back to the container. It's the responsibility of the application utilizing message-driven beans to provide a way to regain resources when necessary. It's not the responsibility of the Bean Provider to do code recovery logic. The complexity of managing a resource recovery process beckons the Bean Provider to open and close resources in onMessage rather than waiting for ejbRemove to be called. A try block with a finally clause in onMessage can ensure that the resources are closed when it finishes, no matter if an exception occurs or not. However, there's a performance penalty incurred with this approach. Pick your poison.

Creating the Deployment Descriptor
As with any EJB, you must create a deployment descriptor, which defines the structural and application assembly information of your component. This information assists the container in managing your component. Three important steps define your message-driven bean's deployment descriptor. Note: Listing 2 is an excerpt from my ejb-jar.xml deployment descriptor. Use it as a guide for creating your bean's descriptor.

First, associate your message-driven bean to a JMS Destination. To send messages to the bean, a client uses this destination rather than a home and remote interface, which don't exist in the message-driven bean's component model. When associating your bean with a queue destination, don't associate multiple message-driven beans with the same queue. JMS doesn't define how multiple message consumers will handle messages, and your application will behave strangely to say the least.

I've chosen to map my example, MessageLogBean, to a Topic. This allows multiple message-driven beans to subscribe to the topic and log to various sources, send e-mail alerts on critical messages, and so on. Topics allow for easy third-party integration. If you're interested in hearing about what's being logged as well, simply create a message-driven bean that subscribes to the same topic!

Second, map resources are needed in the message-driven bean's environment to perform its business logic (remember, resources and resource factories allow your bean code to be portable across servers). My bean has no need for resources, but if I logged to a URL instead of sysout, I'd probably map to a URLConnection factory defined in the server's available resources.

Since I used WebLogic Server, I defined a JMS Server and the Topic for my message-driven bean's destination in the server's config.xml file (see Listing 3). WebLogic Server maps the Topic to the bean in its proprietary deployment descriptor, but since this association is declarative, you may map it as you see fit for your own server.

Last, set your message-driven bean's transactional properties. While entity beans can only be container managed, message-driven beans, like session beans, may manage their own transactions. Whether your bean is container managed or bean managed has an impact on the transactional attribute for your onMessage method as well as the guaranteed delivery of your message handling.

If your bean is bean managed, remember that only the onMessage method should define transaction boundaries. It must commit or roll back the transaction before it returns. With bean-managed, message-driven beans, the act of dequeueing the message is not within transactional boundaries (EJB 2.0 specification, section 16.2.6). Therefore, message delivery isn't guaranteed with bean-managed transactions.

It's advised to use container-managed transactions instead. This lets the container assume responsibility for the transactional scope. It will manage the transaction and guaranteed message delivery for you. The act of dequeueing, calling other EJBs, and database interactions are executed within the transaction.

A message-driven bean that has a transaction type of container-managed can declare only a transaction attribute of NotSupported or Required. Distributed transaction contexts don't propagate to message-driven beans, so there's never a transaction context associated at the time of message arrival. Thus a bean either processes its message with no transaction (NotSupported) or the container creates a new transaction for the bean to process within (Required).

Wow! You're done developing your message-driven bean. That's right, there's no need to develop a remote interface or a home interface for message-driven beans.

Restrictions on Message-Driven Beans
To apply the EJB component model to JMS, certain restrictions evolved that you must be aware of. Among these restrictions is the inability to order messages. The multithreaded container model may process messages in any order, so your message-driven beans must be prepared to process messages out of order. The inability to automatically order messages is a liability for mission-critical applications such as the command-and-control system for NORAD. I see message ordering as an opening for J2EE server vendors to provide a value-added feature that distinguishes their message-driven bean implementation from others.

You shouldn't use the JMS acknowledgment API to acknowledge a message that was successfully processed. Acknowledgment is the container's responsibility. If the bean uses bean-managed transaction demarcations, the acknowledgment can't be part of the transaction.

Last, there are several restrictions on a message-driven bean's context. The container sets a MessageDrivenContext into a message-driven bean to provide the bean access to its contextual information. MessageDrivenContext is derived from EJBContext, yet many of its methods are not accessible to you because they don't make sense in the context of an asynchronous process. The container will throw an IllegalStateException if you try to call these methods, and they're deemed inaccessible by the EJB 2.0 specification.

While the API is present, you can never call getCallerPrincipal and isCallerInRole because the client security context isn't passed along with the call to the JMS Destination represented by the message-driven bean.

Similar to entity and session beans, a context's getRollbackOnly and setRollbackOnly are methods accessible only in container-managed, message-driven beans and only within onMessage. Other methods in the bean have no transaction context and will result in a thrown exception. On the other hand, the method getUserTransaction can be called only from within a bean-managed bean, a restriction also imposed on session and entity beans.

Finally, you may not call getEJBHome on the MessageDrivenContext object. Message-driven beans have no EJBHome objects, thus this method has no meaning.

Message-driven beans fill a void in the EJB architecture, which previously defined a model only for synchronous processing. This new bean provides a component wrapper around JMS's asynchronous messaging. The component model provides ease of development, ease of deployment, and the scalability and portability you've come to expect from the EJB architecture. I foresee many useful applications of the message-driven beans in the future. They'll be used to off-load simple functionality to separate processes and enable the development of asynchronous, mission-critical applications. In this article you saw how easy message-driven beans are to develop. I recommend you take advantage of them when developing asynchronous processing within your EJB application.

More Stories By Jason Westra

Jason Westra is the CTO of Verge Technologies Group, Inc. (www.vergecorp.com). Verge is a Boulder, CO based firm specializing in eBusiness solutions with Enterprise JavaBeans.

Comments (0)

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.