Message-Driven Bean Logical Component Architecture
Figure 1 depicts the basic architecture involved in creating message driven bean components.
Figure 1 Message-driven EJBs.
At the top of the diagram is the javax.ejb.EnterpriseBean marker interface, which is the base interface for all EJBs. The EnterpriseBean interface is extended by the javax.ejb.MessageDrivenBean interface, which is required to be implemented by all message-driven EJB classes. Furthermore, message-driven beans must implement the javax.jms.MessageListener interface. Public, nonfinal, and nonabstract message-driven bean EJBs, such as MyMessageDrivenEJBean, shown in the figure, must implement both of these MessageListener and MessageDrivenBean interfaces. Unlike other EJBs, message-driven beans do not expose business-specific methods to a client; they are concerned only with adhering to those interface requirements of the EJB container. As such, message-driven beans must also have a public parameterless constructor and should not implement the finalize() method.
Message-Driven Bean Interfaces
The setMessageDrivenContext() method defined on a message-driven bean is used to pass an instance of a MessageDrivenContext object to the EJB. It is also the first method defined on the MessageDrivenBean interface that is called by the container. A MessageDrivenContext object encapsulates an interface to the EJB message-driven container context.
One key operation that must be implemented by custom message-driven beans is the parameterless ejbCreate() method. This method is called by the EJB container when the container decides to create an instance of the message-driven EJB. The container might decide to do this when it wants to create an initial pool of bean instances, or it might do this when it receives a client's request. Like EJB creation methods on other beans, the ejbCreate() method is akin to a special type of constructor or initialization method implemented by EJBs.
The ejbRemove() method is called by a container on a message-driven bean object when the container is about to decommission the bean instance from handling any more client requests. The container is solely responsible for determining when it will call ejbRemove() on a particular message-driven bean instance. It is not bound in any way to the EJB client. Be aware, however, that ejbRemove() is not guaranteed to be invoked by the container. Under normal operational circumstances, the container should call ejbRemove(), but when system exceptions are thrown from the message-driven bean implementation back to the container, there is no guarantee that ejbRemove() will be invoked. As such, the bean implementer must ensure that any resources allocated by the bean are periodically checked and cleaned up.
Perhaps most important to the bean implementer is the implementation of onMessage(). This is the method invoked by the container when an asynchronous message is to be handled by the particular bean instance. A regular JMS javax.jms.Message instance is passed to the bean instance from which data may be extracted and the business-specific logic of the bean to handle so that a message can take place. Note, however, that no application-specific exceptions must be thrown by such a method.
JMS Message Interfaces
You might now be wondering what kind of information a message-driven bean implementation extracts from a JMS message passed into an onMessage() method invocation. Figure 2 depicts the core interfaces and conceptual relations of the base JMS Message type. The Message interface is the root interface for all messages that flow through a JMS-based messaging system. The Destination interface is a marker interface used to represent an endpoint of message delivery. Similarly, because a delivery mode for a message also exists, a conceptual relationship to a DeliveryMode interface is shown.
Figure 2 The JMS base message type.
JMS message header information can be get or set, using a standard getter and setter syntax of getJMSXXX() and setJMSXXX(), where XXX is the name of a header property preceded by the characters JMS. Standard header properties defined as getters and setters on the Message interface include unique message IDs, timestamps, reply and destination locations, delivery modes, message types, and priorities.
JMS container provider-specific properties of a message can also be get and set onto a message using a host of getXXXProperty() and setXXXProperty() methods, respectively, where XXX specifies the property type. Each property has a name identified by a String and a value of its specific type. Properties whose names begin with the JMSX prefix are reserved as standard JMS properties.
Five types of messages extend the Message interface, corresponding to the five types of message body data, shown in Figure 3. Byte data is encapsulated by BytesMessage, a Serializable object is encapsulated by ObjectMessage, a String message is encapsulated by TextMessage, key and value pairs are encapsulated by MapMessage, and I/O streams are encapsulated by StreamMessage. Individual methods on the message subinterfaces define getters and setters for the type-specific data body, but a generic clearBody() method to clear the data body of a message and place it in write-only mode exists on the base Message interface.
Figure 3 The JMS message body specialization types.