EJB 2.1 The Enhanced Message-Driven Bean

Java Development News:

EJB 2.1 The Enhanced Message-Driven Bean

By Richard Monson-Haefel

01 Dec 2002 | TheServerSide.com

Introduction

Richard Monson-Haefel is the author of Enterprise JavaBeans, 3rd Edition, Java Message Service and one of the world's leading experts on Enterprise Java. He is co-founder of OpenEJB, an open source EJB container used in Apple Computer's WebObjects, and an independent consultant.

The message-driven bean has undergone some important enhancements that make it far more useful and flexible than it was in EJB 2.0. That's saying a lot, because as an enterprise bean type the message-driven bean may be one of the most important components in the J2EE platform. Message-driven beans allow programmers to process asynchronous messages efficiently, and they act as a messaging gateway to the J2EE platform. At a time when asynchronous messaging is gaining recognition as a powerful programming paradigm, expansion of the MDB to new types of messaging systems is a blessing.

This column covers the new connector-based MDBs, which allow you to use MDBs with any kind of messaging system, not just JMS. In addition, the column covers the activation-configuration properties used in the deployment of MDBs, as well as EJB 2.1's new message-linking facilities.


Connector-Based Message-Driven Beans

The message-driven bean was introduced in EJB 2.0 to support the processing of asynchronous messages from a Java Message Service (JMS) provider. EJB 2.1 expands the definition of the message-driven bean so that it can support any messaging system, not just JMS. This section examines the support that the J2EE Connector Architecture provides for multiple JMS providers as well as non-JMS messaging systems.

Although the JMS-based MDB of EJB 2.0 has proven very useful, it has limitations. Perhaps the most glaring is that EJB vendors support only a small number of JMS providers (usually only one). In fact, many EJB 2.0 vendors support only their own JMS provider and not others', severely limiting your choices. If, for example, your company or a partner company uses a JMS provider that is not supported by your EJB vendor, you will not be able to process messages from that provider1.

The root of the problem is complex and requires a fairly deep understanding of transaction management. In a nutshell, the delivery of the message by the JMS provider to the MDB, and all the work performed by the MDB (using JDBC, invoking methods on other beans, etc.), must be part of the same transaction. Because the EJB container initiates the transaction, it must have prior knowledge that message delivery is imminent, so that the transaction is started before the message actually arrives. Unfortunately, the JMS API doesn't support this kind of functionality, so, in EJB 2.0, custom code is needed to coordinate transactions between each JMS provider and each EJB 2.0 container system. Vendors are faced with an MxN problem set: They must create costly custom integration code for each JMS provider they want to integrate with. As a result, they choose to integrate with very few JMS providers.

Another limitation of EJB 2.0 MDBs is that they support only the JMS programming model for asynchronous messaging. While JMS is very useful, and is the J2EE standard for asynchronous messaging, it's not the only messaging system available today. Examples of non-JMS messaging systems include SOAP, email, CORBA Messaging, and other proprietary messaging systems.

Two new features enable EJB 2.1 developers to support multiple JMS providers as well as non-JMS messaging systems: the expanded definition of message-driven beans, and J2EE Connector Architecture 1.5.

EJB 2.1 supports a more open definition of message-driven beans that allows them to handle just about any kind of messaging system from any vendor. EJB 2.1 vendors are still required to support JMS-based MDBs, but they are no longer limited to JMS. EJB 2.1 vendors can support any messaging system they want. The only requirement is that new types of message-driven beans implement the javax.ejb.MessageDrivenBean interface and adhere to the message-driven bean's life cycle. While EJB 2.1 vendors can build custom code to support a new messaging system (something other than JMS) they must also support any message-driven bean type that's based on the J2EE Connector Architecture 1.5.

The J2EE Connector Architecture (Connectors) provides a standard Service Provider Interface (SPI) that allows any Enterprise Information System (EIS) to plug directly into any J2EE container system. In version 1.0 of Connectors this plugablity applies to any request/reply type of resource in which the J2EE component (EJB or Servlet/JSP) initiates the request. J2EE 1.4, of which EJB 2.1 is a part, uses J2EE Connector Architecture version 1.5, which expanded the types of EIS to include asynchronous messaging systems. In such a system, the component waits for messages to arrive instead of initiating an interaction with an EIS; the EIS initiates the interaction by delivering a message.

J2EE Connector Architecture 1.5 defines a messaging contract specifically tailored to message-driven beans. It defines the contracts between an EJB container and an asynchronous connector so that incoming messages from the EIS are automatically processed by message-driven beans within a transaction started by the EJB container. The MDBs that are based on an asynchronous connector will implement the standard javax.ejb.MessageDrivenBean interface as well as a specific messaging interface defined by the connector itself. So instead of implementing the javax.jms.MessageListener interface, the MDB will implement some other type of interface that is specific to the EIS.

A hypothetical email connector can serve as an example. The email connector allows MDBs to process email much the same way JMS-based MDBs process JMS messages. The email connector is purchased from Vendor X and is delivered in a JAR file called a RAR (Resource ARchive). The RAR contains all the connector code and deployment descriptors necessary to plug into the EJB container system. It also defines a messaging interface and messaging API that the developer uses to create an email MDB. Here is the hypothetical email-messaging interface that an email MDB must implement.


package com.vendorx.email;

public interface EmailListener {
    public void receiveMessage(javax.mail.Message message);
}

The bean class that implements this interface will also implement the javax.ejb.MessageDrivenBean interface and will be responsible for processing email messages delivered to it by the email connector. The following code shows an MDB that implements the EmailListener interface and processes email messages.


package com.titan.email;
public class EmailBean implements javax.ejb.MessageDrivenBean, com.vendorx.email.EmailListener {
      MessageDrivenContext ejbContext;
      public void setMessageDrivenContext(MessgeDrivenContext mdc){
             ejbContext = mdc;
      }
      public void ejbCreate(){}
      public void receiveMessage(javax.mail.Message message){
           javax.mail.internet.MimeMessage msg = (javax.mail.internet.MimeMessage) message;
           Address [] addresses = msg.getFrom();
           //  continue processing Email message
      }
      public void ejbRemove(){}
}

The EmailListener interface delivers a message in the form of a JavaMail Message, a type that represents an email message including MIME attachments. The JavaMail API is too complicated to explain in detail for this hypothetical example. The important thing to take away from this example is that EJB 2.1 containers can, and must, support any type of message-driven bean for which there is a version 1.5 connector.

The messaging interface used by the connector-based MDB may declare a message processing method that defines a return types. For example, you might develop a connector that handles request/reply-style messaging for SOAP, using the ReqRepListener defined by the JAXM (Java API for XML Messaging), a SOAP messaging API defined by Sun Microsystems that is not a part of the J2EE platform:


package javax.xml.messaging;
import javax.xml.soap.SOAPMessage;

public interface ReqRespListener {
    public SOAPMessage onMessage(SOAPMessage message);
}

Notice that here onMessage() has a return type of SOAPMessage. A return type requires that the EJB container and connector coordinate the reply message back to the sender, or to some destination defined in the deployment descriptor. In addition to supporting different method signatures, the messaging interface may have several methods for processing different kinds of messages using the same MDB. The new kinds of message-driven beans that can be supported by EJB 2.1 containers systems are endless, thanks to the expanded definition of message-driven beans and the requirement to support the new J2EE Connector Architecture 1.5.


Activation Configuration for MDBs

There is a significant difference in how EJB 2.0 and EJB 2.1 define message-processing properties of a MDB in the deployment descriptor. EJB 2.0 defined a couple of JMS-specific elements, <message-selector> and <acknowledge-mode>, which have been replaced in EJB 2.1 so that the MDB deployment descriptor can represent both JMS-based MDBs and connector-based MDBs. Because connector-based MDBs may not use JMS as the message service, a message-agnostic element, <activation-config>, was introduced to describe the messaging properties of the MDB. The Activation Configuration properties are passed to the Connector when the MDB is deployed. The Connector will use these properties to configure the way it delivers messages to the MDB.

The following listing shows the <activation-config> elements - in bold:


<enterprise-beans>
    ...
    <message-driven>
        <ejb-name>FooMDB EJB</ejb-name>
        <ejb-class>
            com.monsonhaefel.foo.FooBean
        </ejb-class>
        <messaging-type>javax.jms.MessageListener</messaging-type>
        <transaction-type>Container</transaction-type>
        <message-destination-type>
            javax.jms.Queue
        </message-destination-type> 
        <activation-config>
           <activation-property>
               <activation-config-property-name>destinationType
               </activation-config-property-name>
               <activation-config-property-value>javax.jms.Queue
               </activation-config-property-value>
           <activation-property>
           <activation-property>
               <activation-config-property-name>messageSelector
               </activation-config-property-name>
               <activation-config-property-value>Color = 'Blue'
               </activation-config-property-value>
           <activation-property>
           <activation-property>
               <activation-config-property-name>acknowledgeMode
               </activation-config-property-name>
               <activation-config-property-value>Auto-acknowledge
               </activation-config-property-value>
           <activation-property>
        </activation-config>   
        <ejb-ref>
            ...
        </ejb-ref>
        <ejb-local-ref>
            ...
        </ejb-local-ref>
        <security-identity>
            <run-as>
                <role-name>everyone</role-name>
            </run-as>
        </security-identity>
        <resource-ref>
           ...
        </resource-ref>
    </message-driven>
    ...
</enterprise-beans>


The property names and values used in the <activation-config> to describe the messaging service will vary depending on the type of message service used, but EJB 2.1 defines a set of fixed properties for JMS-based message-driven beans:

  • acknowledgeMode
  • messageSelector
  • destinationType
  • subscriptionDurablity

These JMS specific activation configuration properties replace elements with similar names used in EJB 2.0 MDBs. Other, non-JMS, MDBs will use completely different activation configuration properties that are specific to the messaging system. For example, the hypothetical Email MDB might use a set of activation configuration properties like the following:



<activation-config>
      <activation-property>
            <activation-config-property-name>mailServer
            </activation-config-property-name>
            <activation-config-property-value>mail.ispx.com
             </activation-config-property-value>
      </activation-property>
      <activation-property>
             <activation-config-property-name>serverType
             </activation-config-property-name>
             <activation-config-property-value>POP3
             </activation-config-property-value>
      </activation-property>
      <activation-property>
            <activation-config-property-name>messageFilter
            </activation-config-property-name>
            <activation-config-property-value>to='submit@titan.com'
            </activation-config-property-value>
       </activation-property>
</activation-config>

In addition to the new <activation-config> element, EJB 2.1 introduced the <messaging-type> and <message-destination-type> elements.


<messaging-type>javax.jms.MessageListener</messaging-type>
...
<message-destination-type>
            javax.jms.Queue
</message-destination-type>

The <messaging-type> element declares the messaging interfaces the MDB will use. For JMS-based MDBs the messaging interface is always going to be javax.jms.MessageListener, but for connector-based MDBs it might be something completely different. If the <messaging-type> element is omitted, then the type is assumed to be javax.jms.MessageListener.

The <message-destination-type> element designates the type of destination from which the MDB receives messages. The allowed values for JMS-based MDBs are javax.jms.Queue and javax.jms.Topic. A connector-based MDB might use some other type. Its value must always be a fully qualified class name.

You may have noticed that the <message-destination-type> and the destinationType activation-configuration property specify the same thing. This is redundant for JMS-based MDBs, but for non-JMS MDBs, which do not define a destinationType property, it is not. The activation-configuration properties for connector-based MDBs, will be completely different from those for JMS-based MDBs. It's important that the <message-destination-type> be specified for both JMS-based and connector-based MDBs.


Message Linking

Message linking is a feature, new in EJB 2.1, that allows routing of messages sent by any enterprise bean (stateless, stateful, entity, or message-driven) to a specific message-driven bean in the same deployment. The advantage is that the developer can orchestrate a flow of messages between components in the same application.

To link the outgoing messages sent by an EJB with the incoming messages consumed and processed by an MDB, we need to define <message-destination-link> elements in the deployment descriptor of both the sending EJB and the receiving EJB. The destination identifed by the <message-destination-link> element corresponds to a logical destination defined in <assembly-descriptor> of the beans.

For example, the following shows the declaration of the <message-destination-link> element of a session bean that is sending a JMS message. Senders declare <message-destination-link> element under the <message-destination-ref> element.



  <enterprise-beans>
    ...
    <session>
        <ejb-name>SendingEJB</ejb-name>
        ...
        <resource-ref>
            <res-ref-name>jms/TopicFactory</res-ref-name>
            <res-type>javax.jms.TopicConnectionFactory</res-type>
            <res-auth>Container</res-auth>
        </resource-ref>
        <message-destination-ref>
            <message-destination-ref-name>
                   jms/Topic
            </message-destination-ref-name>
            <message-destination-type>javax.jms.Topic</message-destination-type>
            <message-destination-usage>Produces</message-destination-usage>
            <message-destination-link>
                 DestinationA
            </message-destination-link>
        </message-destination-ref>
        ...
    </session>
    ...
  </enterprise-beans>
  ...
</ejb-jar>


The <message-destination-ref> element is new in EJB 2.1. It declares the destination to which an enterprise bean sends messages. When the <message-destination-ref> includes a <message-destination-link> element, messages will be sent to that destination, which in this case is DestinationA. This destination named must match the name of a destination defined by a <message-destination> element in the assembly descriptor.

The code below specifies the same destination in the <message-destination> element of the <assembly-descriptor> .


<ejb-jar ...>
   <assembly-descriptor>
    ...
    <message-destination>
       <message-destination-name>DestinationA</message-destination-name>
    </message-destination>
    ...
  </assembly-descriptor>
</ejb-jar>

In order to get the MDB to consume messages sent to DestinationA, all we need to do is declare a <message-destination-link> element in the deployment descriptor which declares DestinationA. The code below points the receiving MDB's <message-destination-link> to the same destination: the <message-destination> element specified in the <assembly-descriptor>:


<ejb-jar ...>
    ...
    <message-driven>
        <ejb-name>Receiving MDB</ejb-name>
        ...
        <messaging-type>javax.jms.MessageListener</messaging-type>
        <transaction-type>Container</transaction-type>
        <message-destination-type>
            javax.jms.Topic
        </message-destination-type>
        <message-destination-link>
            DestinationA
        </message-destination-link>
        ...
    </message-driven>
    ...
  </enterprise-beans>
  ...
</ejb-jar>

The sending and receiving EJBs are now linked. Specifying the same destination in the session bean, the message-driven bean, and the assembly descriptor ensures that messages sent by the session bean will go to the MDB.

At deployment time each of the <message-destination> elements declared in the <assembly-discriptor> is mapped to a real messaging destination in the target environment. In most cases, this will be a JMS provider topic or queue, but it could be a destination provided by some other type of messaging system.

Although any enterprise bean can both consume (receive) messages from a logical destination and produce (send) messages, it's recommended that only MDBs consume messages. The reasons are complex but are explained in detail in the third edition of my book, Enterprise JavaBeans (O'Reilly 2001).


Wrapping Up

EJB 2.1 adds a variety of new features to the message-driven bean programming model including support for connector-based MDBs, the new activation-configuration elements, and message linking.

The real beauty of connector-based MDBs is that they will be completely portable across EJB 2.1 vendors, because all vendors must support them. If you use a connector-based MDB with EJB 2.1 vendor A, and later change to EJB 2.1 vendor B, you can continue to use the same connector-based MDB without any portability problems. Unfortunately, at this writing no connector-based MDBs are commercially available, which is why the examples like the email EJB are hypothetical. It's likely that new connector-based MDBs will start popping up within a few months after the EJB 2.1 spec is final.

The new activation-configuration elements for MDBs are necessary to support the arbitrary MDB types that Connectors 1.5 makes possible. The configuration properties are specific to a messaging system, and the container applies them to the connector at runtime. Because JMS-specific elements no longer appear in the MDB's deployment descriptor, it can now declare any kind of MDB, not just JMS-based MDBs.

Message linking allows developers to specify message flow between enterprise beans, in a manner independent of vendor and messaging system. It makes configuring rudimentary message flow fairly easy, but it's not a silver bullet. Message linking can be used only within the same J2EE application. This limitation is import to keep in mind, because messaging systems are often used to communicate between systems that aren't managed by the same application server.

This column wraps up a series of six on the new features of EJB 2.1, starting with an overview that was published in June. At this time I'm hard at work on the fourth edition of my EJB book, as well as a book on J2EE Web Services. (These books won't be available until much later next year, but if you want to be notified when they do come out, please send an email to Book-Notification@monson-haefel.com. ) Due to this heavy workload I'm taking a break from this column for a couple of months. When I return I plan to cover topics related to design, performance, and other more general concerns. See you then!


Notes

1. A workaround is to use a JMS Gateway, which routes messages from one JMS provider to another, but this is a custom solution, outside the EJB specification.




copyright notice (c) Copyright 2002 Richard Monson-Haefel. All rights reserved.