7 Using EJB 3.0 Compliant MDBs

The following topics provide information on how to program and implement EJB 3.0 compliant MDBs:

Implementing EJB 3.0 Compliant MDBs

To implement EJB 3.0 compliant MDBs, follow the steps described in "Overview of the EJB 3.0 Development Process" in Programming WebLogic Enterprise JavaBeans, Version 3.0 for Oracle WebLogic Server.

Programming EJB 3.0 Compliant MDBs

To program EJB 3.0 compliant MDBs, follow the steps described in "Programming the Bean File: Typical Steps" in Programming WebLogic Enterprise JavaBeans, Version 3.0 for Oracle WebLogic Server.

You must use the @javax.ejb.MessageDriven annotation to declare the EJB type as message-driven. You can specify the following optional attributes:

  • messageListenerInterface—Specifies the message listener interface, if you haven't explicitly implemented it or if the bean implements additional interfaces.

    Note:

    The bean class must implement, directly or indirectly, the message listener interface required by the messaging type that it supports or the methods of the message listener interface. In the case of JMS, this is the javax.jms.MessageListener interface.
  • activationConfig—Specifies an array of activation configuration properties that configure the bean in its operational environment.

    Activation configuration properties are name-value pairs that are passed to the MDB container when an MDB is deployed. The properties can be declared in either an ejb-jar.xml deployment descriptor or by using the @ActivationConfigProperty annotation on the MDB bean class. An example using the @ActivationConfigProperty annotation is shown in Example 7-1. An example using the ejb-jar.xml deployment descriptor is shown in Example 7-2.

    Example 7-1 Example @ActivationConfigProperty Code

    . . .
    @ActivationConfigProperties(
        {
            @ActivationConfigProperty(
                name="connectionFactoryJndiName", value="JNDINameOfMDBSourceCF"
            ),
            @ActivationConfigProperty(
                name="initialContextFactory", value="weblogic.jndi.WLInitialContextFactory"
            )
        }
    )
    . . .
    

    To set activation configuration properties in the ejb-jar.xml descriptor, use the activation-config-property element in the message-driven stanza, as shown in Example 7-2.

    Example 7-2 Activation Configuration Properties Set in ejb-jar.xml

    <message-driven>
          . . .
          <activation-config>
            <activation-config-property>
                <activation-config-property-name>destinationJNDIName</activation-config-property-name>
                <activation-config-property-value>myQueue</activation-config-property-value>
            </activation-config-property>
            <activation-config-property>
                <activation-config-property-name>destinationType</activation-config-property-name>
                <activation-config-property-value>javax.jms.Queue</activation-config-property-value>
            <activation-config-property>
          </activation-config>
          . . .
        </message-driven>
        <message-driven>
          . . .
          <activation-config>
            <activation-config-property>
                <activation-config-property-name>destinationJNDIName</activation-config-property-name>
                <activation-config-property-value>myQueue</activation-config-property-value>
            </activation-config-property>
            <activation-config-property>
                <activation-config-property-name>destinationType</activation-config-property-name>
                <activation-config-property-value>javax.jms.Queue</activation-config-property-value>
            <activation-config-property>
          </activation-config>
          . . .
        </message-driven>
    

    Because activation configuration properties can be set in an ejb.jar deployment descriptor or by using activationConfigProperty annotation properties, conflicts may result if the same name is used in both places. Conflicts may also result from using same-named properties from pre-3.0 versions of EJB or from proprietary WebLogic Server EJB annotations. Such conflicts are resolved following the following priority order, in sequence from high to low is

    1. Properties set in the weblogic-ejb-jar.xml deployment descriptor

    2. Proprietary WebLogic Server 10.0 (and later) annotations

    3. activation-config-property properties set in the ejb-jar.xml deployment descriptor

    4. activationConfigProperty annotation properties

    For example, if the same property exists in the weblogic-ejb-jar.xml descriptor and the ejb-jar.xml descriptor, the one in weblogic-ejb-jar.xml has the higher priority and overrides the one in ejb-jar.xml value. Or, if the same property is set in both an ejb-jar.xml descriptor element and in an activationConfigProperty annotation, the descriptor element takes precedence and the annotation is ignored.

    For more information about activation configuration properties, see "javax.ejb.ActivationConfigProperty" in Programming WebLogic Enterprise JavaBeans, Version 3.0 for Oracle WebLogic Server. Also see Table 11-1, which summarizes the activation configuration properties supported by WebLogic Server.

For detailed information on developing MDBs to support the messaging modes as described in MDBs and Messaging Models, see Programming and Configuring MDBs: Details.

MDB Sample Using Annotations

Example 7-3 shows a WebLogic MDB that uses a subscription to a WebLogic JMS queue (from WebLogic Server 10.3.4 or later), transactionally processes the messages, and forwards the messages to a target destination.

The MDB connects using JMS connection factory MyCF to receive from queue MyQueue. It forwards the messages to MyTargetDest using a connection generated from connection factory MyTargetCF.

Resource reference pooling note: The MDB uses a resource reference to access MyTargetCF. The resource reference automatically enables JMS producer pooling, as described in "Enhanced Support for Using WebLogic JMS with EJBs and Servlets" in Programming JMS for Oracle WebLogic Server.

For a similar sample using topics instead of queues, see Example 10-1, "Sample MDB Using Distributed Topics".

Example 7-3 Sample MDB Using Distributed Queues

package test;
import javax.annotation.Resources;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.*;
 
@MessageDriven(
  name = "MyMDB",
  activationConfig = {
    @ActivationConfigProperty(propertyName  = "destinationType", 
                              propertyValue = "javax.jms.Queue"),
 
    @ActivationConfigProperty(propertyName  = "connectionFactoryJndiName",
                              propertyValue = "MyCF"), // External JNDI Name
 
    @ActivationConfigProperty(propertyName  = "destinationJndiName",
                              propertyValue = "MyQueue") // Ext. JNDI Name
  }
)
 
@Resources ({
  @Resource(name="targetCFRef",        
            mappedName="MyTargetCF",   // External JNDI name 
            type=javax.jms.ConnectionFactory.class),
 
  @Resource(name="targetDestRef", 
            mappedName="MyTargetDest", // External JNDI name
            type=javax.jms.Destination.class)
})

public class MyMDB implements MessageListener {

  // inject a reference to the MDB context

  @Resource
  private MessageDrivenContext mdctx;  

  // cache targetCF and targetDest for re-use (performance) 

  private ConnectionFactory targetCF;
  private Destination targetDest;

  @TransactionAttribute(value = TransactionAttributeType.REQUIRED)
  public void onMessage(Message message) {

    System.out.println("My MDB got message: " + message);

    // Forward the message to "MyTargetDest" using "MyTargetCF"

    Connection jmsConnection = null;

    try {
      if (targetCF == null) 
        targetCF = (javax.jms.ConnectionFactory)mdctx.lookup("targetCFRef");

      if (targetDest == null)
        targetDest = (javax.jms.Destination)mdctx.lookup("targetDestRef");

      jmsConnection = targetCF.createConnection();
      Session s = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer mp = s.createProducer(null);

      mp.send(targetDest, message);

    } catch (JMSException e) {

      System.out.println("Forcing rollback due to exception " + e);
      e.printStackTrace();
      mdctx.setRollbackOnly();

    } finally {

      // Closing a connection automatically returns the connection and
      // its session plus producer to the resource reference pool.

      try { if (jmsConnection != null) jmsConnection.close(); }
      catch (JMSException ignored) {};
    }
 
    // emulate 1 second of "think" time
 
    try { Thread.currentThread().sleep(1000); }
    catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Restore the interrupted status
    }
  }
 
}