This article is based on EJB3 in Action, Second Editionand the book will release on October 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) ebooks and pbooks. MEAPs are sold exclusively through Manning.com. All print book purchases include an ebook free of charge. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information.
Working with Message-Driven Beans
Message-driven beans (MDBs) are EJB components designed to consume asynchronous messages. Although MDBs are intended to handle many different kinds of messages, we’ll discuss MDBs that process JMS messages because that is what MDBs are most commonly used for.
JCA connectors and messaging
Although by far JMS is the primary messaging provider for MDBs, as of EJB 2.1, it is not the only one. Thanks to the Java EE Connector Architecture (JCA), MDBs can receive messages from any enterprise information system (EIS), such as PeopleSoft or Oracle Manufacturing, not just message-oriented middleware (MOM) that supports JMS.
Suppose you have a legacy application that needs to send messages to an MDB. You can do this by implementing a JCA-compliant adapter/connector that includes support for message inflow contract. Once your JCA resource adapter or connector is deployed to a Java EE container, you can use the message inflow contract to have an asynchronous message delivered to an endpoint inside the container. A JCA endpoint is essentially the same as a JMS destination —it acts as a server proxy to an MDB (a message consumer/listener in JMS terms). As soon as a message arrives at the end point, the container triggers any registered MDBs listening to the endpoint and delivers the message to it.
For its part, the MDB implements a listener interface that is appropriate to the JCA connector/message type and passes activation configuration parameters to the JCA connector and registers as a listener to the JCA connector. JCA also enables MOM providers to integrate with Java EE containers in a standardized manner using a JCA-compliant connector or resource adapter.
For more information on JCA, visit http://java.sun.com/j2ee/connector/
When to use Messaging and MDBs
Messaging and message-driven beans are powerful concepts but they are not right for every use-case. As a rule of thumb, you should use MDBs only if you require asynchronous processing, loose-coupling, and reliability. The and part in the previous sentence is very important. Unless you need all three of these characteristics, you probably don’t need MDB.
If you simply need asynchronous processing and not reliability or loose-coupling, you should opt to use the session bean @Asynchronous annotation. The @Asynchronous annotation is not loosely-coupled since you invoke the asynchronous session bean method directly from client code. It is less obvious that session bean asynchronous methods are also not reliable. If the container crashes in the middle of asynchronous processing, the session bean is simply lost. With message-driven beans, on the other hand, the JMS message is not removed from the middleware until the MDB finishes processing the message. If the MDB suddenly crashes before finishing processing, the message is simply reprocessed when the container is ready again.
Similarly, if all you need is loose-coupling, you should take a close look at CDI events. CDI events allow message consumers and producers to be separated through elegant type-safe object-based events. Like session bean asynchronous processing, the CDI event bus is not fault-tolerant and is also not asynchronous. Note that you can combine the @Asynchronous annotation with CDI events if you need to.
When you do need asynchronous processing, loose-coupling and reliability, however, MDBs are one of the best solutions around—which is why they are so popular for system integration. Let’s take a closer look at why this is the case next.
Why use MDBs?
Given the problems that plagued EJB 2, you might question the value of EJB 3 MDBs as opposed to using some other mechanism for consuming JMS messages. The truth is that MDBs have enjoyed a reasonable degree of success even in the darkest hours of EJB. We’ll now explain why you should take a serious look at MDBs.
Your business application may require multithreaded message consumers that can process messages concurrently and maximize message throughput. MDBs help you to avoid complexity because they handle multithreading right out of the box, without any additional code. They manage incoming messages among multiple instances of beans (in a pool) that have no special multithreading code themselves. As soon as a new message reaches the destination, an MDB instance is retrieved from the pool to handle the message, as shown in figure 1. This is popularly known as MDB pooling.
Simplified messaging code
The raw JMS code to retrieve messages mirrors the JMS code to send messages and can be pretty verbose and error prone. MDBs relieve you from coding the mechanical aspects of processing JMS messages—tasks such as retrieving connection factories or destinations, creating connections, opening sessions, creating consumers, and attaching listeners. All of these tasks are handled behind the scenes for you. In EJB 3, using sensible defaults for common circumstances eliminates most of the configuration. In the worst-case scenario, you’ll have to supply configuration information using simple annotations or through the deployment descriptor.
Robust message processing
Reliability is a critical hallmark of MDBs. All MDBs use transactions and message acknowledgement by default. What this means is that messages are not removed from the message server unless an MDB message listener method completes normally. If an unexpected error occurs during message processing, the transaction is marked for rollback and the message receipt is not acknowledged. Because MDBs use JTA transactions, any database changes made during message listener processing is also automatically rolled back. As a result, the un-acknowledged message can be re-processed by the MDB cleanly without your having to do any work. In case of successful message processing, database changes are committed and the message is removed from the message server in an atomic fashion.
Starting message consumption
To start picking up messages from the shipping request queue, someone needs to invoke the appropriate method in your code. In a production environment, it is not clear how this will be accomplished. Starting message consumption thought a user-driven manual process obviously is not desirable. In a server environment, almost every means of executing the method on server startup is highly system dependent, not to mention awkward. The same is true about stopping message processing manually. On the other hand, registered MDBs would be bootstrapped or torn down gracefully by the container when the server is started or stopped.
Messaging is an extremely powerful technology for the enterprise, and it helps build loosely integrated reliable systems. JMS allows you to use message-oriented middleware (MOM) from enterprise Java applications without being bound to vendor-specific APIs. Using the JMS API to build a message consumer application can be time consuming, and message-driven beans (MDBs) make using MOM in standardized manner through Java EE extremely easy and robust.