Chapter 3. Message-Driven Beans

In this chapter, we will cover:

  • Handling a text-based message
  • Handling a byte-based message
  • Handling a stream-based message
  • Handling a map-based message
  • Handling an object-based message
  • Using MDB in a publish-and-subscribe application
  • Using MDB in a point-to-point application
  • Specifying which types of messages to receive using the message selector
  • Browsing messages in a message queue

Introduction

A Message-Driven Bean (MDB) implements business logic in an asynchronous fashion. The client does not receive a response to the message from the MDB. A client will send a message to a queue or topic. The message is then retrieved and processed by the MDB. A queue differs from a topic in that a message in a queue is sent to a single MDB. In contrast, a message in a topic will be sent to each MDB that is registered with that topic.

From the client's perspective, the MDB is anonymous. There is no way for the client to identify or otherwise select a specific MDB. The EJB container decides which MDB object to use in response to a request. In order to send a message to an MDB, the client can use dependency injection or JNDI lookup to locate the queue or topic.

MDBs are managed by the server EJB container. The EJB container will treat all instances of the same MDB class identically. A message will be delivered to the next available instance of the MDB. The container will create and destroy MDBs as necessary. While an MDB has no conversational state, instance variables in an MDB may hold information needed to work correctly. This can include database connection and references to other EJBs.

An MDB may use a MessageDrivenContext to obtain information regarding the server's EJB container. This interface is based on the EJBContext interface that deals with transaction and security issues among other things. The MessageDrivenContext will be addressed in other chapters.

The Java EE server uses Java Message Service (JMS) to support MDBs. Many of the details of JMS are hidden, making the use of MDBs easier. As a result, MDBs should not attempt to use the JMS to acknowledge the receipt of messages. This type of response is handled by the container. While not covered here, MDBs can be driven by other connector-driven technologies. For example, the Java EE Connector Architecture (JCA) provides a means of interfacing with Enterprise Information Systems (EIS). These types of technologies can expand the potential uses of MDBs.

The creation of an MDB involves three tasks:

  1. Using the @MessageDriven annotation to designate the class as an MDB
  2. Implementing the javax.jms.MessageListener interface for JMS-driven beans
  3. Overriding the onMessage method

The MDB must be defined as public and cannot be declared as final or abstract. In addition, it needs a public default constructor and cannot have a finalize method.

The javax.jms.MessageListener interface has a single method, onMessage. This method is passed a javax.jms.Message object. Five sub-interfaces exist based on the Message interface:

  • TextMessage A Java String object
  • BytesMessage An unformatted stream of bytes
  • StreamMessage A stream of bytes based on primitive Java data types
  • MapMessage A set of key/value pairs
  • ObjectMessage Contains a Java object

When a message is sent to the onMessage method, the message is cast to one of these interface types and processed.

There are two life cycle call back methods used with MDBs: PostConstruct and PreDestroy. The PostConstruct annotated method will be invoked after the MDB has been created and before any business methods of the MDB are used (and after dependency injection has been done). The PreDestroy annotated method will be called before the MDB is destroyed. The PreDestroy method may not be invoked if the EJB container crashes or a system exception is thrown by the MDB.

When a method of an MDB instance is invoked, it will be executed to completion before that instance of the MDB is called again. This means that MDBs do not have to be re-entrant. The calls to an MDB are done in a serial fashion. However, if a client sends out multiple messages to the server containing an MDB, there is no guarantee the same MDB instance will be used for each message or that the messages will be processed in the same order the client sent them. This means that the application should be designed to handle messages arriving out of order.

For example, an MDB may be created to receive a bill and then process the bill. If the order in which the bills are processed is important, then special processing may be required to ensure they are handled in the correct order. If an MDB is designed to receive commands and then add a command entry in a log, the command to close the log may arrive before a message to append an entry.

As mentioned before, there are several types of messages that can be sent to an MDB. In this chapter, we will compare and contrasts these types by developing five different applications using the five different types of messages to send an order to an MDB for processing. An order will consist of a part number (12345), a weight (12.5f) and a quantity (50). Each application will use a servlet to compose the message but will send it with a different Message derived interface.

Each servlet uses the same structure as illustrated below. Both doGet and doPost methods are used to call a common processRequest method that contains the code for the client.

public class ServletName extends HttpServlet {
@Resource(mappedName="jms/SomeMessageFactory")
private QueueConnectionFactory queueConnectionFactory;
@Resource(mappedName="jms/SomeMessageQueue")
private Queue queue;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
Connection connection;
try {
connection = queueConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = (MessageProducer) session.createProducer(queue);
// Create message
// Initialize message
messageProducer.send(SomeMessage);
System.out.println("---> Text Message Sent");
} catch (JMSException ex) {
Logger.getLogger(TextServlet.class.getName()). log(Level.SEVERE, null, ex);
}
// HTML output
} finally {
out.close();
}
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

The servlet is declared and a queueConnectionFactory and queue variables are injected using the @Resource annotation. In this example, the mappedName attribute was assigned a value of jms/SomeMessageFactory and jms/SomeMessageQueue for the queueConnectionFactory and queue variables respectively. The creation of the queue and factory along with the assignment of their names will need to be done using the EE server. In GlassFish, this is done from the administration console under JMS Resources. You can use whatever naming convention you want, just be consistent.

In the servlet's processRequest method, a Connection variable is declared. A try block is used to catch javax.jms.JMSException.

The process of connecting to a queue and sending a message to the queue involves:

  1. Establishing a connection using the Connection object
  2. Creating a Session object that represents the communications between the servlet and the queue. It is also used to send the message.
  3. Creating a MessageProducer object to create the Message

    The connection to the queue/topic is established using the QueueConnectionFactory interface's createConnection method. Once a connection is made, a Session object is declared and initialized using the connection.

    The first parameter of the createSession method determines whether the connection needs to support transactions. We used false since we are not using transactions in this chapter. The second parameter specifies the acknowledge mode used. For this chapter we will use auto-acknowledge.

    Before a message can be created, the Session object creates a MessageProducer object. At this point, we can use any one of several MessageProducer create type methods to create the message type of interest.

    This code is followed by a message-type specific sequence of statements used to create and initialize the message. The last part of the try block will use the Session object to send the message to the destination queue/topic.

    The actual MDB is annotated with @MessageDriven. The following illustrates a typical MDB structure. Notice the implementation of the MessageListener interface and the presence of the onMessage method.

    @MessageDriven(mappedName = "jms/MapQueue", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
    })
    public class SampleMDB implements MessageListener {
    ...
    public void onMessage(Message message) {
    ...
    }
    }
    

    The @MessageDriven annotation uses a mappedName element and an activationConfig element to declare the class. The mappedName element is assigned the name of the queue to associate with this MDB. In this example the queue name is jms/MapQueue. The mappedName attribute is vendor-specific and is frequently a JNDI name.

    The activationConfig element is a bit more complex. In this example, two @ActivationConfigProperty annotations are used to further define the characteristics of the MDB. The first one establishes how JMS is to acknowledge the receipt of a message from a client. The second one indicates the destination type. The acknowledgement is auto-acknowledge and the destination is a JMS Queue.

    There are two common techniques for structuring an MDB application:

    • Point-to-Point One or more producers will send messages to a queue. These messages are then consumed by one or more consumers (MDBs). Once removed from a queue it is no longer available to other consumers.
    • Publish/Subscribe One or more producers will send messages to a topic. Each message in the topic will then be sent to each consumer (MDB) that is currently subscribing to the topic.

    When a message is placed in a queue it will remain there until it is removed and sent to an MDB. The QueueBrowser class provides a way of determining the messages currently in a queue. The Browsing messages in a message queue recipe illustrates the use of the QueueBrowser.

    Sometimes messages of a similar type are sent to the same queue. However, it may be desirable that the messages be sent to different MDBs based on these differences in message type. A message selector enables this behavior. The approach is sometimes called "message filtering" and is illustrated in the Specifying which types of messages to receive using the message selector recipe.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.97.40