ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

EJB 2 Message-Driven Beans
Pages: 1, 2, 3

Our Example

Our email application will be modeled using the PTP domain. When an email is placed on the queue we only want one receiver, else the email could be sent out multiple times.



JMS Messages

The item that is put on the queue is a JMS Message. This is a generic type that has message headers and contents. There are various subtypes of the JMS Message. For example,

Type

Description

TextMessage

This message holds a String. You manipulate the TextMessage via msg.setText("foo") and msg.getText()

ObjectMessage

This message stores a Serializable object. You manipulate the ObjectMessage via msg.setObject(Object o) and msg.getObject()

MapMessage

This message holds name/value pairs. You manipulate the MapMessage via msg.setString(key, value) and msg.getString(key). There are getter and setters for the various base java types (e.g. getBoolean, getInt, getObject, etc)

BytesMessage

This message is a stream of bytes. This can be used for wrapping existing message formats.

StreamMessage

This message allows you to work with a stream of primitives

We will use a MapMessage in our example, as it is one way that we can place email header information and the email body, into the message.

EJB 2.0: Message Driven Bean

Now we have talked about the fundamentals of JMS, we can talk about the new component type that is in the EJB 2.0 specification. (The EJB 2.0 spec if a Public Draft at the time of this article, although the Message Driven Bean part is not rumored to change.)

Let's review the design so far. We have a sender that puts messages on the queue, and a receiver that will read the messages and use that information to send out email. This receiver could be a program that starts up, subscribes to the "EmailQueue", and deals with the messages that come in. Instead of doing this every time we need a receiver, it would be nice to have a component architecture that allows for the concurrent processing of a stream of messages, is transactionally aware, and takes care of the infrastructure code, letting us work on the business logic. This is where Message Driven Beans come in. A Message Driven Bean (MDB) is simply a JMS message consumer. A client cannot access a MDB directly (as you do with session and entity beans). Your only interface to the MDB is by sending a JMS message to the destination of which the MDB is listening. To allow reuse, as with the other EJBs, a lot of information is provided in the EJB deployment descriptor. This means we do not worry about where we go to get the message (whether to a queue or topic), we just write the onMessage(Message msg) code to deal with the message.

Sample Files

Download the sample files discussed in this article here.

We have talked about JMS, and Message Driven Beans; now it's time to build our example.

Email Application Steps

We will walk through the following steps to get the email application working.

  1. Setup an "Email Queue" message queue in the JMS server
  2. Write an email client, responsible for sending a java message to the email queue
  3. Write a message driven bean that consumes these messages, and sends emails using their contents
  4. Write the deployment descriptors for the MDB
  5. Package the code

The code that we will create is

Code

Description

com.customware.client.EmailClient

The EmailClient that will plug a message on the queue

com.customware.ejb.EmailMDB

The Message Driven Bean that consumes the JMS messages from the client, and then uses the EmailHelper to send out an email

com.customware.util.EmailHelper

A helper class that has the static method sendmail(Map mail) which sends mail using JavaMail

Step One: Setup an email message queue

This step will depend on your messaging server (e.g. IBM MQSeries, SonicMQ, and so on). We need to setup a JMS Queue. I will use the name EmailQueue, which will be used in both the client (sender) and Message Driven Bean (receiver) deployment descriptor.

Step Two: Writing the email client
(EmailClient.java)

Now we will write our client (JMS sender). This client will take in information about the email that we will eventually send out. The main() method takes in the arguments from the command line, builds a Hashtable (any Map would work), and calls the sendmail(Map m) method. The sendmail method takes this information, builds a MapMessage from it, and then sends the message to the EmailQueue queue via sender.send(message).

The majority of the work is done in the constructor. It is there that we do all of the JMS work. The constructor does the following:

  1. We connect to the JNDI service via the getInitialContext() helper method.
  2. We lookup a connection factory for a queue [(QueueConnectionFactory) ctx.lookup(CONNECTION_FACTORY)]
  3. We create a queue connection to our JMS server [conFactory.createQueueConnection()]
  4. We create a JMS session, which is a context for producing and consuming messages [connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE)]
  5. We lookup the queue that we will use to send the messages [(Queue) ctx.lookup(QUEUE_NAME)]
  6. Finally, we create a sender, that can send messages to the queue that we just looked up, using the session that we created earlier [session.createSender(chatQueue)]

That's it. Now we have a client that can send messages to our queue. We can compile and run this client via:

% java com.customware.client.EmailClient dion@customware.com bob@harris.org "my subject" "my email body"

We have a client. Now we build our consumer.

Step Three: Write a Message Driven Bean
(EmailMDB.java)

When writing session, or entity beans, you have to write a remote interface, home interface, and the bean class (and optionally a Primary Key class for an entity bean). With Message Driven Beans we only have the bean class, as there is no "client" that interfaces with the bean.

A Message Driven Bean must implement two interfaces:

Interface

Description

MessageListener
javax.jms.MessageListener

This is the JMS interface that provides the onMessage(Message msg) method. When a message is put on the queue, the MDB will have onMessage called, and the container will pass in the actual message to be consumed

MessageDrivenBean
javax.ejb.MessageDrivenBean

This is the EJB interface that contains the EJB lifecycle methods:

  • ejbCreate(): When the EJB is created the container will call this method
  • ejbRemove(): When the container destroys the EJB it can call the ejbRemove() method
  • setMessageDrivenContext(MessageDrivenContext ctx): The context is passed into the EJB after the object is instantiated, and before ejbCreate() is called. The context has information that the container keeps up to date, and allows you to query: transactions (getUserTransaction(), setRollbackOnly(), getRollbackOnly())
    security (getCallerPrincipal(), isCallerInRole())

If you look at the code in EmailMDB.java you will see that the first few methods implement the MessageDrivenBean interface. All we are doing in these methods is printing that they are called. The setMessageDrivenContext() method saves away the context to an instance variable, in case we want to query it later. Nine times out of ten, this is what you would do. The last method is the callback onMessage(Message msg) from the MessageListener interface. This is where we consume the message and deal with it. First, we create a MapMessage by casting the message that was given to us. Then we loop through the name-value pairs in the map message, and plug in their values to a standard Hashtable. Note the methods that we use to go through the MapMessage:

// Get the names out of the Map Message
Enumeration mapnames = mapmessage.getMapNames();

// Get the string out of the map message, giving the key
String val = mapmessage.getString(key);

Finally, we call the EmailHelper.sendmail(map) method, which takes care of sending the email for us.

That was pretty easy. That is the great thing about MDBs. We don't have lots of ugly JMS code (like we had in the client, or like we would have if we wrote the consumer from scratch); in fact how does the MDB know where to get these messages? For that, we go to the deployment descriptors.

Pages: 1, 2, 3

Next Pagearrow