A Message-Driven Bean Example
Introduction
Session beans allow you to send JMS messages and to
receive them synchronously, but not asynchronously. To receive messages
asynchronously, a Message-driven bean is used. Message driven beans are the light weight components used for
communication via messages (e.g., email or IM messages). In
message driven beans, the messaging service is in asynchronous mode because the
user is not intended to get the instant result.
Message-driven beans can implement any messaging type. Most commonly, they
implement the Java Message Service (JMS) technology.
Message-driven beans have the following
characteristics:
-
A message-driven bean’s instances retain no data
or conversational state for a specific client i.e. they are stateless.
-
A single message-driven bean can process messages
from multiple clients.
-
They are invoked asynchronously.
-
They can be transaction-aware.
-
They do not represent directly shared data in the
database, but they can access and update this data.
-
A message-driven bean has only a bean class i.e. unlike
a session bean, the clients don't access message-driven beans through
interfaces.
-
They don't have the remote or local interfaces that
define client access.
The given diagram shows the working process of a
Message driven bean.

In Message driven beans (MDB), the client components
don't locate message-driven beans and invoke methods directly. Instead, The JMS clients send messages to message queues
managed by the JMS server (e.g., an email inbox can be a message queue) for which the javax.jms.MessageListener interface is
implemented. The message queue is monitored by a special kind of EJB(s) - Message Driven Beans
(MDBs) That processes the incoming messages and perform the services requested by the message. The MDBs are the end-point for JMS service request messages.
You assign a message-driven bean’s destination during deployment
by using Application Server resources.
JMS:
The Java Message Service (JMS) is an API for Java
messaging clients. JMS provides two programming models: point-to-point
and publish-and-subscribe.
In the point-to-point model, one sender puts a message on a queue that is
delivered to only one receiver. The publish-and-subscribe model adds a broadcast
mode in which any number of senders can add messages to a topic, and any
number of recievers receive all messages posted to topics. JMS queues and topics
are bound in the JNDI environment and made available to J2EE
applications.
Example:
Our application assumes the point-to-point model, and
requires setting up a queue with a QueueConnectionFactory in JMS. This section shows, how to implement an MDB with EJB
3.0 describing the source code of a simple message-driven bean application.
In this example, we are going to implement a
Message-driven bean application named "massage" that has the
following components:
Code for the client application:
|
package mdb;
import javax.jms.*;
import javax.naming.*;
import java.text.*;
import
javax.annotation.*;
@Resource(mappedName="jms/Queue")
class MessageConn{
private static Queue queue = null;
QueueConnectionFactory factory = null;
QueueConnection connection = null;
QueueSender sender = null;
QueueSession session = null;
public MessageConn(){
try {
//client creates the connection, session, and message sender:
connection = factory.createQueueConnection();
session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
sender = session.createSender(queue);
//create and set a message to send
TextMessage msg = session.createTextMessage();
for (int i = 0; i < 5; i++) {
msg.setText("This is my sent message " + (i + 1));
//finally client sends messages asynchronously to the queue
sender.send(msg);
}
System.out.println("Sending message");
session.close ();
} catch (Exception e) {
e.printStackTrace ();
}
}
}
public class MessageClient{
public static void main(String[] args){
MessageConn msgcon = new MessageConn();
}
}
|
Description of the given code:
In the given code of MessageClient application, the javax.jms.* package is
imported for extending the Queue, QueueConnectionFactory, QueueConnection,
QueueSender, and QueueSession class.
After you get the
QueueConnectionFactory object, you use its createQueueConnection to
construct a QueueConnection object, like this:
connection =
factory.createQueueConnection( );
Then, you use the createQueueSession
method of the QueueConnection interface to create a QueueSession object, as in
the following code:
session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
Note that you pass false as the first
argument to the createQueueSession method to indicate that you are not creating
a transactional session object.
Next, you can call the createSender
method of the QueueSession interface, passing a Queue object. The return value
of this method is a message producer, which is a QueueSender:
sender = session.createSender(queue);
Then you create a TextMessage object
based on the message received by passing the text for the TextMessage to the
createTextMessage method of the QueueSession object and then set the text to the
object of TextMessage
TextMessage msg =
session.createTextMessage( );
msg.setText("This is my sent message " + (i + 1));
Now, you are ready to send the new
message. You do this by calling the send method of the QueueSender object, as
you see here:
sender.send(msg);
The JMS resource is mapped to the JNDI name of the
destination from which the bean receives messages.
The New Message-Driven Bean wizard has already created the JMS resources for
us. The EJB 3.0 API enables us to look up objects in the JNDI namespace from
within the bean class so that we do not need to configure deployment descriptors
to specify the JMS resources. The EJB 3.0 specifications allow us to use
annotations to introduce resources directly into a class.
The MDB's connection process can
be seen in the figure shown below:

javax.annotation.Resource(@Resource):
The @Resource method specifies a
dependence on an external resource, such as a JDBC data source or a JMS
destination or connection factory.
If you specify the annotation on a field or method, the EJB container
injects an instance of the requested resource into the bean when the bean is
initialized. If you apply the annotation to a class, the annotation declares a
resource that the bean will look up at runtime.
The mappedName attribute of the
annotation Resource specifies the global JNDI
name of the dependent resource. Forexample:
@Resource(mappedName
="jms/Queue")
Specifies that the JNDI name of the
dependent resources is jms/Queue and deployed in the JEE Server JNDI
tree.
For Web-client:
In a Web-client application "jms/Queue"
and ConnectionFactory" argument
are used in the JNDI lookup. Both are logical JNDI names, and use
the outbound connectivity provided by the JMS resource adapter. Make
the JNDI lookup to use a Web Service shown as.
InitialContext ctx = new InitialContext();
queue = (Queue) ctx.lookup("jms/Queue");
QueueConnectionFactory factory =
(QueueConnectionFactory) ctx.lookup("ConnectionFactory");
connection = factory.createQueueConnection();
session = cnn.createQueueSession(false,
QueueSession.AUTO_ACKNOWLEDGE); |
Code for the
message-driven bean:
The MessageBean class demonstrates
the following requirements to its implementation:
-
In EJB 3.0, the MDB bean class is annotated with the
@MessageDriven annotation that specifies, which message queue monitors
the MDB (i.e., jms/Queue). If the queue does not exist, the EJB container automatically creates it at deploy time. There is no XML configuration file needed!
For the Application Server, the @MessageDriven annotation typically
contains a mappedName attribute
that specifies the JNDI name of the destination from which the bean will
consume messages.
- The class must be defined as public and must contain
a public constructor with no arguments. It must not define the finalize
method.
-
It is recommended, but not required,
that a message-driven bean class should implement the MessageListener
interface for the message type it supports. This interface defines only one method onMessage(
). When the EJB container arrives a message, it calls the onMessage(
) method of the message-driven bean to process the message. The onMessage(
) method contains the business logic and handles the processing of the
message in accordance with the application’s business
logic. It can call helper methods, or invoke a session bean to process the
information in the message or to store it in a database.
A message is delivered to a message-driven bean within a transaction context, so
all operations within the onMessage method are part of a single
transaction. If message processing is rolled back, the message will be
redelivered.
In our example, the MessageBean.onMessage( ) method retrieves the message body, parses out the
messages to a TextMessage, perform the necessary business logic, and
displays the text to the message-client.
-
A message-driven bean can also inject
a MessageDrivenContext
resource which is commonly used to call the setRollbackOnly method to
handle exceptions for a bean during container-managed transactions.
| package mdb;
import javax.ejb.*;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import java.text.*;
import javax.naming.*;
import
java.util.logging.Logger;
@MessageDriven(mappedName="jms/Queue")
public
class MessageBean implements
MessageListener {
@Resource
private
MessageDrivenContext mdc;
private static
final Logger logger;
public
void onMessage(Message msg) {
TextMessage tmsg =
null;
try {
tmsg = (TextMessage) msg;
logger.info("MESSAGE BEAN: Message received:
" + tmsg.getText( ));
System.out.println ("The onMessage() is
called");
} catch
(JMSException e) {
e.printStackTrace( );
mdc.setRollbackOnly( );
}
catch (Throwable th) {
th.printStackTrace();
}
}
public
void ejbRemove( )throws
EJBException{
loger.fine("ejbRemove()");
}
}
|
Logger:
When writing log messages
from EJBs within JRun, you have the following options:
- Nonportable Use JRun-specific methods
and services. These methods write to the JRun server's event log.
- Portable Use one of the
System.out.println methods. These methods write to the console (if used when
starting JRun).
An EJB can acquire a logger instance from the container
and use the logger's info( ) method to display the messages for the
client-end, as shown in the following code examples:
logger.info("MESSAGE
BEAN: Message received: " + msg.getText( ));
Packaging, Deploying, and
Running the message application.
To create and package the application using Ant,
use the default target for the build.xml file:
ant
This target packages the application client and
the message-driven bean, then creates a file named message.ear
in the dist directory.
You simply avoid having to create deployment
descriptor files for the message-driven bean and application client by
using resource injection and annotations. You need to use deployment
descriptors only if you want to override the values specified in the
annotated source files.
To deploy the application and run the client
using Ant, use the following command:
ant run
The output in the terminal window looks like
this:
In the server log file, the following lines
should be displayed, wrapped in logging information:
MESSAGE BEAN: Message received: This is my sent
message 1
MESSAGE BEAN: Message received: This is my sent message2
MESSAGE BEAN: Message received: This is my sent message 3
MESSAGE BEAN: Message received: This is my sent message 4
|
Current Comments
0 comments so far (post your own) View All Comments Latest 10 Comments: