Messaging systems play an increasingly important role in enterprise computing. Java™ Message Service (JMS) is the Java API that enables loosely coupled Java clients to make asynchronous interactions with messaging systems such as IBM ® WebSphere® MQ.
JMS is a low-level API that enables applications to connect to messaging
systems. To simplify coding for message consumption, J2EE 1.3 introduces a
new kind of enterprise bean based on the JMS API called the
message-driven bean (MDB). MDBs are part of the
EJB 2.0 specification, and they act as JMS message listeners in the
application server container.
MDBs are fully supported by WebSphere Application Server V5 (which is J2EE 1.3 compliant). WebSphere Studio Application Developer V5 (hereafter called Application Developer) -- provides a convenient way to create MDBs and test them in the WebSphere Test Environment (WTE). The WTE has a built-in MQ Simulator that acts as a temporary JMS provider for unit testing. However, the MQ Simulator cannot be used by an actual application server, which requires a production JMS provider to be configured. Some examples of JMS providers that can be used for production are the WebSphere Embedded Messaging Server and WebSphere MQ (previously known as MQSeries). The WebSphere Embedded Messaging Server is a lightweight version that does not have as many features as WebSphere MQ. External messaging systems from other vendors can also be integrated with WebSphere Application Server as JMS providers.
A recent article discusses the creation and unit-testing of MDBs in Application Developer: Developing and Testing Message-Driven Bean Applications with the MQ Simulator for Java Developers in WebSphere Studio V5 by Sheldon Wosnick.
This article, while focusing on basics of JMS and MDBs, supplements the previous two articles. It introduces the point-to-point (queue) and publish-and-subscribe (topic) models and messages in JMS. It also shows in detail how an MDB works and how it integrates with other J2EE components, and then covers some design issues at the end of the article.
JMS is an abstraction of the interfaces and classes that enable Java messaging clients to communicate with messaging systems. Just as JDBC provides an abstract implementation to access relational database, JMS provides an abstract implementation to access messaging systems. With the help of JMS, messaging clients are portable across messaging products such as WebSphere MQ and SonicMQ.
JMS provides two types of messaging models: publish-and-subscribe (pub/sub) and point-to-point (p2p). In simple terms, pub/sub is intended for the one-to-many or many-to-many broadcast of messages, while p2p is intended for one-to-one message communication.
Publish-and-subscribe (pub/sub) model
In the pub/sub model, a messaging client (producer of the message or
publisher) sends a message through a virtual channel called Topic to
messaging clients (consumers of the message or subscribers) that subscribe
to this topic. Every consumer receives a copy of every message
automatically without having to request or poll the topic for new
messages. The pub/sub model is shown in Figure 1 below. MDBs support both
the pub/sub and p2p models. In the pub/sub model, MDBs act as the
asynchronous subscribers. They implement the
MessageListener interface, which uses the
asynchronous onMessage() callback for messages
arriving in the Topic. For details about the
onMessage() method, see
Lifecycle of MDBs. The pub/sub model also lets
subscribers poll the messages synchronously with the
receive() method.
Figure 1. pub/sub model
The pub/sub model supports Durable Subscription, which means that while a durable subscriber is disconnected from the JMS server, it is the responsibility of the server to store messages that the subscriber misses. The messaging server sends all if the unexpired messages it stores to the subscriber when it reconnects. MDBs support both the pub/sub and p2p models, so if an MDB subscribes to a topic, it has two options for durability: Durable or Nondurable. If Durable is selected, then when it reconnects, the MDB will get all of the messages that it missed during the disconnected period. If the JMS client is a durable subscriber, the messaging system stores the messages (method of storage is vendor-specific). The MQ Simulator does not support persistence, so after the WTE server is restarted, the messages will no longer be available, which is one of reasons why the MQ Simulator is not for production use.
The p2p model lets the message clients send and receive messages via a virtual channel called Queue. Here is a comparison between the p2p and pub/sub models:
- Like the Topic in the pub/sub model, a p2p queue can either push or
pull messages, depending on whether the receiver uses the asynchronous
onMessage()callback or a synchronousreceive()method. If a receiver uses the synchronous method to receive messages, it needs to poll the message from the queue. An MDB implements theMessageListenerinterface, so it uses the asynchronousonMessage()callback for messages arriving in the queue. - Messages in a p2p model are consumed once by one and only one receiver, even though a queue may have many receivers. The policies of the JMS provider determine how messages delivered to a queue are distributed to the queue's receivers. Some JMS providers use load-balancing techniques to distribute messages among receivers, while others use more arbitrary policies.
- The clients in a p2p model have an option to browse the queue before they poll the messages from the queue.
- A message in the queue is durable by default, which means that the message stays on the queue until it is picked up by one consumer.
Figure 2. p2p model
Each model has its own advantages -- the choice between them depends on your preferences and the needs of your application. If multiple MDBs listen to a queue, it is hard to predict which one will get the message, which introduces uncertainty. MDBs have their own implementation for concurrent processing (for details, see Lifecycle of MDBs). That's also why it's not a common practice to define different MDBs that listen to the same queue.
The TopicConnectionFactory interface in JMS is
used to manufacture connections to a Topic in the messaging system. It is
a type of object whose attributes can be configured by the messaging
system administrator. An MDB that works as a subscriber of a Topic binds
to a Topic object and a TopicConnectionFactory
object. The container invokes a method called
createTopicConnection() on the
TopicConnectionFactory to create a
TopicConnection instance for the MDBs to
communicate with the Topic.
The QueueConnectionFactory interface in JMS is
used to manufacture connections to a queue in the messaging system. Like
the TopicConnectionFactory interface, it is a
type of object whose attributes can be configured by the messaging system
administrator. An MDB that acts as a receiver of a queue binds to a Queue
object and a QueueConnectionFactory object. The
container invokes a method called
createQueueConnection() on the
QueueConnectionFactory to create a
QueueConnection instance for the MDBs to
communicate with the Queue.
The JMS message object is used to encapsulate the messages that message clients send or receive. It contains three parts: headers, properties, and the message data or payload.
JMS message headers
Every JMS message has a set of standard headers including
JMSDestination, JMSDeliveryMode, and so on. For
more information on these headers, see the
Sun JMS home page.
JMS message properties
JMS Message Properties can be used as additional headers that are assigned to a message. They provide more information about the message. The value of a property can be String, boolean, byte, double, int, long, or float.
A message selector is not a part of the Message object, but it is related
to the message headers and properties. A message selector lets a JMS
consumer declare which messages it wants to receive by using message
headers and properties as criteria in conditional expressions. An MDB
works as a message consumer; therefore it allows the message selector
definition. Message selectors are based on a subset of the SQL-92
conditional expression syntax, which is used in the WHERE clauses of SQL
statements. The consumers with a simple message selector like
InventoryID = 12345 will receive all messages
that have a property named InventoryID with the
value 12345.
Message payload
Messages come in various types depending on the payload they carry:
| Message type | Payload |
| TextMessage | java.lang.String |
| ObjectMessage | serializable Java object |
| BytesMessage | array of primitive bytes |
| StreamMessage | stream of primitive Java types (int, double, char, etc.) |
| MapMessage | Set of name-value pairs. The values must be Java primitives or their wrappers, such as Integer. |
While JMS providers free developers from routing the messages from producers to consumers, the responsibility for implementing the JMS clients is left to developers. In many case, significant effort is needed to create the JMS client infrastructure, such as security, transaction support, and fault-tolerance. The similar infrastructure for session and entity beans is handled by the container. Therefore, EJB 2.0 introduces the message driven bean, which consumes and processes asynchronous JMS message in the same container as session and entity beans, and benefits from the infrastructure that the container provides.
Based on the JMS API, MDBs support both pub/sub and p2p models. They act as a subscriber of a topic or a receiver of a queue, which is a message listener in the asynchronous communication model.
Compared to session and entity beans, MDBs have the following characteristics:
- Like stateless session beans, MDBs do not maintain conversational state between requests. They may have instance variables throughout the life cycle, but due to the pooling of bean instances by the EJB container, the bean instances that consume the messages may be different between requests. That is why the conversational state may not be stored properly.
- Like a stateless session bean, the EJB container maintains many bean instances of the same type in a pool. This enables concurrent message consumption and processing when several messages are delivered at the same time, which means that MDBs can deliver better performance and scalability.
- Unlike session and entity beans, MDBs do not have remote or home interfaces. An MDB is a listener and it is not a remote process call component.
- Unlike session and entity beans, MDBs do not expose any business methods that can be invoked by clients, such as a servlet, EJB, or Java application.
- Like session and entity beans, MDBs must use the JMS API manually to send messages when they need to act as a message producer. However, sending messages from MDBs is not recommended -- MDBs should delegate this task to the business logic layer.
MDBs implement both the
javax.ejb.MessageDrivenBean and
javax.jms.MessageListener interfaces. The
MessageDrivenBean interface defines the
following three methods:
public interface MessageDrivenBean{
public void ejbCreate();
public void ejbRemove();
public void setMessageDrivenContext(MessageDrivenContext mdc);
}
|
The MessageListener interface defines the
onMessage() method.
public interface MessageListener{
public void onMessage();
}
|
The EJB container is responsible for controlling the lifecycle of an MDB, which is summarized below:
- The
newInstance()method is the first method called by the container to instantiate the bean. - The container calls
setMessageDrivenContext()and provides the MDB with an instance ofMessageDrivenContext, a subclass ofEJBContextwith access to a container-provided run time context. For example, an MDB can obtain aUserTransactionby calling thegetUserTransaction()method of theEJBContextinterface. - The
ejbCreate()method is invoked after thesetMessageDrivenContext()method and then the bean instance is added to a pool for a particular MDB and is ready to consume and process messages. - When a message arrives, the bean instance is removed from the pool and
its
onMessage()method is invoked. When theonMessage()method returns, the bean instance is returned to the pool and is ready to process another message. It is the developer's responsibility to implement theonMessage()method that parses the messages and delegates the action to the business logic layer, such as a session bean. - If the container needs to reduce the size of the pool, or if the
server is shutting down, the
ejbRemove()method is invoked on an instance when it is discarded.
The lifecycle of MDBs is shown in Figure 3.
Figure 3. Lifecycle of MDBs
The container provides MDBs with the infrastructure for security. Like
session and entity beans, MDBs use the
Transaction Type element of the deployment
descriptor to declare whether the MDB is of the bean-managed or
container-managed transaction demarcation type. The application assembler
can use the Transaction Attribute to manage
transaction demarcation for EJBs with container-managed transaction
demarcation. The transaction attribute for the MDB's
onMessage() method specifies how the container
must manage transactions for the method when it is invoked as a result of
the arrival of a JMS message. However, the application assembler should
not define transaction attributes for an MDB with bean-managed transaction
demarcation.
Only the NotSupported and
Required transaction attributes may be
specified for an MDB, because there can be no preexisting transaction
context (RequiresNew,
Supports) and no client to handle exceptions
(Mandatory,
Never). The container invokes an MDB method
whose transaction attribute is set to
NotSupported with an unspecified transaction
context. If the onMessage() method invokes
another EJB, the container does not pass a transaction context with the
invocation. The container must invoke an MDB method with the transaction
attribute set to Required and with a valid
transaction context.
For example, consider an MDB that invokes many EJBs in the
onMessage() method. If the MDB's transaction
attribute is set to NotSupported, the EJBs
invoked by the MDB do not participate in a global transaction. Each EJB
will commit on its own. However, if the MDB's transaction attribute is set
to Required, then the EJBs invoked by the MDB
will participate in the same transaction. These EJBs may commit or
rollback together.
Configuring an MDB's deployment descriptor
An MDB is by definition a JMS consumer. The EJB container takes care of subscribing the bean to the desired topic or connecting it to the desired queue based on its deployment descriptor.
The following information in the deployment descriptor and its IBM extension is needed for the container to deploy the MDBs. It is application assembler's responsibility to make sure that the settings are correct according to the business needs.
Destination
A queue or a topic. An MDB either connects to a queue or subscribes to a topic. The container also needs to know how to look up the instance of the destination through the use of a JNDI name.
Connection factory
For MDBs that connect to a queue, the container needs to know how to look
up the QueueConnectionFactory instance, which
creates connections to the queue. For MDBs that subscribe to a topic, the
container needs to know how to look up a
TopicConnectionFactory instance, which creates
connections to the topic.
Durability
An MDB that subscribes to a topic can choose to use durable or non-durable subscriptions with the topic. Durability is not applicable to the message receivers of a queue. For details, see Publish-and-subscribe (pub/sub) model.
Message selector
An MDB can select the messages for consumption based on their headers and properties. for details, see Message selector.
Transaction type
Includes container-managed or bean-managed transactions.
Transaction attribute
For container-managed transaction demarcation only. The value should be
Required or
NotSupported. For details, see
Transaction.
A Queue and a QueueConnectionFactory are a pair
of objects administered by the application server. The same statement
applies to a Topic and a
TopicConnectionFactory. WebSphere Application
Server introduces a Listener Port object that simplifies the
administration and associates a connection factory, a destination, and the
deployed MDB. A listener port binds to a destination and connection
factory pair, while an MDB binds to a listener port. Furthermore, multiple
MDBs can bind to the same listener port. The association between
connection factory, destination, listener port, and MDB is shown in
Figure 4.
Figure 4. Association between connection factory, destination, listener port, and MDB
With the help of the listener port object, the deployment descriptor of an MDB does not need to specify the JNDI name of the connection factory and destination. Instead, it must specify the name of the listener port defined in the server configuration of WebSphere Application Server.
Integrating MDB into enterprise applications
MDBs offer a standard way to create a message consumer that is fully managed by the container. The bean provider can concentrate on writing the logic that performs the parsing and processing of the message. Typically, an MDB delegates the execution of business logic to some other EJB -- a session bean in most cases.
Figure 5 below shows the typical way to integrate MDBs into enterprise applications. It is easier to implement MDBs by clearly separating message and business processing. This design pattern promotes component reusability because the business logic session bean can be used by a variety of other clients, such as servlets and standalone Java applications. This programming model also reinforces the concept that the MDB acts only as an interface to the application.
Figure 5. Integrating MDB into enterprise applications
In the above design pattern, you should consider some design decisions carefully:
- Choosing the JMS provider. WebSphere Test Environment has a built-in MQ Simulator, a low-footprint native JMS provider that allows rapid turnaround for developers. However, it does not support inter-process communications. This means that a standalone Java application running outside the server JVM process cannot communicate with this JMS provider. Nor does it support persistent messages. Therefore MQ Simulator is not an option for a production environment, where you need an external JMS provider such as WebSphere MQ or the Embedded WebSphere Messaging Server, which comes with WebSphere Application Server and is a subset of WebSphere MQ with limited functionality. For example, the Embedded WebSphere JMS Messaging Server supports only JMS clients. For more information about WebSphere MQ, see the WebSphere MQ Web site.
- Selecting the messaging model. The differences between the pub/sub and
p2p models are discussed above in
Point-to-point (p2p) model. It depends on the
business scenario whether the MDB should connect to a queue or
subscribe to a topic. Messages in a queue are durable until one
consumer picks up a message and no other consumers can get the same
copy of the message. That's why no durability option is available for
an MDB that acts as a message consumer of a queue. However, you need
to specify the durability property as
Durablefor the topic subscribers (MDB listening to the Topic) if you want the messages to be available to them after they reconnect to the topic. For details, see JMS messaging models and Configuring an MDB's deployment descriptor. - Deciding the communication model. An MDB acts as a message consumer
that always consumes messages asynchronously. In some cases, an
enterprise system needs to involve other components like servlets,
session beans, or standalone Java applications that act as message
producers, message consumers, or both. Figure 6 shows a business
scenario involving submitting user feedback on a product to a
corporate web site. The HTML feedback input form invokes a servlet,
which produces a message with the destination of a JMS provider and
forwards the HTTP request to a JSP, which shows the thank-you
information to the user. The message is picked up by a MDB, which
delegates the execution of the business logic to a session bean, which
processes the feedback according to the business rules. The key point
is that the user does not need to know the result of the feedback
processing. In this case, having the servlet act as a message producer
is an appropriate design choice.
Figure 6. A scenario using a servlet as the message producer
However, servlets and session beans cannot implement the MessageListener interface, so they cannot act as asynchronous message consumers like MDBs. As a message consumer, a servlet or session bean must poll the messages from the queue or topic with the synchronous
receive()method. In the scenario above, if the servlet needs to tell the user whether the company has processed the feedback successfully as in Figure 7, it has to wait until the reply from the session bean becomes available in the queue or topic. This kind of synchronous model might hold up the user for a long time, so servlets and session beans might not be a good choice as a message consumer in this case.
Figure 7. A scenario using a servlet as the message producer and consumer
One way to work around the problem is to replace the servlet or JSP implementation with a standalone Java application that implements the MessageListener interface, as shown in Figure 8. With this asynchronous communication model, users are allowed further actions after the message is sent. Once the reply message from the company arrives in the topic or in a queue that the Java application listens to, the Java application will be notified, and its
onMessage()method will be invoked to handle the message. This asynchronous model greatly improves the scalability and usability of the application.
Figure 8. A scenario using a Java application as the message producer and consumer
- Choosing the Message type. TextMessage is the most common one to use, and is compatible with other non-JMS messaging clients. XML can be encapsulated into TextMessage, which makes the parsing process easier and faster. ObjectMessage is a good choice in some cases, but it may be incompatible with some non-JMS messaging clients.
- Using the Message Selector. A message selector will help the MDB filter out unwanted messages and lead to better performance.
- Excluding business logic from MDBs. MDBs should delegate the action to other EJBs, typically a session bean.
- Configuring appropriate transaction type and transaction attribute for MDBs. Bean-managed transactions are much more complex than container-managed transactions. If the business logic accesses multiple resources, such as an enterprise information system (EIS) and a database, you might want to start the transaction context from the MDBs to guarantee a unit of work.
MDBs are a powerful way to process inbound messages. It provides asynchronous interface to business logic and helps developers create loosely coupled applications. As a message consumer, MDBs support p2p and pub/sub models. The container takes care of the quality of service including transaction, lifecycle, connection pooling and security.
Ying Zhao is currently working as a Software Developer on WebSphere Studio Application Developer at the IBM Software Solutions Toronto Lab, Canada. Ying received her Master of Science degree in Electrical Engineering at Technion - Israeli Institute of Technology in 1999. Ying is an IBM Certified Enterprise Developer, Solution Developer, XML Developer and Specialist for WebSphere Studio Application Developer. You can reach Ying at yingzhao@ca.ibm.com.
Colin Yu is a Technical Designer on the WebSphere Business Scenarios team at the IBM Toronto Lab, Canada. Colin received a Bachelor of Engineering degree in 1995 and a Master of Applied Science degree from the University of Waterloo, Ontario in 2000. Colin is an IBM Certified Enterprise Developer and Systems Expert on WebSphere Application Server, and an IBM Certified Solution Developer on WebSphere Studio Application Developer and VisualAge for Java. You can reach Colin at coliny@ca.ibm.com.
Jane Fung works with the WebSphere Studio Application Developer Technical Support Team. Jane earned a Bachelor of Applied Science degree in Electrical Engineering at the University of Waterloo, Ontario, and is a Sun Java 2 Certified Programmer. She can be reached Jane at. jcyfung@ca.ibm.com.
Comments (Undergoing maintenance)





