Message-Driven Beans in WebSphere 5.0
Summary
With the release of the Enterprise JavaBeans (EJB) 2.0 specification a new category of enterprise beans was introduced - message-driven beans (MDBs). Message-driven beans have been specifically designed to process incoming JMS messages within an EJB container.
With the release of the Enterprise JavaBeans (EJB) 2.0 specification a new category of enterprise beans was introduced- message-driven beans (MDBs). Message-driven beans have been specifically designed to process incoming JMS messages within an EJB container.
Although it's been more than a year since the release of the EJB 2.0 specification, previous versions of IBM WebSphere Application Server did not provide support for it. The upcoming release of WebSphere Application Server 5.0 not only supports the
EJB 2.0 specification but all the other specifications that make up the J2EE 1.3 specification. At the time of writing of this article, only a technology preview of WebSphere 5.0 was publicly available.
You can use the preview to check out the new features that will be available in the final release of the product.
In this article we'll use it to demonstrate developing and deploying message-driven beans in WebSphere.
This is the first of a two-part series in which I will introduce message-driven beans and discuss how to develop, assemble, and deploy them in WebSphere 5.0. In this article, I will introduce concepts surrounding Java Message Service (JMS) and MDBs and
present what you need to know in order to develop your first message bean. Since MDBs are designed to handle incoming JMS messages, having a prior understanding of JMS would help in understanding MDBs better. In this article I will
provide a brief introduction to JMS.
For a more extensive treatment of this topic, refer to the resources section at the end of this article.
JMS Overview
In this section I will provide an introduction to JMS and how it relates to message-driven beans. Even if you're familiar with JMS,
I would recommend that you read this section. JMS is a standard, vendor-neutral API that Java programs can use to create, send, receive, and process messages from an enterprise messaging system. Enterprise messaging systems (sometimes referred to as message-oriented middleware) provide the infrastructure necessary to exchange messages asynchronously between distributed software applications. JMS is analogous to JDBC. Just as JDBC is a vendor-neutral API used for accessing a variety of relational databases, JMS is a vendor-neutral API used for accessing enterprise messaging systems.
JMS is supported by many commercial messaging products, including IBM MQSeries, and Progress' SonicMQ.
If you develop your messaging applications to use the JMS API, they become portable across messaging products that support JMS.
JMS Clients and Providers
Java applications that use JMS are called JMS clients and the messaging systems that handle routing and handling of messages are called JMS providers.
A JMS application is composed of one or more JMS clients and usually one JMS provider A JMS client that sends messages is
called a producer, whereas a JMS client that receives messages is called a consumer.
Although from the definition it looks like you would need two programs, a single JMS client can behave as both producer and consumer.
When a client wants to send or receive a message it needs to specify a JMS destination.
In the case of a producer the JMS destination is where the messages are sent, while in the case of a consumer it identifies the source from which messages are received.
EJBs of all types can use JMS to send messages to various destinations. Those messages are consumed by other JMS clients, including message-driven beans. An MDB is the only type of EJB that can consume messages; entity
and session beans cannot consume messages.
This is because entity and session beans respond to synchronous Java RMI calls from EJB clients and cannot be programmed to receive JMS messages.
In order to overcome this limitation, message-driven beans were introduced in EJB 2.0. JMS Is Asynchronous
One of the key differentiators of JMS messaging is that it is asynchronous. That is, a JMS client can send a message and continue with its processing without having to wait for a reply. In other words, the client
does not block for a response. This allows you to build applications where various subsystems can be loosely coupled and do not have to all be running at the same time in order for them to communicate.
These subsystems communicate through a messaging server. Because of this, if one subsystem
fails, it doesn't impede the functioning of another. Asynchronous messaging combined with guaranteed delivery and a
store-and-forward mechanism provide the high-availability capabilities required by critical business systems to continue functioning without any interruption.
JMS Messaging Models
JMS provides two types of messaging models: publish-and-subscribe (pub/sub) and point-to-point (P2P).
Pub/sub is intended for a one-to-many delivery of messages, while point-to-point is intended for one-to-one delivery of messages (see Figures 1 and 2).
In publish-subscribe messaging, one producer can send messages to multiple consumers.
The destination to which a producer sends a message is called a topic. Consumers can express interest in receiving messages from a topic by subscribing to it. If one or more consumers subscribe to a topic,
messages sent to the topic are delivered to each of the topic's consumers. Pub/sub is a push-based model, where consumers automatically receive messages without them having to poll the topic for new messages.
In point-to-point messaging, a producer can send a message to only one consumer. The destination to which a producer sends a message
is called a queue. A queue can have multiple consumers, but only one consumer receives the message. P2P can be used as either a pull-
or push-based model. Consumers either poll the queue or receive messages automatically when they arrive in the queue.
Message-Driven Beans
Having seen some of the key JMS concepts in the previous section, we're now ready to talk about message-driven beans.
In this section I'll introduce message-driven beans and what is required to develop one.
A message-driven bean is a new type of EJB component that can receive JMS messages. An MDB is a JMS client that can consume messages sent to either a queue or a topic by another JMS client (see Figure 3).
MDBs are stateless transaction-aware server-side
components that run within an EJB container. While a message bean is responsible for processing messages, the EJB container is
responsible for its environment, including transaction and resource management, concurrency control, security enforcement, and
message acknowledgement.
Message-driven beans can consume messages concurrently. That means the container can instantiate multiple instances of a message bean
to process messages in a JMS destination. An MDB can receive thousands of messages and process all the messages at the same time. The
EJB container manages resources, transactions, and security in a multithreaded environment and leaves the job of processing the message
to the MDB. On the other hand, traditional JMS clients have to custom-implement all these functions.
Message-driven beans, like entity and session beans, are enterprise beans. But message beans don't have component interfaces such
as home, remote, or local interfaces. All they have is a bean class and an XML deployment descriptor. Since they aren't
accessible using the Java RMI API and they respond only to asynchronous messages, they don't require component interfaces. The
messages processed by a message bean can come from any messaging client, such as MQSeries or SonicMQ client.
Message-driven beans are stateless; this means they cannot hold any conversational state. In this sense they are similar to stateless
session beans. Since the container could instantiate multiple instances of the message bean to process messages, they cannot hold any
conversational state.
Having laid the groundwork, we're now ready to discuss what's involved in developing message-driven beans.
MDB Semantics
Message-driven beans at the minimum have to implement the javax.ejb.MessageDrivenBean and the javax.jms.MessageListener interfaces.
In addition, each message-driven class should have one ejbCreate method that returns void and accepts no arguments. Here is what the
javax.jms.MessageDrivenBean
interface looks like:
public interface MessageDrivenBean extends javax.ejb.EnterpriseBean {
public void ejbRemove() throws EJBException; public void setMessageDrivenContext( MessageDrivenContext ctx)throws EJBException;
}
The life cycle of a message bean is simple. The container creates an instance of a message bean in three steps. First, the container
checks the message bean pool for an available instance. If it cannot find one, it creates a new instance of the message bean and adds
it to the pool. Second, the container calls the setMessageDrivenContext method to pass the context object to the instance. Third, the
container calls the instance's ejbCreate method.
The setMessageDrivenContext method is called by the bean's container to provide the bean with access to information about the
container in which it executes. In the ejbCreate method you would code logic to initialize it. Since message beans are stateless and
the ejbCreate method doesn't have any arguments, in most cases you wouldn't have any code in this method.
The ejbRemove method is invoked when a message-driven bean is removed from the pool. You would code logic to clean up any resources
you have acquired in ejbCreate.
Here is what the javax.jms.MessageListener
interface looks like:
public interface MessageListener {
public void onMessage(Message message)
}
By implementing the MessageListener interface, the bean class is telling the container that it is a message-driven bean. Whenever a
message arrives in the destination object associated with the bean, the container invokes the bean instance's onMessage method by
passing the message as an argument.
The onMessage method contains the business logic that handles the processing of the message.
A Simple Example
In this section I'm going to illustrate message-driven beans using a simple example. In the example, a simple JMS client sends a
text message to the MDB. The message bean, on receiving the text message, logs it to stdout.
Message Bean Class
In our example the com.jpeople.mdb.ejb.LogMessageBean is the message bean class.
As I mentioned, the message bean doesn't have any component interfaces.
Although this is a very simple example, it provides a good starting point for implementing your beans (see Listing 1).
In each of the methods we display a message to illustrate the life cycle of the message bean:
-
By implementing the javax.jms.MessageDrivenBean interface, our bean is now a message-driven bean.
-
By implementing the javax.jms.MessageListener interface, our bean can consume messages.
-
In the setMessageDrivenContext method, we save the context (passed as an argument in an instance variable of the bean class) in
order for other methods to use it.
-
Since this message bean is stateless, we don't have anything to initialize in the ejbCreate method. Similarly we don't have
anything to clean up in the ejbRemove method.
-
The onMessage method is where the business logic is coded. It receives the message as an argument and downcasts the message to an
object of type javax.jms.TextMessage. TextMessage is a particular type of JMS method that is used for encapsulating text messages.
The message is retrieved from the TextMessage object and is written to the stdout. Notice that we don't specify the JMS
destination anywhere in the class. The JMS destination is specified as part of the deployment descriptor and during deployment in
WebSphere. The message bean's JMS destination type can be either a queue or topic.
The Deployment Descriptor
MDBs have deployment descriptors like entity and session beans. The code snippet below shows only the portion of the deployment
descriptor relevant to our simple message-driven bean.
<enterprise-beans>
<message-driven>
<display-name >DemoMessageBean </ display-name>
<ejb-name> DemoMessageBean < /ejb-name >
<ejb- class> com.jpeople.mdb.ejb.Log MessageBean < /ejb-class >
<transaction-type > Container < / transaction-type >
< message-driven-destination >
< destination-type > javax.jms.Queue </destination-type>
</message-driven-destination >
</message-driven >
</enterprise-beans >
For each message-driven bean you need to have a <message-driven> entry. The <message-driven> entry is declared in the
<enterprise-beans> element alongside other entity and session beans. Just like session beans, it defines an <ejb-name>,
<ejb-class> and <transaction-type>. It doesn't have any component interfaces. In addition, it also needs to define
the <message-driven-destination> entry. The <message-driven-destination> element identifies whether the MDB is listening
to a queue or topic. In our example, it is listening to a queue. You notice that nowhere in the deployment descriptor do we specify
the name of the actual queue. It is specified when the MDB is deployed in WebSphere.
The JMS Client
Having discussed the implementation of our simple message-driven bean, let's look at the bean client. An MDB client can be any
messaging client. In our example, we have a JMS client that accepts a message as an argument and writes the message to a JMS
destination queue. The MDB client source is shown in Listing 2. Since this article is about MDBs and not JMS, I will not discuss the
client source in great detail.
In short, the bean client:
-
Verifies that it has been invoked with an argument
-
Obtains the QueueConnectionFactory and the Queue from the JNDI InitialContext
-
Creates a QueueConnection and a QueueSession object
-
Creates a QueueSender and a TextMessage using the QueueSession object,
-
Sets it in the TextMessage object and writes it to the queue
Deploying the Message Bean
Deploying an MDB involves creating an EJB JAR, packaging it in an enterprise application .ear file and installing it in
WebSphere. In the second installment of this article, I will discuss what is involved in packaging and deploying our simple message bean in
WebSphere.
I will also discuss how to package the JMS client as a J2EE application client and how to execute it.
If you cannot wait for the next installment of this series, download the already packaged enterprise application and client JAR
and follow the included instructions on how to install and execute the message-driven bean. For location of the download refer to the
Resources section.
Summary
In this article we've learned about developing message-driven beans.
We learned some key JMS concepts and how message beans compare to session and entity beans. Finally, we looked at what interfaces
you need to implement in order to develop message beans.