Chapter 22. Asynchronous Message Processing—Message-Driven Beans

What Is a Message-Driven Bean?

As organizations strive to develop collaborative software solutions, it is not uncommon to discover that an enterprise software system does not actually exist or function as a single software entity. Instead, there is a high degree of probability that the enterprise system actually comprises numerous independent software subsystems, each of which can exist on separate software platforms with unique operational boundaries.

The communication mechanism and the development plumbing effort involved to enable applications to communicate between themselves really depend on the interoperability of their respective implementation technologies. For example, the Remote Procedure Call (RPC) model is a viable communication solution between applications that have accessible and well-defined interfaces for processing client requests. The downside of the RPC model is that, regardless of whether communication between applications is enabled through an application adapter, you are implementing tight integration at the interface level (object class/methods) between possibly autonomous applications. Also, communication could be limited to synchronous interactions; for example, Enterprise JavaBean (EJB) clients use the home and remote interfaces to make synchronous RMI calls to their target EJB server. The client invoking the EJB call pauses its processing (blocks) until the EJB server returns processing control to the client through a response.

When loose coupling, reliability, and asynchronous processing are requirements between J2EE applications/components and Enterprise Information Systems (EISs), leveraging an intermediary messaging system that is a Java Messaging Service (JMS) provider and also interoperable with the EIS is probably your best option for centralizing communication flow through the production and consumption of messages.

Note

A JMS provider implements the JMS API, a set of standard interfaces that define how a JMS client accesses the facilities of a messaging service. This chapter uses a lot of concepts related to messaging domains and the JMS API. If you’re not familiar with either topic, reading Chapter 15, “The Java Messaging Service (JMS),” is recommended before proceeding with this chapter.

By leveraging a messaging model,

  • The calling application (message producer) sends a message to a JMS destination (queue or topic) implemented by the JMS provider, such as WebLogic JMS Server, which can deliver messages to the recipient (message consumer) immediately or store them until the consumer is available. In the meantime, the message producer does not block processing by waiting for a response from the message consumer. This is asynchronous processing.

  • The message producer does not need to know the implementation details of the message consumer—this is called loose coupling.

  • The JMS provider can persist messages if required—this is referred to as reliability.

A shortfall of the EJB 1.1 specification is that it did not provide an EJB type for handling asynchronous communications. However, a Java client could be used as a synchronous or asynchronous message listener for a JMS destination, which made it possible to route messages to an EJB for processing.

Note

Any J2EE component can send or synchronously receive a JMS message.

One of the goals of the EJB 2.0 specification is to provide a tighter integration with the JMS API, which was successfully achieved by introducing the message-driven bean (MDB), a new EJB type that can consume asynchronous JMS messages by listening to a queue or subscribing to a topic. By fulfilling the consumer side of the JMS messaging model, MDBs are asynchronous message listeners/consumers that are associated with only one queue or topic.

Figure 22.1 illustrates the partition of component functionality provided by J2EE and the EJB 2.0 specification.

The functionality provided by J2EE and the EJB 2.0 specification.

Figure 22.1. The functionality provided by J2EE and the EJB 2.0 specification.

The Characteristics of Message-Driven Beans

As an EJB type, MDBs reside within the context of an EJB container and, similar to standard JMS message listeners, cannot interact directly with their JMS clients because they do not have a home or remote interface. For this reason, MDBs are considered anonymous because they have no client-visible identity.

Similar to stateless session beans, MDB instances also have no conversational state, which implies that all bean instances are equivalent when they are not involved in servicing a client message. This characteristic permits the EJB container to pool and create multiple instances of the same MDB based on the throughput of messages in the MDB’s JMS destination, hence enabling the concurrent consumption of messages, as shown in Figure 22.2.

The concurrent consumption of messages by an MDB in an EJB container.

Figure 22.2. The concurrent consumption of messages by an MDB in an EJB container.

A client accesses an MDB through JMS by sending messages to a JMS destination (queue or topic) for which the MDB class is assigned as a message listener. MDBs are associated with a specific JMS destination via their deployment descriptors.

Note

Standard JMS message listeners receive messages asynchronously by implementing javax.jms.MessageListener with the onMessage() method.

When a message arrives in a JMS destination targeted for an MDB, the JMS provider and the EJB container automatically collaborate to deliver the message to the MDB. One instance from the MDB bean pool is selected to handle the message. Similar to a standard JMS message listener, an MDB contains an onMessage() method that is called automatically when a message arrives. However, different messages can be processed concurrently by different MDB instances, which provides performance and scalability benefits.

After an MDB consumes a message, it can process the message or pass the message to another EJB type for processing, as shown in Figure 22.3.

An example of processing an MDB.

Figure 22.3. An example of processing an MDB.

The Value Proposition of Message-Driven Beans

The primary benefit of using MDBs for asynchronous consumption of messages is derived from MDBs existing completely in the context of an EJB container and, therefore, being able to leverage EJB services the container provides. For example, the WebLogic EJB container provides a standard set of services, such as concurrency control, persistence, security, transaction management, locking, clustering, and complete JMS infrastructure support, which would otherwise need to be configured in code and via the Administration Console for a standard JMS Listener.

Note

MDBs can initiate Container-Managed and Bean-Managed Transactions, which are discussed in “An Overview of Message-Driven Bean Transactions” later in this chapter.

Although an MDB can serve only one JMS destination, its stateless nature allows the WebLogic EJB container to create multiple MDB instances as necessary to process large volumes of messages concurrently, hence providing a scalable mechanism for concurrent consumption/processing of messages. In contrast, you would have to configure the standard JMS Listener class to be a member of a serverwide session pool via the Administration Console.

Note

Instance pooling within the EJB container implies that messages are not received or processed in sequential order, although individual onMessage() calls to an MDB instance are serialized.

As you will learn later in the “The Life Cycle of a Message-Driven Bean” section, WebLogic Server maintains a free pool of EJBs for every MDB class to expedite processing asynchronous messages.

Another benefit of using MDBs is that they inherit the write-once, deploy-anywhere quality of EJBs, as the associated JMS destination and server resource attributes are assigned only at deployment time. This provides better portability of MDBs between different implementations of the EJB container.

MDBs are suitable for use over standard JMS Consumers in the following situations:

  • Broadcasting a variable volume of messages to a large subscription base—Because the EJB server can instantiate MDBs to handle a varied volume of messages from a JMS destination, MDBs are well suited to applications, such as a newsgroup application, that generate and broadcast a large volume of messages to a subscription base.

  • Legacy application integration into J2EE-based enterprise applications—MDBs can be used to process application requests that rely on message transfers, messaging routing, or message filtering operations.

  • As a JMS Listener—MDBs can be used as message listeners that receive messages from a JMS destination and then route them to other bean types for processing.

  • Enabling JMS transaction services—MDBs can demarcate transaction boundaries by using Bean-Managed or Container-Managed Transactions.

One constraint of using MDBs compared to standard JMS Listeners is that you can associate an MDB only to a single JMS destination (queue or topic). If you require a single JMS Consumer to process messages from multiple JMS destinations, you can use a standard JMS Consumer or deploy MDB classes for each JMS destination.

Differences Between MDBs and Other Bean Types

Even though an MDB is a bean type that can mimic the behavior of stateless session bean instances at times (pooling) and, like other EJBs, live within the boundaries of an EJB container, the following differences between MDBs and the other bean types (session and entity) further clarify their unique message consumption characteristics:

  • MDBs have no home or remote interface and, therefore, internal or external clients cannot directly access them.

  • The EJB container maintains an MDB’s entire life cycle, so MDB instances cannot be created or removed as a result of client requests or other API calls.

  • MDBs process multiple JMS messages asynchronously, instead of processing a serialized sequence of method calls, as with session and entity beans.

  • MDBs do not propagate application or remote exceptions back to their JMS clients, which forces exceptions to be handled within a bean’s onMessage() method.

  • Because an MDB’s role is to consume JMS messages, it has only one input parameter that is a subclass of javax.jms.Message, which is used to invoke the bean.

Table 22.1 summarizes the differences between message-driven, session, and entity beans.

Table 22.1. Summary of EJB Type Characteristics

Functionality

Message-Driven Bean

Session Bean

Entity Bean

Can implement a remote/local interface

No

Yes

Yes

Can implement a home interface

No

Yes

Yes

Exception propagation to client

No

Yes

Yes

Receives messages via a message listener—the onMessage() method

Yes

No

No

Can post a message to a queue or topic

Yes

Yes

Yes

Can return values to the caller

No

Yes

Yes

Can take advantage of pooling

Yes

Yes (stateless session beans)

Yes

Is isolated from the client

Yes

No

No

Life cycle is managed by the EJB container

Yes

Yes

Yes

Used primarily for asynchronous design solutions

Yes

No

No

The Message-Driven Bean Class

An MDB class implements the javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces, as shown in Figure 22.4.

The implementation of an MDB class.

Figure 22.4. The implementation of an MDB class.

The MessageDrivenBean interface, which extends the javax.ejb.EnterpriseBean interface, declares two methods: ejbRemove() and setMessageDrivenContext(). The bean developer must implement these methods along with the ejbCreate() method inherited from the EnterpriseBean interface. The MessageListener interface declares only one method that the bean developer must implement: onMessage(). Listing 22.1 shows the basic components required to implement an MDB.

Example 22.1. The Basic Components of an MDB Class

public class myMDB implements MessageDrivenBean, MessageListener{
  public myMDB() {...}; // Required Bean Constructor
  public void ejbCreate() {...}
  public void setMessageDrivenContext(MessageDrivenContext ...) {...}
  public void onMessage(javax.jms.Message MessageName) {...}
  public void ejbRemove() {...}
}

The following sections briefly describe the four methods that constitute an MDB class and discuss how to handle exceptions in MDBs.

Note

The MDB class is allowed to implement other methods, such as utility methods called internally by onMessage(). However, these methods are never called directly by the container or any client.

The ejbCreate() Method

The container calls the ejbCreate() method for bean instantiation into the WebLogic EJB container’s free pool, which is an area of memory WebLogic Server reserves to maintain a set of method-ready stateless bean instances (session and MDBs). This method is a convenient place to implement logic for allocating the necessary resources to process messages. For example, if an MDB sends messages to other destinations, you can place code in the ejbCreate() method to look up JMS API connection factories and destinations and create the JMS API connection.

The MDB class must define one ejbCreate() method, and its signature must abide by the following rules:

  • The name must be ejbCreate.

  • The method must be declared as public, not as final or static.

  • The return type must be void.

  • The method can have no arguments.

  • The throws clause must not define any application exceptions.

The setMessageDrivenContext() Method

The setMessageDrivenContext() method is called before ejbCreate() and invoked by the EJB container to associate an MDB instance with its context, which is maintained by the container. The EJB container passes the javax.ejb. MessageDrivenContext object into this method, which is usually established as a member variable, thus allowing the MDB instance to access its context information in subsequent methods.

For example, the transactional context that the EJB container sets for an MDB can be accessed via the following methods from the MessageDrivenContext interface:

  • setRollbackOnly()—. Marks a transaction to roll back so that it cannot commit. The bean can use this method only if it uses Container-Managed Transaction demarcation.

  • getRollbackOnly()—. Returns a Boolean value indicating whether the transaction can commit. The bean can use this method only if it uses Container-Managed Transaction demarcation.

    Caution

    The getRollbackOnly() and setRollbackOnly() methods should be used only in MDB methods that execute in the context of a transaction; otherwise, the container throws java.lang.IllegalStateException.

  • getUserTransaction()—. Returns a reference to the javax.transaction.UserTransaction object that allows the bean to initiate, commit, and roll back transactions from within the bean. The bean can use this method only if it uses Bean-Managed Transaction demarcation.

Even though the getCallerPrincipal(), isCallerInRole(), getEJBHome(), and getEJBLocalHome() methods are inherited as part of the MessageDrivenContext interface, they should not be called by MDBs for these reasons:

  • The bean does not receive a client security context.

  • The bean does not have EJBHome or EJBLocalHome objects associated with it.

The MDB class must define one setMessageDrivenContext() method. This method’s signature must abide by the following rules:

  • The method must be declared as public, not as final or static.

  • The method must accept one argument of type javax.ejb.MessageDrivenContext.

  • The method must return void.

  • The method should not include a throws clause.

The onMessage() Method

The EJB container calls the onMessage() method when it determines that an MDB instance should receive a message from a JMS queue or topic. The onMessage() method accepts an object of type javax.jms.Message object as an input argument. Within this method, the bean provider implements the business logic to process the received message, which is cast to one of the five JMS message types. After a message has been processed, the bean instance is available to receive other requests.

The MDB class must define one onMessage() method; its signature must abide by the following rules:

  • The method must be declared as public, not as final or static.

  • The return type must be void.

  • The method must have a single argument of type javax.jms.Message.

  • The throws clause must not define any application exceptions.

The ejbRemove() Method

The EJB container calls this method to remove the bean from WebLogic Server’s free pool. You can also use this method to implement the code for releasing any resources allocated in the bean’s ejbCreate() or setMessageDrivenContext() methods, such as JMS or JDBC connections.

The MDB class must define one ejbRemove() method. This method’s signature must abide by the following rules:

  • The method must be declared as public, not as final or static.

  • The method’s return type must be void.

  • The method can have no arguments.

  • The throws clause must not define any application exceptions.

Handling Exceptions

MDBs do not propagate exceptions to the client, and for this reason they should not throw an application exception or a RemoteException. If any of the bean’s methods throws such an exception, WebLogic Server immediately removes the EJB instance without calling ejbRemove(). From a client’s perspective, the MDB still exists, as future messages are forwarded to a new bean instance that WebLogic Server allocates from the free pool. However, from a WebLogic Server perspective, these types of exceptions are undesirable because any resources the MDB allocated would not be released appropriately.

To counter these exceptions, the bean provider must handle all exceptions within the MDB’s methods, for example:

  • For the onMessage() method, use a try-catch block to catch any exceptions.

  • For the ejbCreate() method, you can include javax.ejb.CreateException in the throws clause.

  • For the ejbRemove() method, you can use javax.ejb.EJBExecption in the throws clause.

In addition to the preceding exception-handling techniques, a bean developer should implement additional internal methods to perform resource cleanup routines outside the ejbRemove() method—for example, to clean up resources allocated through the ejbCreate() and setMessageDrivenContext() methods.

The Life Cycle of a Message-Driven Bean

Because an MDB exists in the context of the WebLogic EJB container, its lifetime is also controlled by the container. An MDB initially exists in the Does Not Exist state in the WebLogic EJB container. At its instance creation, the bean is placed in a Method Ready state in WebLogic’s EJB free pool waiting to process a message on the JMS destination (queue or topic) it’s assigned to as a message listener. As shown in Figure 22.5, the following steps describe the life cycle of an MDB instance:

The life cycle of an MDB.

Figure 22.5. The life cycle of an MDB.

  1. An MDB instance’s life starts when the container calls newInstance() on the MDB class to create a new instance.

  2. The container provides the EJB container context reference to the MDB by calling the setMessageDrivenContext() method.

  3. The container then calls ejbCreate() on the MDB instance to instantiate the bean to the EJB free pool (the Method Ready state). The MDB instance is then ready to receive a message sent via its assigned JMS destination by any client.

    Note

    During the ejbCreate() or setMessageDrivenContext() methods, an MDB can acquire connections to various resources, such as JMS connections. These resources are bound to the MDB as long as it remains in the Method Ready state.

  4. When a JMS message is posted to a topic or queue, the container calls the designated MDB’s onMessage() method. The bean instance in turn parses and processes the message. After the method completes, the bean instance is returned to the free pool.

  5. The MDB instance goes into the Does Not Exist state from the Method Ready state, when the container decides to reduce the number of bean instances in the free pool, either to reclaim the allocated resources or because it no longer needs additional instances of the bean in the free pool. The WebLogic EJB container removes the bean from the free pool by calling the bean’s ejbRemove() method.

When WebLogic Server starts, if MDBs have been deployed to the server, the WebLogic EJB container automatically instantiates multiple instances of the bean, as specified by the value of the <initial-beans-in-free-pool> element in the weblogic-ejb-jar.xml deployment descriptor. These bean instances are placed in the free pool, ready to service an incoming message on their assigned JMS destinations. Populating the free pool in this manner improves the initial response time for the MDB because initial requests for the bean can be satisfied without generating a new bean instance.

Note

<initial-beans-in-free-pool> defaults to 0 if the element is not defined, which implies that WebLogic Server will not prepopulate the free pool with any MDB instances.

The EJB container creates new instances of MDBs as needed for concurrent and parallel message processing. However, the maximum number of MDB instances that can be accommodated in the free pool depends on two factors:

  • The value of the <max-beans-in-free-pool> element in the weblogic-ejb-jar.xml deployment descriptor, which places an upper boundary on the number of MDB instances the container creates.

  • The number of execution threads available in WebLogic Server to service each message consumption by an MDB. For example, if <max-beans-in-free-pool> is set to 20 but only 10 execution threads are available to the EJB container, only 10 of the MDBs actually receive messages.

Note

▸ To learn how to configure WebLogic Server execute threads, seeThe WebLogic Server Execute Queues,” p. 1021, in Chapter 28, “Performance Tuning the WebLogic Server.”

Note

The default value of the <max-beans-in-free-pool> element enables you to run beans in parallel, using as many threads as possible. The only reason to change this setting is to limit the number of beans running in parallel because of memory/processor constraints that affect the performance or operation of WebLogic Server.

Hence, when a JMS queue or topic receives a message, the WebLogic EJB container calls an associated MDB as follows:

  • If a bean instance is available in the free pool, WebLogic Server uses that instance, and the message is consumed through the bean’s onMessage() method.

  • If no bean instances are available in the free pool because all instances of an MDB class are active and <max-beans-in-free-pool> has been reached, the message waits in the JMS destination until an MDB becomes available to consume the message.

  • If the free pool is empty and <max-beans-in-free-pool> has not been reached, the EJB container creates a new instance of the MDB to consume the message.

Developing a Simple Message-Driven Bean Example

The series of steps in the following sections guide you through implementing and deploying a simple MDB (myMessageDrivenBean) that consumes a text message from a JMS topic and prints the message to your WebLogic Server’s standard out (stdout). An overview of this MDB example is shown in Figure 22.6.

An overview of the MDB example.

Figure 22.6. An overview of the MDB example.

Step 1: Writing the Message-Driven Bean Code

Because MDBs have only four methods where you can implement code, they are quite easy to develop. The code for myMessageDrivenBean is shown in Listing 22.2.

Example 22.2. The myMessageDrivenBean MDB Code

package MDBExample;

import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class myMessageDrivenBean
       implements MessageDrivenBean, MessageListener {

  private MessageDrivenContext mdbContext;

// A simple logging mechanism
  private void log(String s) {
    if (true) System.out.println(s);
  }

  // container callback methods

  public void ejbRemove() throws EJBException {
    log("ejbRemove called");
  }

  public void setMessageDrivenContext(MessageDrivenContext ctx) {
    mdbContext = ctx;
    log("context set");
  }

  public void ejbCreate () throws CreateException {
    log("ejbCreate called");
  }

  // The implementation of the onMessage method

  public void onMessage(Message msg) {
    TextMessage tm = (TextMessage) msg;
    try {
      log("onMessage called");
      String text = tm.getText();
      System.out.println("[myMessageDrivenBean] Received message: " + text );
    }
    catch(JMSException ex) {
      System.out.println("Caught JMSException: " + ex );
    }
  }
}

Note

To show you when the WebLogic EJB container calls an MDB’s method, simple logging to WebLogic Server’s stdout has been provided for each method in Listing 22.2.

After you have created this code using a Java IDE or text editor, follow these steps:

  1. Create a temporary staging directory anywhere on your hard drive. This example uses d:stage as the staging directory. For this example, it’s assumed that the staging directory is located on a file system local to WebLogic Server.

  2. Save the file as myMessageDrivenBean.java in your staging directory.

  3. Create a META-INF subdirectory in your staging directory.

  4. Open a command shell session and set your Java environment, using the WebLogic setEnv batch file in your WebLogic domain directory.

  5. From within your staging directory, compile your .java file to a .class file (myMessageDrivenBean.class) using the Java javac utility, as follows:

    javac -d c:stage myMessageDrivenBean.java
    

    The bytecode myMessageDrivenBean.class file is then created in the d:stageMDBExample directory.

Step 2: Writing the Associated Deployment Descriptors

For this example, you need to create the following deployment descriptors with a text or XML editor and place them in the META-INF subdirectory within your staging directory.

The ejb-jar.xml Deployment Descriptor

This deployment descriptor describes an EJB’s structure and declares its internal dependences. You can also include the bean’s application assembly information, which describes how the bean in the ejb-jar.xml file is assembled into an application deployment unit. The complete ejb-jar.xml deployment descriptor for myMessageDrivenBean is shown in Listing 22.3.

Example 22.3. The ejb-jar.xml Deployment Descriptor for myMessageDrivenBean

<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

<!-- Generated XML! -->

<ejb-jar>
  <enterprise-beans>
    <message-driven>
      <ejb-name>myMDB</ejb-name>
      <ejb-class>MDBExample.myMessageDrivenBean</ejb-class>
      <transaction-type>Container</transaction-type>
      <acknowledge-mode>Auto-acknowledge</acknowledge-mode>
      <message-driven-destination>
        <destination-type>javax.jms.Topic</destination-type>
        <subscription-durability>NonDurable</subscription-durability>
      </message-driven-destination>
    </message-driven>
  </enterprise-beans>

  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>myMDB</ejb-name>
        <method-name>onMessage</method-name>
      </method>
      <trans-attribute>NotSupported</trans-attribute>
    </container-transaction>
  </assembly-descriptor>

</ejb-jar>

Table 22.2 explains the deployment descriptor elements that pertain to MDBs in the ejb-jar.xml file.

Table 22.2. A Description of Elements in the ejb-jar.xml File

Element

Description

<ejb-name>

Specifies a unique name for the EJB that identifies the bean in deployment descriptors.

<ejb-class>

Specifies the fully qualified name of the EJB class.

<transaction-type>

Specifies how the bean manages transactions. The valid values are Bean or Container.

<acknowledge-mode>

Specifies how the MDB acknowledges receipt of a message back to WebLogic JMS Server after it has received and processed the message. Without an acknowledgment, WebLogic JMS Server does not know whether an MDB has received the message, so it tries to redeliver the message to the bean.

For beans that use Container-Managed Transactions, the container immediately acknowledges every message when a transaction commits (value is Auto-acknowledge).

For beans that use Bean-Managed Transactions, you can also instruct the container to lazily acknowledge the delivery of all messages based on processor availability (value is Dups-ok-acknowledge). However, this setting can result in the delivery of duplicate message receipts if WebLogic JMS Server fails.

<destination-type>

Specifies whether the JMS destination the MDB consumes messages from will be a queue or topic. The valid values for this element are javax.jms.Queue or javax.jms.Topic.

<subscription-durability> (valid only if the MDB’s JMS destination is a topic)

Specifies whether the MDB should be a durable subscriber. The valid values for this element are Durable or NonDurable (the default).

Optionally, you can also use the <message-selector> element to specify a valid JMS message selector query used to restrict the message type an MDB consumes, as shown here:

<message-selector>company_name='objectmind'</message-selector>

Note

The message selector is a conditional expression that follows the ANSI-92 SQL syntax for a WHERE clause in a standard SQL statement.

The weblogic-ejb-jar.xml Deployment Descriptor

This deployment descriptor contains deployment information for a bean that is WebLogic specific, such as the initial and maximum number of MDBs in the free pool and mappings to WebLogic resources. The complete weblogic-ejb-jar.xml deployment descriptor for myMessageDrivenBean is shown in Listing 22.4.

Example 22.4. The weblogic-ejb-jar.xml Deployment Descriptor for myMessageDrivenBean

<!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN'
'http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd'>

<!-- Generated XML! -->

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>myMDB</ejb-name>
    <message-driven-descriptor>
      <pool>
        <max-beans-in-free-pool>15</max-beans-in-free-pool>
        <initial-beans-in-free-pool>5</initial-beans-in-free-pool>
      </pool>

      <destination-jndi-name>myJMSTopic</destination-jndi-name>
      <jms-polling-interval-seconds>10</jms-polling-interval-seconds>
    </message-driven-descriptor>

    <transaction-descriptor>
    </transaction-descriptor>

    <jndi-name>myMDB</jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

Table 22.3 explains the deployment descriptor elements that pertain to MDBs in the weblogic-ejb-jar.xml file.

Table 22.3. A Description of Elements in the weblogic-ejb-jar.xml File

Element

Description

<ejb-name>

Specifies the name of the bean assigned in the ejb-jar.xml file.

<max-beans-in-free-pool>

Specifies the upper boundary for the number of MDB instances that can be placed in the EJB container’s free pool.

<initial-beans-in-free-pool>

Specifies how many instances of the bean should be created by the EJB container and placed in the free pool when the bean is deployed.

<destination-jndi-name>

Associates an MDB with a JMS queue or topic that has already been deployed in the WebLogic JNDI tree.

<jms-polling-interval-seconds>

Specifies the number of seconds between each attempt to reconnect to the JMS destination.

<jndi-name>

Specifies a JNDI name for the bean.

By default, WebLogic Server uses the standard JMS connection factory (weblogic.jms.MessageDrivenBeanConnectionFactory) to enable an MDB to gain connections to its JMS destination. However, if you have created your own JMS connection factory, you need to specify its JNDI name via the <connection-factory-jndi-name> element in the weblogic-ejb-jar.xml file:

<message-driven-descriptor>
  <connection-factory-jndi-name>myJMSConnectionFactory</connection-factory-jndi-name>
</message-driven-descriptor>

Note

If the <connection-factory-jndi-name> element is not specified, weblogic.jms.MessageDrivenBeanConnectionFactory is used by default.

Step 3: Building Your MDB Example

After the MDB class and its associated deployment descriptors are in their respective locations in the staging directory, the next step is packaging these files into a deployment unit—the EJB JAR file. To do this, issue the following jar command from within your staging directory after ensuring that you have set up your Java environment:

jar cvf std_mdb.jar MDBExample META_INF

Issuing this command creates the std_mdb.jar file (the deployment unit).

To generate container classes for this deployment unit, which the WebLogic EJB container uses to access the MDB, you need to use the WebLogic ejbc utility.

The ejbc compiler generates container classes according to the deployment elements you have specified in the weblogic-ejb.jar.xml deployment descriptor file. This is the ejbc command for generating container classes for the std_mdb.jar deployment unit:

java weblogic.ejbc std_mdb.jar mdb.jar

Note

The ejbc utility accepts both JAR files and exploded directories as input. If you specify an output JAR file, ejbc places all generated files in the JAR file.

This command creates a new JAR file (mdb.jar) with the original EJB classes, interfaces, and XML deployment files in the JAR as well as the new container classes that ejbc generates. You will deploy mdb.jar to WebLogic Server later in this chapter.

Note

If you use the ejbc utility to output to a JAR file, the output JAR name must be different from the input JAR name.

Figure 22.7 shows issuing the jar and ejbc commands through a batch file named build.cmd.

Packaging the MDB example for deployment.

Figure 22.7. Packaging the MDB example for deployment.

Step 4: Configuring the JMS Destination for the MDB Example

Before you can deploy your MDB to WebLogic Server, you must ensure that the JMS destination associated with the bean has been configured in the target WebLogic Server and, most important, that the topic is active. The JNDI name for the JMS topic associated with this example is myJMSTopic, which is specified via the <destination-jndi-name> weblogic-ejb-jar.xml element.

Note

▸ To learn how to configure a JMS destination in WebLogic Server, seeCreating a JMS Destination—Queue or Topic,” p. 495, in Chapter 15, “The Java Messaging Service (JMS).”

Step 5: Deploying the MDB Example to Your WebLogic Server

To deploy the mdb.jar deployment unit to your target WebLogic Server, follow these steps:

  1. Launch and log in to the Administration Console.

  2. Expand the Deployments node in the left pane.

  3. Right-click the EJB node and select the Configure a New EJB option.

  4. Locate the mdb.jar archive in your file system using the Administration Console’s directory navigation mechanism.

  5. Click [select] to the left of the mdb.jar file.

  6. Select a target server from the Available Servers list box.

  7. Enter a name for the MDB, such as myMessageDrivenBean, to identify it in the Administration Console.

  8. Click Configure and Deploy. The Administration Console displays the Deploy tab, which lists deployment status and activities for the MDB. A successful deployment of the MDB example is shown in Figure 22.8.

    The Deploy tab indicates a successful deployment of the MDB example.

    Figure 22.8. The Deploy tab indicates a successful deployment of the MDB example.

After your MDB has been deployed to WebLogic Server, the EJB container creates five instances of the bean in the free pool, as specified by the <initial-beans-in-free-pool> element in weblogic-ejb-jar.xml. Because logging was enabled in each method of myMessageDrivenBean (refer to Listing 22.2), the methods called to instantiate the been in the free pool are displayed in WebLogic Server’s stdout, as shown in Figure 22.9.

Use stdout to view the methods called to instantiate the MDB in the free pool.

Figure 22.9. Use stdout to view the methods called to instantiate the MDB in the free pool.

After the MDB has been successfully deployed, using these steps to monitor the bean from the Administration Console is a good idea:

  1. Select your MDB in the left pane of the Administration Console.

  2. Click the Monitoring tab.

  3. Click the Monitor All Message Driven EJBRuntimes link.

In the Deploy tab, shown in Figure 22.10, you can easily verify how many bean instances have been created and placed in the free pool and ensure that the bean is listening on an active JMS destination.

Monitoring your deployed MDB.

Figure 22.10. Monitoring your deployed MDB.

Step 6: Testing Your Message-Driven Bean

To test your MDB, you need to write a JMS client that sends a text message to the bean’s associated JMS topic (myJMSTopic). After the message is sent to the JMS topic, the EJB container calls the bean’s onMessage() method for message consumption and processing. Listing 22.5 provides the code for a JMS client (PubSubProducer) that sends five text messages to myJMSTopic.

Note

In Listing 22.5, you have to modify the context.PROVIDER_URL value accordingly to match your WebLogic Server host and port number.

Example 22.5. The PubSubProducer JMS Client

import javax.naming.*;
import javax.jms.*;
import java.util.*;

public class PubSubProducer {

  private final static String NAMING_FACTORY=
                                     "weblogic.jndi.WLInitialContextFactory";
  private final static String TCON_FACTORY="javax.jms.TopicConnectionFactory";
  private final static String MESSAGE_TOPIC="myJMSTopic";

  public static void main ( String[] argv ){

    try {

      // Create a JNDI initial context
      Properties props = new Properties() ;
      props.put( Context.INITIAL_CONTEXT_FACTORY, NAMING_FACTORY);
      props.put( Context.PROVIDER_URL, "t3://einstein:7001");
      InitialContext ic = new InitialContext(props);

      // Look up a connection factory in JNDI
      TopicConnectionFactory tCF =
                           (TopicConnectionFactory)ic.lookup(TCON_FACTORY);

      // Create a connection
      TopicConnection tCon = tCF.createTopicConnection();

      // Create a session
      TopicSession tSession =
                   tCon.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);

      // Look up the topic
      Topic topic = (Topic)ic.lookup(MESSAGE_TOPIC);

      // Create a TopicPublisher bound to the topic
      TopicPublisher tPublisher = tSession.createPublisher(topic);

      // Create a TextMessage
      TextMessage mes = tSession.createTextMessage("My First Message-Driven Bean Message!");

      // Start the topic, send the message, and close the topic
      tCon.start() ;

         for(int i = 0; i<5; i++ ) {
           tPublisher.publish(mes);
           System.out.println("  Sending Message - " + (i+1) +
                               " to the JMS Topic");
      }

         tCon.close() ;

    } catch ( NamingException e1 ) {
      System.out.println(e1);
    } catch ( JMSException e2 ) {
      System.out.println(e2);
    }
  }

}

To test your MDB, first compile the JMS client code (PubSubProducer.java) using the javac utility from a command shell. Then run the JMS client from the command shell by using the following command:

Java PubSubProducer

As shown in Figure 22.11, the JMS client sends five messages to the JMS topic, and the onMessage() method is called five times to process each message.

Testing your MDB with a JMS client.

Figure 22.11. Testing your MDB with a JMS client.

An Overview of Message-Driven Bean Transactions

A transaction in JMS cannot include both producing and consuming messages as one logical unit of work, so both processes need to be treated within their own transactional contexts. The reason for this transactional separation is that a JMS client’s (JMS Producer) transaction context does not flow with a JMS message, which implies that transactions initiated from a JMS client cannot be propagated to the message consumer.

If a transaction is started from a JMS Producer, the JMS message is not immediately propagated to a JMS destination (a queue or topic). Instead, the message is buffered in memory or stored in the JMS server’s persistent storage device (a JDBC database or file). Only if the transaction commits will messages in the buffer be sent to the target JMS destination as part of transaction commit. However, if the JMS producer’s transaction is rolled back, the JMS message is flushed from the buffer, and the message is not sent to the target JMS destination.

From a JMS Consumer’s perspective, such as an MDB, if a transaction commits, the processed JMS message is removed from the JMS destination. However, if the transaction rolls back, the JMS message stays in the JMS destination and is redelivered as follows:

  • In a publish/subscribe message domain using a JMS topic, the message is redelivered to the same subscriber of the message.

    Note

    A consumer for a topic is referred to as a subscriber.

  • In a point-to-point (PTP) message domain using a JMS queue, the message is redelivered to the queue so that another consumer can process the message.

Specifying an MDB’s Transaction Demarcation Type

When designing an MDB to use transactions, you must decide whether the bean will demarcate transactions programmatically via the onMessage() method (Bean-Managed Transaction, or BMT, demarcation) or whether the EJB container will perform transaction demarcation (Container-Managed Transaction, or CMT, demarcation).

You specify the transaction demarcation type of an MDB via the <transaction-type> element in the ejb-jar.xml deployment descriptor, for example:

  • <transaction-type>Bean</transaction-type>—. BMT demarcation

  • <transaction-type>Container</transaction-type>—. CMT demarcation

Note

The <transaction-type> element specifies how the EJB container must manage transactions for the onMessage() method on the arrival of a JMS message.

The onMessage() method is called in the scope of a transaction determined by the <transaction-type> element.

Container-Managed Transactions

With CMT demarcation, the EJB container demarcates transactions based on instructions in the ejb-jar.xml deployment descriptor. These instructions, called transaction attributes (as shown in Listing 22.6), tell the container whether it should run an MDB’s onMessage() method in a new transaction started by the container or run the method without any transactions.

Example 22.6. A Segment of ejb-jar.xml Showing Transaction Attributes for the onMessage() Method Using CMT

<ejb-jar>
  ...
 <assembly-descriptor>
    ...
    <container-transaction>
      <description>Transactions required for this method.</description>
      <method>
        <ejb-name>myMDB</ejb-name>
        <method-name>onMessage</method-name>
      </method>
      <trans-attribute>NotSupported or Required</trans-attribute>
    </container-transaction>
    ...
 </assembly-descriptor>
  ...
</ejb-jar>

As shown in Listing 22.6, these are the only transaction attributes that are applicable to MDBs with CMT demarcations:

  • NotSupported—. The container calls the onMessage() method with an unspecified transaction context, which implies the method cannot be used to initiate transactions.

  • Required—. The EJB container automatically starts a new transaction before dequeuing the JMS message and before calling the onMessage() method. Therefore, the EJB container calls onMessage() with a valid transaction context and a message receipt from the JMS destination. The container attempts to commit the transaction after the onMessage() method has completed. For MDBs that use CMT, the container automatically acknowledges a message receipt to the WebLogic JMS Server after the transaction commits.

Avoiding Poison Messages with Container-Managed MDBs

If the onMessage() method does not successfully complete or the container rolls back the transaction, the container does not acknowledge a message receipt; the message remains in its associated JMS destination and is delivered again to the MDB. When a message continuously gets redelivered to a container-managed MDB only to be rolled back, the message is referred to as a poison message. Poison messages can have a negative impact on WebLogic JMS Server’s performance. They also consume MDB resources from the free pool, so they should removed from the JMS destination when they’re detected. WebLogic JMS Server has built-in support for detecting message redelivery attempts and for redirecting a message to an error destination when redelivery attempts for the message reach a specific limit. You can configure WebLogic JMS Server to manage poison messages via the Administration Console, as shown in Figure 22.12.

Manage poison messages via the Administration Console.

Figure 22.12. Manage poison messages via the Administration Console.

Bean-Managed Transactions

When an MDB uses BMT demarcation, you must use the EJBContext.getUserTransaction() callback to obtain a reference to the javax.transaction.UserTransaction object. After you have an instance of javax.transaction.UserTransaction, you can mark the beginning of a transaction with the begin() method, and end the transaction with the commit() or rollback() method. If the transaction commits, a message acknowledgement is sent to WebLogic JMS Server based on the value the <acknowledge-mode> element specifies in the ejb-jar.xml deployment descriptor: Auto-acknowledge or Dups-ok-acknowledge. The skeleton code for an onMessage() method using BMT is shown in Listing 22.7.

Example 22.7. Skeleton Code for the onMessage() Method Showing How to Use BMT

public void onMessage(Message msg) {
    try {
    // get the UserTranaction from the EJB context

    javax.transaction.UserTransaction mdbtx = mdb_context.getUserTransaction();

    // start the transaction

      mdbtx.begin();

      do some processing...

      if (OK Condition)

      {

        // commit the transaction

        mdbtx.commit();

      }

      else

      {

        // roll back transaction

        mdbx.rollback();

      }
    }
}

Summary

This chapter is in essence a continuation of Chapter 15, which introduces the concepts behind the JMS API and explains how you can communicate between applications and systems through the production and consumption of messages. However, in Chapter 15, only synchronous messaging was discussed.

This chapter has methodically introduced you to MDBs, J2EE’s solution to consuming JMS messages in an asynchronous manner, and their value as an EJB-type JMS message listener within the context of an EJB container. MDBs are easy to implement, and a simple example has been supplied so that you can get a handle on how to develop and deploy a working MDB to WebLogic Server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset