Connector-Based Message-Driven Beans




Connector-Based Message-Driven Beans

Although the JMS-based MDB has proven very useful, it has limitations. Perhaps the most glaring limitation is that EJB vendors are able to support only a small number of JMS providers (usually only one). In pre-EJB 2.1 days, most vendors supported only their own JMS provider, and no others. Obviously, this limits your choices: if 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 JMS provider.[*]

[*] 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.

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 (e.g., using JDBC, invoking methods on other beans, etc.), must be part of the same transaction, which is initiated by the EJB container. This requires that the EJB container have prior knowledge that message delivery is imminent so that it can initiate a transaction before the message is actually delivered. Unfortunately, the JMS API doesn't support this kind of functionality. So in the early days of EJB, JMS providers had to perform custom integration with each and every EJB vendor. Custom integration was expensive (businesswise), so old EJB 2.0 vendors generally choose to integrate with very few JMS providers.

Another limitation with JMS-based MDBs is that you are tied to the JMS programming model; no other messaging systems are supported. While JMS is very useful, it's not the only messaging system available. SOAP, email, CORBA messaging, proprietary messaging systems used in ERP systems (SAP, PeopleSoft, etc.), and legacy messaging systems are examples of other non-JMS messaging systems.

EJB 3.0 (and 2.1) supports an expanded, more open definition of message-driven beans that allows them to service any kind of messaging system from any vendor. The only requirement is that new types of message-driven beans adhere to the message-driven bean life cycle. EJB vendors can build custom code to support a new messaging system (something other than JMS), but they must also support any message-driven bean type that's based on JCA 1.5.

The JCA provides a standard Service Provider Interface (SPI) that allows any EIS to plug into any Java EE container system. Version 1.0 of the connector architecture applies only to request/reply resources in which the Java EE component (EJB or servlet/JSP) initiates the request. The current version of the connector architecture (1.5), which is required by J2EE 1.4 and higher, is much more general and can work with any asynchronous messaging systems. In such systems, the Java EE component waits for messages to arrive, instead of initiating an interaction with an EIS; the EIS initiates the interaction by delivering a message.

JCA 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 message-driven beans automatically process incoming messages from the EIS. MDBs based on an asynchronous Connector can implement a specific messaging interface defined by the Connector itself. Instead of implementing the javax.jms.MessageListener interface, the MDB implements some other type of interface that is specific to the EIS.

For example, Chapter 3 introduced a hypothetical Email Connector that allows MDBs to process emailsimilar to how JMS-based MDBs process JMS messages. The Email Connector is purchased from Vendor X and is delivered in a JAR file called a Resource ARchive (RAR). The RAR contains all the Connector code and deployment descriptors necessary to plug into the EJB container system. It also defines a messaging interface that the developer uses to create an email MDB. Here is the hypothetical email messaging interface that must be implemented by an email MDB:

package com.vendorx.email;

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

The bean class that implements this interface is responsible for processing email messages delivered by the Email Connector. The following code shows an MDB that implements the EmailListener interface and processes email:

package com.titan.email;

@MessageDriven(activationConfig={
                @ActivationConfigProperty(
                     propertyName="mailServer",
                     propertyValue="mail.ispx.com"),
                @ActivationConfigProperty(
                     propertyName="serverType",
                     propertyValue="POP3 "),
                @ActivationConfigProperty(
                     propertyName="messageFilter",
                     propertyValue="to='submit@titan.com'")})
public class EmailBean implements com.vendorx.email.EmailListener {

    public void onMessage(javax.mail.Message message){
        javax.mail.internet.MimeMessage msg =
            (javax.mail.internet.MimeMessage) message;
        Address [] addresses = msg.getFrom( );
        //  continue processing Email message
 }
}

In this example, the container calls onMessage( ) to deliver a JavaMail Message object, which represents an email message including MIME attachments. However, the messaging interfaces used by a Connector-based MDB don't have to use onMessage( ). The method name and method signature can be whatever is appropriate to the EIS; it can even have a return type. For example, a Connector might be developed to handle request/reply-style messaging for SOAP. This connector might use the ReqRespListener defined by the Java API for XML Messaging (JAXM), which is a SOAP messaging API defined by Sun Microsystems that is not part of the Java EE platform:

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


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

In this interface, onMessage( ) has a return type of SOAPMessage. This means the EJB container and Connector are responsible for coordinating 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.

There's no limit to the new kinds of message-driven beans that EJB container systems can support. The real beauty of all of this is that Connector-based MDBs are completely portable across EJB vendorsbecause all vendors must support them. If you use a Connector-based MDB with EJB Vendor A and later change to EJB Vendor B, you can continue to use the same Connector-based MDB with no portability problems.

The activation configuration properties used with non-JMS-based MDBs depend on the type of Connector and its requirements. Let's see an example of this:

@MessageDriven(activationConfig={
                @ActivationConfigProperty(
                     propertyName="mailServer",
                     propertyValue="mail.ispx.com"),
                @ActivationConfigProperty(
                     propertyName="serverType",
                     propertyValue="POP3"),
                @ActivationConfigProperty(
                     propertyName="messageFilter",
                     propertyValue="to='submit@titan.com'")})

We talked about @ActivationConfigProperty annotations before. As you can see from the preceding example, any name/value pair is supported within this annotation, so it can easily support the email-specific configuration for this Connector type.