10 Configuring and Deploying MDBs Using JMS Topics

This chapter describes how to develop an MDB that automatically sets up JMS topic subscriptions and then processes subscription messages. A message that is published to a JMS topic is replicated to all subscriptions that have a matching selector filter. A single deployed MDB may create multiple topic subscriptions and may have one or more free pools per host WebLogic Server instance. This behavior is controlled by MDB attribute settings, topic type, and whether the MDB is running on the same cluster or JVM as its topic. (For information about MDB free pools, see MDBs and Concurrent Processing.)

This chapter also describes how to use topic MDBs together with WebLogic JMS distributed topics. WebLogic JMS distributed topics are logical topics that are composed of multiple physical topics, where each physical topic is hosted on a different JMS Server instance. This distributed topic capability was significantly enhanced in WebLogic Server 10.3.4 to provide increased scalability and high availability. The enhancements include direct support for remotely hosted distributed topics, for fully distributing the processing of a single logical subscription across multiple physical subscriptions, and for multiple JVMs to process messages from the same physical subscription.

This chapter is organized as follows:

For additional information about JMS topics see:

Oracle recommends reviewing the previous chapters of this book before reading this chapter.

Supported Topic Types

WebLogic MDBs support the following types of topics:

  • Singleton topics -- A singleton topic is either a non-distributed WebLogic JMS topic or a reference to a particular member topic of a WebLogic JMS distributed topic. The JNDI name syntax for a WebLogic JMS uniform distributed topic member is based on the name of the JMS server that hosts the member: jms-server-name@jndi-name-of-distributed-topic.

  • Foreign provider topics -- Non-WebLogic JMS topics are called foreign provider topics. MDBs treat foreign provider topics similarly to singleton topics. Such topics are typically considered to be remote.

  • Replicated distributed topics -- WebLogic JMS distributed topics are logical topics composed of multiple physical topics, where each physical topic is hosted on a different JMS server instance in the same cluster. In releases of WebLogic Server prior to 10.3.4, each message sent to any member of a distributed topic is always automatically replicated (forwarded) to all subscriptions on all of the other members of the distributed topic. This kind of distributed topic is still supported and is now called a replicated distributed topic (abbreviated RDT).

  • Partitioned distributed topics - WebLogic JMS distributed topics are logical topics composed of multiple physical topics, where each physical topic is hosted on a different JMS server instance in the same cluster. A partitioned distributed topic (abbreviated PDT) does not forward messages between members. Messages published to a member of a PDT are only copied to subscriptions on that member. Partitioned distributed topics are supported starting with WebLogic Server 10.3.4.

To configure a distributed topic type, you set Partitioned or Replicated as the value for the JMS distributed topic configuration attribute JMS Forwarding Policy. For more information, see "Configuring Partitioned Distributed Topics" in Configuring and Managing JMS for Oracle WebLogic Server.

The Most Commonly Used MDB Attributes

The most commonly used topic MDB attributes are:

  • JMS destination and connection factory

  • Subscription durability

  • Container managed transactions

  • Message distribution tuning

Some other useful topic MDB attributes are:

  • Free pool size

  • Auto-delete on undeploy

  • Message filtering (JMS selectors)

  • Subscription identifier

The message distribution tuning settings include the topicMessagesDistributionMode, distributedDestinationConnection, and generate-unique-client-id attributes.

Most attributes can be configured either by using an annotation or via descriptor XML stanzas. In addition, specific attribute names for descriptor XML stanzas and annotations are summarized in the tables in Chapter 11, "Deployment Elements and Annotations for MDBs."

Setting the JMS Destination, Destination Type, and Connection Factory

A topic MDB's configuration must properly specify the location of its JMS connection factory, its destination, and its destination type. Typically, this is accomplished by:

  1. Specifying a topic type. In the message-driven-destination element of ejb-jar.xml, set destination-type to javax.jms.Topic. Alternatively, if using annotations, specify an ActivationConfigProperty with propertyName = "destinationType" and propertyValue = "javax.jms.Topic".

  2. Specifying a connection factory JNDI name and a destination JNDI name. Specifying a connection factory JNDI name is usually not necessary if the connection factory is hosted on the same cluster or server as the MDB. The default usually suffices.

  3. If the destination is not located in the same cluster or server as the MDB pool, administratively configure a mapping from the remote destination and connection factory JNDI entries to local JNDI entries that match those specified in #2, above. There are alternative approaches to referencing remote resources, but the mapping approach is the Oracle-recommended best practice.

For each free pool, the MDB container creates a connection using the specified connection factory, then uses the connection to find or create one or more subscriptions on its destination, and finally uses the connection to create JMS consumers that receive the messages from the subscription(s).

For the specific names of connection factory and destination MDB attributes, as well as recommended JNDI mapping configuration, see Configuring MDBs for Destinations.

Setting Subscription Durability

MDBs automatically create subscriptions on JMS topics. JMS topics support two types of subscriptions: durable and non-durable.

  • Non-durable subscriptions exist only for the length of time their subscribers exist. When a subscription has no more subscribers, the subscription is automatically deleted. Messages stored in a non-durable subscription are never recovered after a JMS server shut down or crash.

  • Durable subscriptions make it possible for a subscriber to receive messages that are published while the subscriber application is unavailable. For each durable subscription on a topic, JMS stores a copy of each published persistent message in a file or database until it can be delivered (or until it expires), even if there are no active subscribers on the subscription at the time the message is delivered. JMS also stores a copy of each non-persistent message in each durable subscription, but such messages are not recovered if the JMS server shuts down or crashes.

    Non-durable subscriptions are the default. To specify a durable subscription, in the message-driven-destination element of ejb-jar.xml, set subscription-durability to Durable. Alternatively, when using annotations, specify an ActivationConfigProperty with propertyName = "subscriptionDurability" and propertyValue = "Durable".

Setting Automatic Deletion of Durable Subscriptions

You can configure an MDB to automatically delete a durable topic subscription when the MDB is undeployed or deleted from a server. To configure an MDB to automatically delete durable topic subscriptions, set durable-subscription-deletion to True. By default, durable-subscription-deletion is set to False

Setting Container Managed Transactions

See Configuring Transaction Management Strategy for an MDB.

Setting Message Filtering (JMS Selectors)

JMS provides an SQL-like syntax for filtering messages based on standard JMS message header fields and message properties. In addition, WebLogic JMS supports an extension to the selector syntax that allows the specification of selectors that include XML "xpath" expressions for filtering XML messages based on their XML contents.

One way to specify a message selector is to specify it as the propertyValue for an ActivationConfigProperty with propertyName = "messageSelector".

The syntax of JMS selectors is fully described in the Javadoc for the javax.jms.Message class. The WebLogic xpath selector extension syntax is described in "Filtering Messages" in Programming JMS for Oracle WebLogic Server.

Controlling MDB Concurrency

As discussed in Appendix A, "Topic Deployment Scenarios,", an MDB deployment may create one or more MDB free pools. The max-beans-in-free-pool and dispatch-policy descriptor attributes work together to control MDB thread concurrency in an MDB free pool as follows:

  • For a discussion of how to determine the number of concurrent MDBs, see "Determining the Number of Concurrent MDBs" in Performance and Tuning for Oracle WebLogic Server.

  • When an MDB topicMessagesDistributionMode is set to Compatibility and the MDB uses container-managed transactions, concurrent MDB invocations are prevented. In addition, max-beans-in-free-pool should be explicitly set to 1 for bean-managed transaction MDBs that are driven by a foreign (non-WebLogic) topic.

    Caution:

    Non-transactional Foreign Topics: Oracle recommends explicitly setting max-beans-in-free-pool to 1 for non-transactional MDBs that work with foreign (non-WebLogic) topics. Failure to do so may result in lost messages in the event of certain failures, such as the MDB application throwing Runtime or Error exceptions.

    Unit-of-Order: Oracle recommends explicitly setting max-beans-in-free-pool to 1 for non-transactional Compatibility mode MDBs that consume from a WebLogic JMS topic and process messages that have a WebLogic JMS Unit-of-Order value. Unit-of-Order messages in this use case may not be processed in order unless max-beans-in-free-pool is set to 1.

See "Tuning Message-Driven Beans" in Performance and Tuning for Oracle WebLogic Server for more information.

Setting Subscription Identifiers

Individual JMS topic subscriptions are created and referenced based on their "subscription identifier," which an MDB generates based on a number of MDB configuration settings. For a discussion of the syntax of generated subscription identifiers, see Appendix B, "Topic Subscription Identifiers."

Setting Message Distribution Tuning

This section describes how and when to use message distribution tuning settings. It contains information that applies to all topic types (singleton, foreign, and distributed). The settings include the topicMessagesDistributionMode, distributedDestinationConnection, and generate-unique-client-id attributes. They control where topic subscriptions are created, what the subscription identifiers are, and whether an MDB processes each published topic message only once or once per server.

For detailed descriptions and diagrams of the resulting automatically generated subscription IDs, subscription locations, and deployed MDB free pool locations, see Appendix A, "Topic Deployment Scenarios," and Appendix B, "Topic Subscription Identifiers."

Setting topicMessagesDistributionMode

Use the topicMessagesDistributionMode setting in combination with the distributedDestinationConnection setting or the generate-unique-client-id setting to control topic message processing behavior. To set the topicMessagesDistributionMode, you can use the same-named @ActivationConfigProperty annotation or specify an <activation-config-property> in the ejb-jar.xml deployment descriptor.

The valid values for topicMessagesDistributionMode are:

  • One-Copy-Per-Application -- Specifies that the MDB application as a whole receives each message published to a distributed topic once, no matter how many servers host the application. This mode works with WebLogic JMS singleton and distributed topics in WebLogic Server 10.3.4 and later.

  • One-Copy-Per-Server -- Specifies that each deployment instance of the MDB application receives every message published to a distributed topic. This mode works with WebLogic JMS singleton and distributed topics in WebLogic Server 10.3.4 and later.

  • Compatibility - (Default) Specifies that the MDB application handles messages from distributed topics in the same way they were handled in WebLogic Server releases prior to 10.3.4. The mode supports durable and non-durable subscriptions with foreign (non-WebLogic) topics, local replicated distributed topics (RDTs), and singleton WebLogic topics; it also supports non-durable subscriptions with a remote replicated distributed topics. See the Compatibility notes section below for more detail.

    Note:

    Oracle recommends using the One-Copy-Per-Application and One-Copy-Per-Server modes for most new applications, except those that must consume from WebLogic JMS topics in versions of WebLogic Server prior to 10.3.4 or from foreign (non-WebLogic) topics.

    The topic distribution modes support different topic types and versions with the following restrictions:

    • The One-Copy-Per-Application and One-Copy-Per-Server modes work only with WebLogic singleton and distributed topics in WebLogic Server 10.3.4 and later. WebLogic MDBs log a warning and do not process messages with these modes when using a foreign (non-WebLogic) topic or when using a WebLogic topic from WebLogic Server releases prior to 10.3.4.

    • One-Copy-Per-Application topic MDBs that are durable, that subscribe to a local RDT, and that use the default LocalOnly value for the distributedDestinationConnection attribute, do not support Service Migration and require that exactly one topic member be configured per WebLogic Server instance. If a service migration occurs, if there is no local topic member configured, or if more than one topic member is deployed per server, then the application may experience duplicate or lost messages and may also create abandoned subscriptions that accumulate unprocessed messages. If service migration is required, then use the EveryMember option for the distributedDestinationConnection attribute instead of the default LocalOnly.

    • The Compatibility mode supports durable and non-durable subscriptions with foreign (non-WebLogic) topics, with local replicated distributed topics (RDTs) (with limitations described later), and with singleton WebLogic topics. Compatibility mode also supports non-durable subscriptions with a remote RDT. A deployment of a durable MDB that subscribes to the logical JNDI name of a remote RDT may succeed, but the MDB deployment will fail to connect, with Warning log messages. Similarly, a deployment of an MDB that subscribes to a WebLogic PDT may succeed, but the MDB deployment will fail to connect, with Warning log messages. See "Notes on the Compatibility mode of topicMessagesDistributionMode" below, for more detail.

    • Compatibility mode MDBs that are durable and subscribes to a local RDT, see Notes on the Compatibility mode of topicMessagesDistributionMode.

    For a detailed descriptions and diagrams of MDB generated subscriptions, subscription IDs, and free pool locations, refer to Appendix A, "Topic Deployment Scenarios," and Appendix B, "Topic Subscription Identifiers.".

Setting distributedDestinationConnection

To optionally fine tune the behavior of the One-Copy-Per-Application and One-Copy-Per-Server modes of topicMessagesDistributionMode for a local distributed topic, you can use the distributedDestinationConnection activation config property. Alternatively, you can use the distributed-destination-connection element in the weblogic-ejb-jar.xml deployment descriptor. The valid values are LocalOnly and EveryMember.

The distributedDestinationConnection setting specifies whether a WebLogic Server MDB container sets up a local MDB free pool for each subscription in the entire cluster (EveryMember), or local free pools only for subscriptions on members local to the current WebLogic Server (LocalOnly - the default).

The use of distributedDestinationConnection is restricted as follows: if it is specified for an MDB that subscribes to a remote cluster, a warning message is given and the option is ignored. If you try to use it in Compatibility mode, a warning is given and the option is ignored.

One reason to use EveryMember is that the LocalOnly option for durable MDBs has restrictions for local RDTs in the One-Copy-Per-Server mode. See Warning About Using Local RDTs with Durable MDBs.

Another reason to use EveryMember is to better handle uneven message loads or message processing delays. See Handling Uneven Message Loads and/or Message Processing Delays, for advice.

Notes on the Compatibility mode of topicMessagesDistributionMode

  • See Setting topicMessagesDistributionMode, above, for a statement about supported topic types and versions.

  • If you're using the Compatibility topicMessagesDistributionMode in combination with non-transactional MDBs, and the topic is a foreign (non-WebLogic) destination, or the topic is a WebLogic destination with Unit-of-Order (UOO) messages, then see Controlling MDB Concurrency, for warnings.

  • Set the generate-unique-client-id attribute to change behavior:

    • If generate-unique-client-id is set to true, each durable MDB free pool generates a unique subscriber ID. Each MDB free pool will then receive a copy of each published message. For more information see Appendix B, "Topic Subscription Identifiers." For more information about free pools, see MDBs and Concurrent Processing, and Appendix A, "Topic Deployment Scenarios."

    • If generate-unique-client-id is false (the default), only one subscription will be created by a durable MDB, and only one MDB free pool will successfully connect to the durable subscription (the remaining MDB pools will fail to connect, log warnings, and will keep retrying).

  • For durable subscription MDBs that subscribe to the logical name of a local replicated distributed topic (a local RDT), only the configuration described in Warning About Using Local RDTs with Durable MDBs, below, is supported.

  • For durable subscription MDBs that subscribe to the logical name of a remote replicated topic (a remote RDT):

    • A deployment of a durable MDB that subscribes to the logical JNDI name of the RDT may succeed, but the MDB deployment will fail to connect, with Warnings logs.

  • For durable subscription MDBs that subscribe to a particular member destination of a remote replicated topic:

    • A deployment of a durable MDB that subscribes directly to a member of the RDT will succeed, and the subsequent behavior will be determined by the generate-unique-client-id setting, as described above. For a uniform distributed destination, the JNDI name of a particular member is "jms-server-name@udd-jndi-name".

  • For a non-durable subscription MDB that subscribes to the logical name of a local replicated distributed topic (a local RDT), the logical name of a remote replicated distributed topic (a remote RDT), a foreign topic, or a singleton topic, each server will receive a copy of each message that is sent to the topic.

  • The distributedDestinationConnection option does not apply to Compatibility mode. When set, a warning is given and it is ignored.

Best Practices

Consider the information in the following sections to help you configure MDBs.

Warning about Non-Transactional MDBs in Compatibility Mode

If you're using the Compatibility mode of topicMessagesDistributionMode in combination with non-transactional MDBs and the topic is a foreign (non-WebLogic) destination or the topic is a WebLogic destination with Unit-of-Order (UOO) messages, see Controlling MDB Concurrency, above, for warnings.

Warning About Using Local RDTs with Durable MDBs

In Compatibility mode, for durable subscription MDBs that subscribe to the logical name of a local replicated distributed topic (a local RDT), only the following configuration is supported:

  • Always set generate-unique-client-id to true.

  • Ensure each WebLogic Server in the cluster hosts exactly one member of the RDT.

  • Do not use WebLogic JMS service-migration. It is unsupported for this use case; but you can use "whole server migration."

  • Note that each server receives a copy of each message sent to the topic. When a message arrives at one of the RDT physical topic members, the RDT automatically ensures that a copy of the message is forwarded to each of the other members of the topic.

Similarly, in One-Copy-Per-Application mode when distributedDestinationConnection is set to LocalOnly, for durable subscription MDBs that subscribe to the logical name of a local replicated distributed topic (a local RDT), only the following configuration is supported:

  • If your configuration does not match the above recommendations, you may get nondeterministic behavior, including lost messages, duplicate messages, and stuck messages. For more information, including alternatives, see Setting topicMessagesDistributionMode, above.

Warning about Changing Durable MDB Attributes, Topic Type, EJB Name

Changing MDB or JMS settings can cause the current messages on durable subscriptions to be deleted, or can cause existing durable subscriptions to be abandoned, deleted, or replaced in favor of new durable subscriptions. These settings include topic type, JMS selector, distribution tuning, subscription durability, ejb-name, and, client-id.

Abandoned durable subscriptions continue to accumulate messages even though no MDB is processing the messages. This can eventually lead to quota exceptions or even JVM out-of-memory errors that prevent additional messages from being published to the topic.

For a discussion about locating and removing abandoned subscriptions see "Managing Durable Subscriptions" in Programming JMS for Oracle WebLogic Server. For a discussion about subscription IDs and locations, see Appendix B, "Topic Subscription Identifiers."

Choosing Between Partitioned and Replicated Topics

Supported Topic Types, above, describes the two types of WebLogic distributed topics (partitioned and replicated). In general, Oracle recommends using partitioned topics (PDTs), when available, except for these two cases:

  • When replicated topic (RDT) behavior is required to interoperate with legacy applications or non-MDB applications.

  • In the local RDT case in the One-Copy-Per-Server LocalOnly case under certain message loads. The message load determines whether the heavy forwarding overhead built into an RDT is less expensive in comparison to the increased network traffic required for the fully connected topology in the PDT One-Copy-Per-Server mode. In general, it is better to use a PDT for non-persistent or "lighter" persistent message loads.

To configure a distributed topic type, you set Partitioned or Replicated as the value for the WebLogic JMS Distributed Topic configuration attribute JMS Forwarding Policy. For more information, see "Configuring Partitioned Distributed Topics" in Configuring and Managing JMS for Oracle WebLogic Server.

Choosing an MDB Topic Messages Distribution Mode

Oracle recommends using the One-Copy-Per-Application and One-Copy-Per-Server modes for most new applications, except for those that must consume from WebLogic JMS topics in WebLogic Server releases prior to 10.3.4 or from foreign (non-WebLogic) topics. These two modes only work with WebLogic JMS topics in WebLogic Server 10.3.4 or later.

Managing and Viewing Subscriptions:

See Appendix A, "Topic Deployment Scenarios," and Appendix B, "Topic Subscription Identifiers," for detailed discussions of the names and location of subscriptions.

See also "Managing Durable Subscriptions" in Programming JMS for Oracle WebLogic Server.

Handling Uneven Message Loads and/or Message Processing Delays

For applications with uneven message loads or unanticipated message processing delays, you may want to consider the following:

  • For local distributed topics when the topic distribution mode is One-Copy-Per-Server or One-Copy-Per-Application, tune distributedDestinationConnection to EveryMember. While the LocalOnly option can yield significantly better performance since it avoids unnecessary network traffic, there are use cases where the LocalOnly optimization network savings does not outweigh the benefit of distributing message processing for unbalanced queue loads as evenly as possible across all JVMs in a cluster. This is especially a concern when message backlogs develop unevenly throughout the cluster and message processing is expensive. In these use cases, the LocalOnly configuration should be avoided in favor of the EveryMember scenario with durable subscribers.

  • Use a PDT instead of an RDT, and tune producer load balancing in the producer's connection factory configuration so that each producer's messages are evenly processed on a round-robin basis throughout the cluster. Incoming messages can be load balanced among the distributed topic members using the WebLogic JMS connection factory Server Affinity Enabled and Load Balancing Enabled attributes. Disabling affinity can increase network overhead but helps ensure that messages are evenly load balanced across a cluster. The affinity setting has no effect with RDTs. See "Load Balancing Messages Across a Distributed Destination" in Configuring and Managing JMS for Oracle WebLogic Server.

  • Decrease the WebLogic JMS asynchronous message pipeline size to 1 to prevent additional messages from being pushed to an MDB thread that is already blocked processing a previous message. The default for this setting is 10, and it is configured by (a) configuring a custom WebLogic connection factory with the Messages Maximum attributed tuned to 1 and XA Enabled set to true, (b) targeting the connection factory to the same cluster that hosts the distributed topic, and (c) modifying the MDB so that it references the custom connection factory.

Configuring for Service Migration

For durable subscriptions, JMS service migration (auto or manual) is not supported once LocalOnly is applied on local replicated topics. Normally LocalOnly means the MDB deployment instance is pinned on the local distributed topic member once the distributed topic member is migrated to another server. The MDB deployment instance cannot subscribe to the same original distributed member after a restart, which may cause warning messages to be generated. Therefore, to use JMS service migration, you should configure as EveryMember. Whole server migration is supported for both cases.

Upgrading Applications from Previous Releases

As described throughout this chapter, new JMS features in WebLogic Server 10.3.4, such as relaxed client ID, sharable subscriptions, and partitioned durable topics, make it possible to implement and deploy MDBs that provide enhanced scalability and high availability. To take advantage of these features, you must upgrade MDB applications written for releases of WebLogic Server prior to 10.3.4.

Applications written to run on releases of WebLogic Server prior to 10.3.4 will continue to run without modification in Compatibility mode, which is the default setting for topicMessagesDistributionMode, as described in Setting topicMessagesDistributionMode.

To upgrade applications from previous releases,

  1. Consider changing to a partitioned distributed topic. See Choosing Between Partitioned and Replicated Topics, above.

  2. Set the topicMessagesDistributionMode to One-Copy-Per-Server or One-Copy-Per-Application and tune the distributedDestinationConnection options. See Setting Message Distribution Tuning, above.

Caution:

Current messages are not preserved when changing out of Compatibility mode. See Warning about Changing Durable MDB Attributes, Topic Type, EJB Name.

Topic MDB Sample

Example 10-1 shows a WebLogic MDB that uses a durable subscription to a JMS topic (in 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 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 described in "Enhanced Support for Using WebLogic JMS with EJBs and Servlets" in Programming JMS for Oracle WebLogic Server.

For a similar sample using queues instead of topics, see Example 7-3, "Sample MDB Using Distributed Queues".

Example 10-1 Sample MDB Using Distributed Topics

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.Topic"),
 
    @ActivationConfigProperty(propertyName  = "subscriptionDurability",
                              propertyValue = "Durable"),
 
    @ActivationConfigProperty(propertyName  = "connectionFactoryJndiName",
                              propertyValue = "MyCF"), // External JNDI Name
 
    @ActivationConfigProperty(propertyName  = "destinationJndiName",
                              propertyValue = "MyTopic"), // Ext. JNDI Name
 
    @ActivationConfigProperty(propertyName  = "topicMessagesDistributionMode",
                              propertyValue = "One-Copy-Per-Application") 
  }
)
 
@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
    }
  }
 
}