7 Using EJB 3.2 Compliant MDBs

Examine how to program and implement EJB 3.2 compliant MDBs:

This chapter includes the following sections:

7.1 Implementing EJB 3.2 Compliant MDBs

You can implement MDBs that are complaint to specific EJB version.

To implement EJB 3.2 compliant MDBs, follow the steps described in Overview of the EJB Development Process in Developing Enterprise JavaBeans for Oracle WebLogic Server.

7.2 Programming EJB 3.2 Compliant MDBs

You can program MDBs that are complaint to specific version of EJB.

To program EJB 3.2 compliant MDBs, follow the steps described in Programming the Bean File: Typical Steps in Developing Enterprise JavaBeans 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 have not 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.

    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.

    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 Oracle 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 Oracle 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 Developing Enterprise JavaBeans for Oracle WebLogic Server. Also see Table 11-1, which summarizes the activation configuration properties supported by Oracle WebLogic Server.

    Note:

    Based on the Enterprise JavaBean specification, the javax.ejb.ActivationConfigProperty annotation is used for MDBs only. This annotation is not used for session or entity beans.

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

Example 7-1 Example @ActivationConfigProperty Code

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

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

<message-driven>
      . . .
      <activation-config>
        <activation-config-property>
            <activation-config-property-name>destinationLookup</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>destinationLookup</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>

7.2.1 MDB Sample Using Annotations

Example 7-3 shows a WebLogic MDB that uses a subscription to a WebLogic JMS queue (from Oracle 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 Developing JMS Applications for Oracle WebLogic Server.

For a similar sample using topics instead of queues, see Example 10-1.

This is a sample Oracle WebLogic MDB for durably subscribing to a WebLogic 10.3.4/11gR1PS3 or later JMS topic, transactionally processes the messages, and forwards the messages to a target destination.

The MDB connects using JMS connection factory "MyCF" to receive from topic "MyTopic." 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 per the "Enhanced Support for Using WebLogic JMS with EJBs and Servlets" chapter of "Programming JMS for Oracle WebLogic Server."

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
    }
  }
 
}