3 Enhanced Support for Using WebLogic JMS with EJBs and Servlets

Learn how to use WebLogic JMS wrappers when accessing WebLogic JMS or third-party JMS providers from EJBs and servlets. The wrappers provide resource pooling and container integration for JMS operations.

Enabling WebLogic JMS Wrappers

WebLogic Server uses JMS wrappers that make it easier to use WebLogic JMS inside a Jakarta EE component, such as an EJB or a servlet.

The JMS wrappers also provide a number of enhanced usability and performance features:

  • Automatic pooling of JMS connection and session objects (and some pooling of message producer objects as well)

  • Automatic transaction enlistment for WebLogic JMS implementations and for third-party JMS providers that support two-phase commit transactions (XA protocol)

  • Testing of the JMS connection, as well as reestablishment after a failure

  • Security credentials that are managed by the EJB or servlet container

The following sections provide information on how to use WebLogic JMS wrappers:

Declaring a JMSContext Object Using @Inject Annotation

In a Jakarta EE component that supports CDI injection, use @Inject to obtain a container-managed JMSContext. The following example selects a JMS connection factory:

@Inject
@JMSConnectionFactory("myJMSCF")
private JMSContext context;

The container manages the injected JMSContext. Its underlying context is created when first used in the active transaction or request and is closed when that scope ends. Application code must not close an injected JMSContext.

Note:

  • Injection should be enabled for the class. Depending on the class being used and the archive in which it is packaged, it may be necessary to specify a beans.xml file. For more information, see Using Contexts and Dependency Injection for the Jakarta EE Platform in Developing Applications for Oracle WebLogic Server.

  • If the field is null, verify that the containing object is managed by the container and that CDI injection is enabled for its class and archive. Check the application's CDI configuration, including beans.xml when required. If using a non-null injected JMSContext fails, inspect the exception and server log, and verify the connection-factory lookup name and availability.

Specifying a Lookup Name in JMSContext Injection

When injecting a JMSContext object, you can use the @JMSConnectionFactory annotation to specify the product-specific global JNDI look up name of a connection factory to be used by the container.

Note:

When you provide a product-specific global JNDI name for the connection factory annotation, you cannot override it using a resource reference in the deployment descriptor of the container.

Alternatively, you can specify a fully qualified resource reference name of the form java:comp/env/res-ref-name as follows:

@Inject
@JMSConnectionFactory("java:comp/env/res-ref-name")
private JMSContext context;

In this case, declare the component's connection-factory resource reference and map it to the desired connection factory. You can declare the reference with a resource-ref element in the deployment descriptor or with an appropriate @Resource declaration. See Declaring a Wrapped JMS Factory using Deployment Descriptors.

If @JMSConnectionFactory is omitted, the container uses the Jakarta EE default JMS connection factory at java:comp/DefaultJMSConnectionFactory. When the annotation is present, supply its connection-factory lookup name.

Determining the Authentication Type for JMSContext Injection

When @JMSConnectionFactory names a connection-factory resource reference, the reference's authentication setting applies. To supply a user name and password through @JMSPasswordCredential, configure the resource reference with res-auth set to Application. WebLogic Server rejects explicit credentials when the reference uses Container authentication.

Omitting @JMSPasswordCredential does not change a resource reference's authentication setting. A reference configured for Container continues to use container authentication. A reference configured for Application creates the context without explicitly supplied credentials; the JMS provider determines whether that connection can be authenticated.

For a direct connection-factory lookup handled by the WebLogic JMS wrappers, the credentials in @JMSPasswordCredential are used when the annotation is present. Container authentication is used when the annotation is absent.

Declaring JMS Objects as Resources In the EJB or Servlet Deployment Descriptors

The following sections provide information on declaring JMS objects as resources:

For more information about packaging EJBs, see Implementing Enterprise JavaBeans in Developing Jakarta Enterprise Beans Using Deployment Descriptors. For more information about programming servlets, see Creating and Configuring Servlets in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.

Declaring a Wrapped JMS Factory using Deployment Descriptors

Note:

New applications will likely use EJB 3.0 annotations instead of deployment descriptors. Annotations are described in Declaring JMS Destinations and Connection Factories Using Annotations.

You can declare a JMS connection factory as part of an EJB or servlet by defining a resource-ref element in the ejb-jar.xml or web.xml file, respectively. This process creates a "wrapped" JMS connection factory that can benefit from the more advanced session pooling, automatic transaction enlistment, connection monitoring, and container-managed security features described in Improving Performance Through Pooling.

Here is an example of such a connection factory element:

<resource-ref>
 <res-ref-name>jms/QCF</res-ref-name>
 <res-type>jakarta.jms.QueueConnectionFactory</res-type>
 <res-auth>Container</res-auth>
 <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

This element declares that a JMS QueueConnectionFactory object be bound into JNDI, at the location:

java:comp/env/jms/QCF

This JNDI name is only valid inside the context of the EJB or servlet where the resource-ref is declared, which is what the java:comp/env JNDI context signifies.

For the mapping shown here, add a matching resource-description element to weblogic-ejb-jar.xml for an EJB or to weblogic.xml for a web application. The res-ref-name must match the reference declared in the standard deployment descriptor. For example:

<resource-description>
 <res-ref-name>jms/QCF</res-ref-name>
 <jndi-name>weblogic.jms.ConnectionFactory</jndi-name>
</resource-description>

The example maps the reference to weblogic.jms.ConnectionFactory, one of the WebLogic default connection factories. This factory is available when JMS Default Connection Factories Enabled is enabled on the hosting WebLogic Server instance. To use another locally bound factory, specify its JNDI name in jndi-name. A Foreign Server mapping can provide a local JNDI name for a factory in another WebLogic domain or supplied by another JMS provider.

An invalid connection-factory mapping can fail when the reference is resolved or used. This may occur during component initialization or later during application execution; successful deployment does not by itself verify that the connection factory is available.

Declaring JMS Destinations using Deployment Descriptors

You can use a jms-destination element in a web, EJB, application-client, or application deployment descriptor to define a JMS destination resource. Use a resource-env-ref element to declare a reference to a destination; the reference does not create the destination.

Note:

New applications will likely use EJB 3.2 annotations instead of deployment descriptors. Annotations are described in Declaring JMS Destinations and Connection Factories Using Annotations.

The transaction enlistment, pooling, connection monitoring features take place in the connection factory, not in the destinations. However, this feature is useful for consistency, and to make an application less dependent on a particular configuration of WebLogic Server, since destinations can easily be modified by simply changing the corresponding jms-destination or resource-env-ref description, without having to recompile the source code

Declaring JMS Destinations Using the jms-destination Element

You can define a JMS destination resource using the jms-destination element in the ejb-jar.xml or web.xml deployment descriptors. It creates the destination and binds it to the appropriate naming context based on the namespace specified.

The following example defines a queue destination myQueue1 that is bound to JNDI at the location java:app/MyJMSDestination:

<jms-destination>
  <description>JMS Destination definition</description>
  <name>java:app/MyJMSDestination</name>
  <interface-name>jakarta.jms.Queue</interface-name>
  <destination-name>myQueue1</destination-name>
</jms-destination>

For the jms-destination element and its child elements, see jms-destinationType in the Jakarta EE 11 common schema.

Declaring JMS Destinations Using the resource-env-ref Element

You can also bind a JMS queue or topic destination into the java:comp/env JNDI tree by declaring it as a resource-env-ref element in the ejb-jar.xml or web.xml deployment descriptors.

A resource-env-ref declares a reference to a destination; it does not create that destination. The destination must be available when the reference is resolved. An invalid destination mapping can fail during injection or startup, or later when the application looks up or uses the destination.

Here is an example of such a queue destination element:

<resource-env-ref>
 <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
 <resource-env-ref-type>jakarta.jms.Queue</resource-env-ref-type>
</resource-env-ref>

This element declares that a JMS Queue destination object will be bound into JNDI, at the location:

java:comp/env/jms/TESTQUEUE

As with a referenced connection factory, this JNDI name is only valid inside the context of the EJB or servlet where the resource-ref is declared.

You must also define a matching resource-env-description element in the weblogic-ejb-jar.xml or weblogic.xml file. This provides a layer of indirection that enables you to easily modify referenced destinations just by changing the corresponding resource-env-ref deployment descriptors.

<resource-env-description>
 <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
 <jndi-name>jmstest.destinations.TESTQUEUE</jndi-name>
</resource-env-description>

Referencing a Packaged JMS Application Module In Deployment Descriptor Files

When you package a JMS module with an enterprise application, you must reference the JMS resources within the module in all applicable descriptor files of the Jakarta EE application components, including:

  • The WebLogic enterprise descriptor file, weblogic-application.xml

  • Any WebLogic deployment descriptor file, such as weblogic-ejb-jar.xml or weblogic.xml

  • Any Jakarta EE descriptor file, such as EJB (ejb-jar.xml) or WebApp (web.xml) files

Referencing Application Modules in a weblogic-application.xml Descriptor

When including JMS modules in an enterprise application, you must list each JMS module as a module element of type JMS in the weblogic-application.xml descriptor file packaged with the application, and a path that is relative to the root of the Jakarta EE application. Here is an example of a reference to a JMS module name Workflows:

<module>
  <name>Workflows</name>
  <type>JMS</type>
  <path>jms/Workflows-jms.xml</path>
</module>
Referencing JMS Resources in a WebLogic Application

Within any weblogic-foo descriptor file, such as EJB (weblogic-ejb-jar.xml) or WebApp (weblogic.xml), the name of the JMS module is followed by a number (#) separator character, which is followed by the name of the resource inside the module. For example, a JMS module named Workflows that contains a queue named OrderQueue, would have a name of Workflows#OrderQueue.

<resource-env-description>
  <resource-env-ref-name>jms/OrderQueue</resource-env-ref-name>
  <resource-link>Workflows#OrderQueue</resource-link>
</resource-env-description>

Note that the <resource-link> element is unique to WebLogic Server, and is how the resources that are defined in a JMS module are referenced (linked) from the other Jakarta EE Application components.

Referencing JMS Resources in a Jakarta EE Application

Declare a connection-factory reference with resource-ref and a destination reference with resource-env-ref in the component's standard deployment descriptor. These reference names are the names used by the component in java:comp/env.

Match each res-ref-name to the res-ref-name in its WebLogic resource-description mapping. Match each resource-env-ref-name to the resource-env-ref-name in its WebLogic resource-env-description mapping. The mapping's resource-link identifies the JMS module and resource; the component reference name does not have to equal the JMS resource's name. For example, jms/OrderQueue can map to Workflows#OrderQueue, as shown in Referencing JMS Resources in a WebLogic Application.

The following declarations provide the component reference names and types:

<resource-ref>
  <res-ref-name>jms/OrderQueueFactory</res-ref-name>
  <res-type>jakarta.jms.ConnectionFactory</res-type>
</resource-ref>
<resource-env-ref>
  <resource-env-ref-name>jms/OrderQueue</resource-env-ref-name>
  <resource-env-ref-type>jakarta.jms.Queue</resource-env-ref-type>
</resource-env-ref>

Declaring JMS Destinations and Connection Factories Using Annotations

WebLogic Server 10.0 and later releases support the EJB 3.0 programming model which uses annotations to configure metadata, eliminating the need for deployment descriptors. You can declare JMS objects using the @Resources annotation as described in Standard JDK Annotations Used By EJB 3.0 in Developing Jakarta Enterprise Beans for Oracle WebLogic Server.

Injecting Resource Dependency into a Class

Apply @Resource to a field or setter method to request injection of a JMS destination or connection factory into a container-managed component. The following example uses field injection. A class-level @Resource or @Resources declaration creates resource references for lookup; it does not inject a field. See Non-Injected EJB 3.0 Resource Reference Annotations.

Example 3-1 is a Wrapped JMS Pooling Annotation example:

Example 3-1 Wrapped JMS Pooling Annotation Example

.
.
.
// The "name=" or "type=" are not always required,
// "mappedName=" is usually sufficient.
@Resource(name="ReplyQueue",
 type=jakarta.jms.Queue.class,
 mappedName="jms/ReplyQueue") Destination rq;
.
.
.
@Resource(name="ReplyConnectionFactory",
 type=jakarta.jms.ConnectionFactory.class,
 mappedName = "jms/ConnectionFactory") ConnectionFactory cf;
.
.
.
Non-Injected EJB 3.0 Resource Reference Annotations

Injected resource dependencies are resolved when the host EJB or servlet is instantiated. You may not want injected resource because:

  • The injection may prevent applications from deploying successfully if the container attempts to resolve references during deployment.

  • You might want to defer reference resolution until the application is first invoked.

You can setup a non-injected resource reference by placing the @Resources annotation above the class definition. An application can resolve such references at runtime by looking up the reference in the bean context. As a best practice, the bean or servlet should also cache the result in order to avoid the overhead of repeated lookups as shown in Example 3-2:

For a full example, see EJB 3.0 Wrapper Without Injection.

Example 3-2 Non-Injected Resource Example

.
.
.
@Resources ({
  @Resource(name="targetCFRef",
            mappedName="TargetCFJNDIName",
            type=jakarta.jms.ConnectionFactory.class),
 
  @Resource(name="targetDestRef",
            mappedName="TargetDestJNDIName",
            type=jakarta.jms.Destination.class)
})
 
@Stateless(mappedName="StatelessBean")
public class MyStatelessBean implements MyStateless {
 
  @Resource
  private SessionContext sctx;   // inject the bean context
 
  private ConnectionFactory targetCF;
  private Destination targetDest;
 
  public void completeWorkOrder() {
 
      // Lookup the JMS resources and cache for re-use.  Note that a 
      // "java:comp/env" prefix isn't needed for EJB3.0 bean contexts.
 
      if (targetCF == null) targetCF =    
        (jakarta.jms.ConnectionFactory)sctx.lookup("targetCFRef");
 
      if (targetDest == null) targetDest =
        (jakarta.jms.Destination)sctx.lookup("targetDestRef");
.
.
.

Avoid Transactional XA Interfaces

With resource wrapping, do not use the jakarta.jms XA interfaces. The container uses them internally if the JMS code is used inside a transaction context. This allows your EJB application code to run EJBs in an environment where transactions are present or in a non-transactional environment, just by changing the deployment descriptors.

Disabling Wrapping and Pooling

It is sometimes desirable to leverage resource references but disable resource reference wrapping and pooling.

To disable resource wrapping and pooling, use the deployment descriptor approach, but change the res-type to java.lang.Object in the resource-ref stanza for the connection factory. There is currently no known way to disable wrapping and pooling using annotations.

What's Happening Under the JMS Wrapper Covers

Understand what is actually taking place under the covers when WebLogic Server creates a set of wrappers around the JMS objects.

For example, the code fragment in Sending a JMS Message in a Jakarta EE Container, shows an instance of a WebLogic-specific wrapper class being returned, rather than the actual JMS connection factory because the connection factory was looked up from the java:comp/env JNDI tree. This wrapper object intercepts certain calls to the JMS provider and inserts the correct Jakarta EE behavior, as described in the following sections.

Automatically Enlisting Transactions

A wrapped JMS session can participate automatically in an active Jakarta transaction when the selected connection factory provides the required transaction support. This applies to container-managed EJB transactions and to transactions demarcated with UserTransaction where application-managed transaction demarcation is permitted.

If the wrapped session cannot participate in the active transaction, the classic JMS operation fails with a JMSException. The current message identifier is 169822:

Unable to use a wrapped JMS session in the transaction because two-phase commit is not available.

Use a connection factory configured for XA participation when the messaging operation must be part of the Jakarta transaction. Otherwise, arrange for that operation to run outside an active transaction. For an EJB using container-managed transactions, use the NOT_SUPPORTED transaction attribute (NotSupported in a deployment descriptor) where appropriate. In a servlet or bean-managed-transaction EJB, use the permitted UserTransaction boundaries to control whether the messaging operation is transactional.

Container-Managed Security

WebLogic JMS uses the security credentials that are present on the thread when the EJB or servlet container is invoked. For foreign JMS providers, however, when you declare a JMS connection factory through a resource-ref element in the ejb-jar.xml or web.xml file, there is an optional sub element called res-auth. This element may have one of two settings:

Container — When you set the res-auth element to Container, security to the JMS provider is managed by the Jakarta EE container. In this case, if the JMS connection factory was mapped into the JNDI tree using a Foreign JMS Connection Factory configuration MBean, then the user name and password from that MBean is used. Otherwise, WebLogic Server connects to the provider with no user name or password specified and throws an error if the createConnection() method is used to pass a user name and password to the connection factory.

Application — When you set the res-auth element to Application, any user name or password on the MBean is ignored. Instead, the application code must specify a user name and password to the createConnection(String userName, String password) method of the JMS connection factory, or use the version of createConnection() with no parameters if the user name or password are not required.

Note:

Injecting a JMSContext does not override a connection-factory resource reference's authentication setting. To supply credentials through @JMSPasswordCredential when using such a reference, configure it for Application authentication. See Determining the Authentication Type for JMSContext Injection.

Connection Testing

The JMS wrappers register an ExceptionListener on pooled connections to detect provider-reported failures. When connection health checking is enabled and a temporary destination and consumer can be created, a pool tester sends and receives a test message at two-minute intervals.

If health checking is disabled or the temporary-message test cannot be initialized, those probes are not run; exception-listener monitoring remains available. For foreign connection factories, ConnectionHealthChecking controls the temporary-message test and defaults to enabled.

Jakarta EE Compliance

EJB and servlet applications must follow Jakarta Messaging restrictions. For the classic API, do not call:

  • On Connection: createConnectionConsumer, createDurableConnectionConsumer, createSharedConnectionConsumer, createSharedDurableConnectionConsumer, setClientID, setExceptionListener, or stop.

  • On Session: getMessageListener, setMessageListener, or run.

  • On MessageConsumer: getMessageListener or setMessageListener.

These containers also restrict JMSContext methods, limit each Connection to one active Session, and prohibit CompletionListener-based asynchronous sends. See sections 12.2 and 12.3 of the Jakarta Messaging 3.1 specification for the complete rules.

For portable code using the classic API, create sessions with createSession(false, Session.AUTO_ACKNOWLEDGE). An active JTA transaction controls completion of participating JMS operations. When a Session is created through the classic API without an active JTA transaction, nontransacted sessions using AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE retain their specified behavior; support for local transactions and CLIENT_ACKNOWLEDGE is optional and should not be assumed in portable applications. See Automatically Enlisting Transactions.

Note:

Use a message-driven bean for asynchronous message receipt in an EJB application. See Developing Message-Driven Beans for Oracle WebLogic Server.

Pooled JMS Connection Objects

The JMS wrappers pool various session objects in order to make code like the example provided in Sending a JMS Message in a Jakarta EE Container more efficient. A pooled JMS connection is a session pool used by EJBs and servlets that use a resource-ref element in their deployment descriptor to define their JMS connection factories, as discussed in Declaring a Wrapped JMS Factory using Deployment Descriptors.

Improving Performance Through Pooling

The automatic pooling of connections and other objects by the JMS wrappers means that it is efficient to write code.

For example, see the example in Sending a JMS Message in a Jakarta EE Container. Although in this example the Connection Factory, Connection, and Session objects are created every time a message is sent, in reality these three classes work together so that when they are used as shown, they do little more than retrieve a Session object from the pool.

Speeding Up JNDI Lookups by Pooling Session Objects

The JNDI lookups of the Connection Factory and Destination objects can be expensive in terms of performance. This is particularly true if the Destination object points to a Foreign JMS Destination MBean, and therefore, is a lookup on a non local JNDI provider. Because the Connection Factory and Destination objects are thread-safe, they can be looked up after they are inside an EJB or servlet at creation time, which saves the time required to perform the lookup each time.

Inside a servlet, these lookups can be performed inside the init() method. The Connection Factory and Destination objects can then be assigned to an instance variable and reused whenever a message is sent.

Inside an EJB, these lookups can be performed inside the ejbCreate() method and assigned to an instance variable. For a session bean, each instance of the bean will then have its own copy. Because stateless session beans are pooled, this method is also very efficient (and is consistent with the Jakarta EE specifications), because the number of a times that lookups occur is drastically reduced by pooling the JMS connection objects. (Caching these objects in a static member of the EJB class may work, but it is discouraged by the Jakarta EE specification.)

Cached connection factories and destinations may become unusable after a provider failure. If an operation fails because a cached object is no longer valid, invalidate that object and repeat its JNDI lookup before a later attempt. Recovery depends on the provider and configuration; a server restart does not always require a new lookup. PoolTestBean.java illustrates invalidating cached references after a failure and looking them up again on a later invocation.

Speeding Up Object Creation Through Caching

Acquire connections, sessions, and producers when needed and close them after use so the wrappers can return their underlying resources to the pool. Keeping these handles for the lifetime of a bean reduces the resources available for reuse. After a provider failure, discard failed handles and acquire new ones. Do not rely on pool recovery to repair a handle already returned to the application.

Enlisting the Proper Transaction Mode

Use an XA-capable connection factory when JMS work must participate in JTA. A container-managed EJB transaction or a transaction begun through UserTransaction can provide that context. Servlets may use UserTransaction; they do not use EJB container-managed transaction attributes. The wrappers enlist a foreign XA resource as needed, while WebLogic JMS uses its native transaction integration. An operation cannot participate in JTA if the required transaction support is unavailable.

Outside an existing JTA transaction, wrapper transaction handling depends on the provider and operation. An XA session can require a transaction around an individual operation, but native WebLogic JMS producer sends are not automatically wrapped in a new JTA transaction solely because the factory supports XA.

Choose a connection factory according to the required transaction semantics, and measure overhead with a representative workload. Do not disable XA for a resource used by work that must participate in JTA.

Simplified Access to Foreign JMS Providers

Learn how to access foreign JMS providers by using WebLogic Remote Console.

Foreign JMS server configuration maps connection factories and destinations from an external JNDI provider into the local WebLogic JNDI tree. The provider can be another WebLogic Server cluster or domain, or a third-party JMS provider. See Accessing Foreign JMS Providers in Administering JMS Resources for Oracle WebLogic Server.

The configuration contains the following resources:

  • Foreign server: Specifies the remote JNDI provider's initial context factory, connection URL, and additional JNDI properties. It contains the foreign connection-factory and destination mappings and determines where those mappings are deployed. Target the foreign server to the applicable WebLogic server or cluster. See ForeignServerBean in the MBean Reference for Oracle WebLogic Server.

  • Foreign connection factory: Maps a local JNDI name to the connection factory's JNDI name in the remote provider. It can also specify a user name and password for container-managed connection authentication. A non-replicated local binding is created on each WebLogic Server instance where its parent foreign server is deployed. See ForeignConnectionFactoryBean in the MBean Reference for Oracle WebLogic Server.

  • Foreign destination: Maps a local JNDI name to a destination's JNDI name in the remote provider.

Examples of JMS Wrapper Functions

JMS wrapper functions make it easier to use WebLogic JMS inside a Jakarta EE component, such as an EJB or a servlet.

Stateless Session Bean Example

The following files define a stateless session bean with an EJB2.x remote/home view. Its sendXATransactional method sends a message through JMS resource references within the container-managed transaction configured by the deployment descriptors. Configure the TESTQUEUE destination before deployment. The later MDB example shows a separate message-driven bean that uses JMS resource references.

ejb-jar.xml

This section describes the EJB components. For the "JMS wrapper" code examples provided in this section, note that this section declares the resource-ref and resource-env-ref elements for the wrapped JMS connection factory (QueueConnectionFactory) and referenced JMS destination (TESTQUEUE).

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
         version="2.1">
 <enterprise-beans>
 <session>
  <ejb-name>PoolTestBean</ejb-name>
  <home>weblogic.jms.pool.test.PoolTestHome</home>
  <remote>weblogic.jms.pool.test.PoolTest</remote>
  <ejb-class>weblogic.jms.pool.test.PoolTestBean</ejb-class>
  <session-type>Stateless</session-type>
  <transaction-type>Container</transaction-type>

  <resource-ref>
   <res-ref-name>jms/QCF</res-ref-name>
   <res-type>jakarta.jms.QueueConnectionFactory</res-type>
   <res-auth>Container</res-auth>
   <res-sharing-scope>Shareable</res-sharing-scope>
  </resource-ref>

  <resource-env-ref>
  <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
   <resource-env-ref-type>jakarta.jms.Queue</resource-env-ref-type>
  </resource-env-ref>
 </session>
 </enterprise-beans>

 <assembly-descriptor>
  <container-transaction>
   <method>
    <ejb-name>PoolTestBean</ejb-name>
    <method-name>*</method-name>
   </method>
   <trans-attribute>Required</trans-attribute>
  </container-transaction>
 </assembly-descriptor>
</ejb-jar>
weblogic-ejb-jar.xml

The resource-description element maps the jms/QCF connection-factory reference to weblogic.jms.XAConnectionFactory. The resource-env-description element maps the jms/TESTQUEUE destination reference to TESTQUEUE. Configure that destination before deploying the application.

<?xml version="1.0" encoding="UTF-8"?>
<weblogic-ejb-jar xmlns="http://www.bea.com/ns/weblogic/920"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://www.bea.com/ns/weblogic/920 http://www.bea.com/ns/weblogic/920/weblogic-ejb-jar.xsd">
 <weblogic-enterprise-bean>
  <ejb-name>PoolTestBean</ejb-name>
  <stateless-session-descriptor>
   <pool>
    <max-beans-in-free-pool>8</max-beans-in-free-pool>
    <initial-beans-in-free-pool>2</initial-beans-in-free-pool>
   </pool>
  </stateless-session-descriptor>
  
  <resource-description>
   <res-ref-name>jms/QCF</res-ref-name>
   <jndi-name>weblogic.jms.XAConnectionFactory</jndi-name>
  </resource-description>
  <resource-env-description>
   <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
   <jndi-name>TESTQUEUE</jndi-name>
  </resource-env-description>
  <jndi-name>PoolTest</jndi-name>
 </weblogic-enterprise-bean>
</weblogic-ejb-jar>
PoolTest.java

This section defines the "remote" interface for the PoolTest bean. It declares one method, called sendXATransactional.

package weblogic.jms.pool.test;

import java.rmi.*;
import jakarta.ejb.*;
public interface PoolTest extends EJBObject
{
 public String sendXATransactional(String text)
  throws RemoteException;
}
PoolTestHome.java

This section defines the home interface required by this example's EJB2.x remote client view.

package weblogic.jms.pool.test;

import java.rmi.*;
import jakarta.ejb.*;

public interface PoolTestHome
 extends EJBHome
{
 PoolTest create()
  throws CreateException, RemoteException;
}
PoolTestBean.java

This section defines the actual EJB code. It sends a message whenever the sendXATransactional method is called.

package weblogic.jms.pool.test;

import java.lang.reflect.*;
import java.rmi.*;
import jakarta.ejb.*;
import jakarta.jms.*;
import javax.naming.*;
import jakarta.transaction.*;

public class PoolTestBean
 implements SessionBean
{ 
 private SessionContext context;
 private QueueConnectionFactory qcf;
 private Queue destination;
 
 public void ejbActivate()
 {
 }
 
 public void ejbRemove()
 {
 }
 
 public void ejbPassivate()
 {
 }
 
 public void setSessionContext(SessionContext ctx)
 {
  context = ctx;
 }

 private void lookupJNDIObjects()
  throws NamingException
 {
  InitialContext ic = new InitialContext();
  try {
   qcf =
    (QueueConnectionFactory)ic.lookup
                    ("java:comp/env/jms/QCF");
   destination =
    (Queue)ic.lookup("java:comp/env/jms/TESTQUEUE");
   } finally {
    ic.close();
   }
 }
 
 public void ejbCreate()
  throws CreateException
 {
  try {
   lookupJNDIObjects();
  } catch (NamingException ne) {
   throw new CreateException(ne.toString());
  }
 }
 
 public String sendXATransactional(String text)
  throws RemoteException
 {
  String id = "Not sent yet";
  try {
   if ((qcf == null) || (destination == null)) {
    lookupJNDIObjects();
   }
   QueueConnection connection = qcf.createQueueConnection();
   try {
    QueueSession session = connection.createQueueSession
                           (false, Session.AUTO_ACKNOWLEDGE);
    TextMessage message = session.createTextMessage
                           (text);
    QueueSender sender = session.createSender(destination);
    sender.send(message);
    id = message.getJMSMessageID();
   } finally {
    connection.close();
   }
  } catch (Exception e) {
   // Invalidate the JNDI objects if there is a failure.
   // A later invocation will look them up again.
   // Recovery behavior depends on the JMS provider.
   qcf = null;
   destination = null;
   throw new RemoteException("Failure in EJB: " + e);
  }
  return id;
 }
}

Sending a JMS Message in a Jakarta EE Container

After you declare the JMS connection factory and destination resources, you can use them to send and receive JMS messages inside an EJB or servlet. The following sections provide examples of how to send a message:

Using comp/env

The code in Example 3-3 sends a message if you map to the java:comp/env JNDI tree:

Example 3-3 Sending a Message Using comp/env

QueueConnectionFactory qcf;
Queue destQueue;
InitialContext ic = new InitialContext();
try {
 qcf = (QueueConnectionFactory)ic.lookup("java:comp/env/jms/QCF");
 destQueue = (Queue)ic.lookup("java:comp/env/jms/TESTQUEUE");
} finally {
 ic.close();
}
QueueConnection connection = qcf.createQueueConnection();
try {
 QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
 QueueSender sender = session.createSender(destQueue);
 TextMessage msg = session.createTextMessage("This is a test");
 sender.send(msg);
} finally {
 connection.close();
}

This is standard code that complies with the Jakarta EE specification and should run on any EJB or servlet product that properly supports Jakarta EE , the difference is that it runs more efficiently on WebLogic Server, because under the covers various objects are pooled, as described in Pooled JMS Connection Objects.

Note that this code example uses a try...finally block to guarantee that the close() method on the JMS Connection object is executed even if one of the statements inside the block throws an exception. If no connection pooling were being done, then this block would be necessary in order to ensure that the connection is closed, and to prevent server resources from being wasted. But because WebLogic Server pools some of the objects that are created by this code example , it is even more important that close() be called; otherwise, the EJB or servlet container will not know when to return the object to the pool.

Also, none of the transactional XA extensions to the JMS API are used in this code example . Instead, the container uses them internally if the JMS code is used inside a transaction context. But whether or not XA is used internally, the user-written code is the same, and does not use any JMS XA classes. This is what is specified by Jakarta EE. Writing EJB code in this way enables you to run EJBs in an environment where transactions are present or in a non-transactional environment, just by changing the deployment descriptors.

Note:

When an EJB uses a wrapped JMS connection factory obtained through a resource reference in java:comp/env, application code must not use the jakarta.jms XA interfaces. The container manages transaction integration.

Dependency Injection

The code in Example 3-4 sends a message if you have used dependency injection to a variable.

MyStateless.java

package test;

public interface MyStateless {
    void completeWorkOrder();
}

Example 3-4 Sending a Message using Dependency Injection

package test;
// Example injected annotation.
import jakarta.annotation.Resource;
import jakarta.ejb.*;
import jakarta.jms.*;

@Stateless(mappedName="StatelessBean")
public class MyStatelessBean implements MyStateless {
  @Resource(mappedName="myDestJNDIName")
  private Destination dest;
 
 @Resource(mappedName="weblogic.jms.XAConnectionFactory")
  private ConnectionFactory connectionFactory;
 
  public void completeWorkOrder() {
    Connection con =  null;
    Session session = null;
    MessageProducer sender = null;
    try {
      System.out.println("completeWorkOrder called!");
      con = connectionFactory.createConnection();
      session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
      sender = session.createProducer(null);
      Message message = session.createTextMessage("work order complete!");
      sender.send(dest, message);
    } catch(Exception e) {
      throw new EJBException("Exception sending message:   " + e, e);
    } finally {
      try {
        if (con != null) con.close();
      } catch(Exception e) {
        e.printStackTrace();
      }
    }
  }
}

EJB 3.0 Wrapper Without Injection

Example 3-5 demonstrates EJB 3.0 annotations for an MDB that references resources that are not injected. The references are resolved at runtime when the MDB is invoked instead of when the MDB instances are instantiated.

Example 3-5 Non injected MDB Example

 
package test;
 
import jakarta.annotation.Resources;
import jakarta.annotation.Resource;
import javax.naming.*;
import jakarta.ejb.*;
import jakarta.jms.*;
 
import jakarta.ejb.ActivationConfigProperty;
 
@MessageDriven(
     name = "MyMDB",
     mappedName = "JNDINameOfMDBSourceDest",
     activationConfig = {
     // JMS interface type of the MDB destination:
     // jakarta.jms.Topic or jakarta.jms.Queue
     @ActivationConfigProperty(
          propertyName = "destinationType",
          propertyValue = "jakarta.jms.Queue"),
     // optionally specify a connection factory
     // there's no need to specify a connection factory if the source
     // destination is a WebLogic JMS destination
     @ActivationConfigProperty(
          propertyName = "connectionFactoryJndiName",
          propertyValue = "JNDINameOfMDBSourceCF"),
    })
 
// resources that are not injected
 
@Resources ({
  @Resource(name="targetCFRef", 
            mappedName="TargetCFJNDIName",
            type=jakarta.jms.ConnectionFactory.class), 
 
  @Resource(name="targetDestRef",
            mappedName="TargetDestJNDIName",
            type=jakarta.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(TransactionAttributeType.REQUIRED)
  public void onMessage(Message message) {
 
    Connection jmsConnection = null;
 
    try {
      System.out.println("My MDB got message: " + message);
 
      if (targetCF == null) 
        targetCF = (jakarta.jms.ConnectionFactory)mdctx.lookup("targetCFRef");
 
      if (targetDest == null)
        targetDest = (jakarta.jms.Destination)mdctx.lookup("targetDestRef");
 
      jmsConnection = targetCF.createConnection();
      Session s = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer mp = s.createProducer(null);
 
      if (message.getJMSReplyTo() != null) 
        mp.send(message.getJMSReplyTo(), s.createTextMessage("My Reply"));
      else
        mp.send(targetDest, message);
      
    } catch (JMSException e) {
      throw new EJBException(e); 
 
    } finally {
 
      // Return JMS resources to the resource reference pool for later re-use.
      // Closing a connection automatically also closes its sessions, etc.
 
      try { if (jmsConnection != null) jmsConnection.close(); }
      catch (JMSException ignored) {};
    }
  }
}