Programming WebLogic JMS
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Usability features that are generally hidden behind the J2EE standard have been enhanced to make it easier to access EJB and servlet containers with WebLogic JMS or third-party JMS providers. In fact, implementing this "JMS wrapper" support, as described in this section, is the best practice method of sending a WebLogic JMS message from inside an EJB or servlet.
Simplified Access to Remote or Foreign JMS Providers briefly describes the Administration Console support for foreign JMS providers, as documented in "Accessing Foreign JMS Providers" in the Administration Console Online Help. This feature makes it possible to easily map foreign JMS providers — including remote instances of WebLogic Server in another cluster or domain — so that they appear in the local JNDI tree as a local JMS object.
WebLogic Server uses JMS wrappers that make it easier to use WebLogic JMS inside a J2EE component, such as an EJB or a servlet, while also providing a number of enhanced usability and performance features:
What's Happening Under the JMS Wrapper Covers describes how WebLogic Server implements these features behind the scenes.
You enable these enhanced J2EE features by declaring a JMS connection factory as a resource-ref
in the EJB or servlet deployment descriptors, as described in Declaring a Wrapped JMS Connection Factory. For example, when a connection factory is declared as a resource-ref
, a JMS application can look it up from JNDI using the java:comp/env/
subtree that is created for each EJB or servlet. It is important to note that the features listed above are only enabled when using a JMS resource inside the deployment descriptors. The EJB and servlet programmers still have direct access to the JMS provider by performing a direct JNDI lookup of the connection factory or destination.
For more information about packaging EJBs, see Implementing Enterprise JavaBeans in Programming WebLogic Enterprise JavaBeans. For more information about programming servlets, see "Programming Tasks" in Programming WebLogic HTTP Servlets.
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>javax.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 will 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.
In addition to this element, there must be a matching resource-description
element in the weblogic-ejb-jar.xml
(for EJBs) or weblogic.xml
(for servlets) file that tells the J2EE container which JMS connection factory to put in that location. Here is an example:
<resource-description>
<res-ref-name>jms/QCF</res-ref-name>
<jndi-name>weblogic.jms.ConnectionFactory</jndi-name>
</resource-description>
The connection factory specified here must already exist in the global JNDI tree, as described in "Connection Factory Tasks" in the Administration Console Online Help. (This example uses one of the default JMS connection factories that is automatically created when the built-in WebLogic JMS server is used). To use another WebLogic JMS connection factory from the same cluster, simply include that connection factory's JNDI name inside the jndi-name
element. To use a connection factory from another vendor, or from another WebLogic Server cluster, create a Foreign JMS Server, as described in "Accessing Foreign JMS Providers" in the Administration Console Online Help.
If the JNDI name specified in the resource-description
element is incorrect, then the application is still deployed. However, you will receive an error when you try to use the connection factory.
You can also bind a JMS queue or topic destination into the java:comp/env/jms
JNDI tree by declaring it as a resource-env-ref
element in the ejb-jar.xml
or web.xml
deployment descriptors. 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 resource-env-ref
description, without having to recompile the source code.
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>javax.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 which allows you to easily modify referenced destinations just by changing the corresponding resource-env-ref
deployment descriptors.
<resource-env-description>
<res-env-ref-name>jms/TESTQUEUE</res-env-ref-name>
<jndi-name>jmstest.destinations.TESTQUEUE</jndi-name>
</resource-env-description>
The queue or topic destination specified here must already exist in the global JNDI tree, as described in "JMS Queue and Topic Destination Tasks" in the Administration Console Online Help. Again, if the destination does not exist, the application is deployed, but an exception is thrown when you try to use the destination.
After you declare the JMS connection factory and destination resources in the deployment descriptors so that they are mapped to the java:comp/env
JNDI tree, you can use them to send and/or receive JMS messages inside an EJB or a servlet.
For example, the following code fragment sends a message:
InitialContext ic = new InitialContext();
QueueConnectionFactory qcf =
(QueueConnectionFactory)ic.lookup("java:comp/env/jms/QCF");
Queue destQueue =
(Queue)ic.lookup("java:comp/env/jms/TESTQUEUE");
ic.close();
QueueConnection connection = qcf.createQueueConnection();
try {
QueueSession session = connection.createQueueSession(0, false);
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 J2EE specification and should run on any EJB or servlet product that properly supports J2EE — 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 fragment 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 since WebLogic Server pools some of the objects that are created by this code fragment, 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 fragment. Instead, the container uses them internally if the JMS code is used inside a transaction context. But whether 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 J2EE. 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.
Caution: When using a wrapped JMS connection factory, which is obtained by using the resource-ref
feature and looked up by using the java:comp/env/jms
JNDI tree context, then the EJB must not use the transactional XA interfaces.
This section explains 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 J2EE 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 J2EE behavior, as described in the following sections.
This feature works for either WebLogic JMS implementations or for third-party JMS providers that support two-phase commit transactions (XA protocol). If a wrapped JMS connection sends or receives a message inside a transaction context, the JMS session being used to send or receive the message is automatically enlisted in the transaction through the XA capabilities of the JMS provider. This is the case whether the transaction was started implicitly because the JMS code was invoked inside an EJB with container-managed transactions enabled, or whether the transaction was started manually using the UserTransaction
interface in a servlet or an EJB that supports bean-managed transactions.
However, if an EJB or servlet attempts to send or receive a message inside a transaction context and the JMS provider does not support XA, the send()
or receive()
call throws the following exception:
[J2EE:160055] Unable to use a wrapped JMS session in the transaction because two-phase commit is not available.
Therefore, if you are using a JMS provider that doesn't support XA to send or receive a message inside a transaction, either declare the EJB with a transaction mode of NotSupported
or suspend the transaction using one of the JTA APIs.
To read more information on the configuration attributes for a WebLogic JMS connection factory that supports transactions, see "JMS Connection Factory --> Configuration --> Transactions" in the Administration Console Online Help.
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 via a resource-ref
element in the weblogic-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 J2EE 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 (see Simplified Access to Remote or Foreign JMS Providers). Otherwise, WebLogic Server connects to the provider with no user name or password specified. In this mode, it is an error to pass a user name and password to the createConnection()
method of the JMS 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()
method of the JMS connection factory, or use the version of createConnection()
with no user name or password if none are required.
The JMS wrapper classes monitor each connection that is established to the JMS provider. They do this in two ways:
ExceptionListener
object on the connection.Note: In order to create a temporary queue or topic destination to test connections to a WebLogic JMS server, the JMS server must be configured to host temporary destinations. For more information, see "Using Temporary Destinations" in Programming WebLogic JMS.
The J2EE specification states that you should not be allowed to make certain JMS API calls inside a J2EE application. The JMS wrappers enforce these restrictions by throwing the following exceptions when they are violated:
createConnectionConsumer()
, createDurableConnectionConsumer()
, setClientID()
, setExceptionListener()
, and stop()
should not be called.getMessageListener()
and setMessageListener()
should not be called.QueueReceiver
or TopicSubscriber
object), the methods getMessageListener()
and setMessageListener()
should not be called.Furthermore, the createSession()
method, and the associated createQueueSession()
and createTopicSession()
methods, are handled differently. The createSession()
method takes two parameters: an "acknowledgement" mode and a "transacted" flag. When used inside an EJB, these two parameters are ignored. If a transaction is present, then the JMS session is enlisted in the transaction as described in Automatically Enlisting Transactions; otherwise, it is not. By default, the acknowledgement mode is set to "auto acknowledge". This behavior is expected by the J2EE specification.
Note: This may make it more difficult to receive messages from inside an EJB, but the recommended way to receive messages from inside an EJB is to use a MDB, as described in "Designing and Developing Message-Driven Beans" in Programming WebLogic Enterprise JavaBeans.
Inside a servlet, however, the parameters to createQueueSession()
and createTopicSession()
are handled normally, and users can make use of all the various message acknowledgement modes.
The JMS wrappers pool various session objects in order to make code like the example provided in Sending a JMS Message In a J2EE 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 Connection Factory.
You monitor pooled JMS sessions on the "JMS Pooled Connections" tab, which is accessed through the Server --> Monitoring --> JMS node on the Administration Console, as described in "Server --> Monitoring --> JMS" in the Administration Console Online Help.
The automatic pooling of connections and other objects by the JMS wrappers means that it is efficient to write code as shown in Sending a JMS Message In a J2EE 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.
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 once 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 may 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. Since stateless session beans are pooled, this method is also very efficient (and is perfectly consistent with the J2EE 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 J2EE specification.)
However, if these objects are cached inside the ejbCreate()
or init()
method, then the EJB or servlet must have some way to recreate them if there has been a failure. This is necessary because some JMS providers, like WebLogic JMS, may invalidate a Destination object after a server failure. So, if the EJB runs on Server A, and JMS runs on Server B, then the EJB on Server A will have to perform the JNDI lookup of the objects from Server B again after that server has recovered. The example, PoolTestBean.java includes a sample EJB that performs this caching and re-lookup process correctly.
Once Connection Factory object and/or Destination object pooling has been established, it may be tempting to cache other objects, such as the Connection, Session, and Producer objects, inside the ejbCreate()
method. This will work, but it is not always the most efficient solution. Essentially, by doing this you are removing a Session object from the cache and permanently assigning it to a particular EJB, whereas by using the JMS wrappers as designed, that Session object can be shared by other EJBs and servlets as well. Furthermore, the wrappers attempt to reestablish a JMS connection and create new session objects if there is a communication failure with the JMS provider, but this will not work if you cache the Session object on your own.
When a JMS send()
or receive()
operation is performed inside a transaction, the EJB or servlet automatically enlists the JMS provider in the transaction. A transaction can be started automatically inside an EJB or servlet that has container-managed transactions, or it can be started explicitly using the UserTransaction
interface. In either case, the container automatically enlists the JMS provider. However, if the underlying JMS connection factory used by the EJB or servlet does not support XA, the container throws an exception.
Performing the transaction enlistment has overhead. Furthermore, if an XA connection factory is used, but the send()
or receive()
method is invoked outside a transaction, the container must still create a JTA transaction to wrap the send()
or receive()
method in order to ensure that the operation properly takes place no matter which JMS provider is used. Although this is only a one-phase commit, it can still slow down the server.
Therefore, when writing an EJB or servlet that uses a JMS resource in a non-transactional manner, it is best to use a JMS connection factory that is not configured to support XA. For more information on configuring a WebLogic JMS connection factory, see "Configuring a JMS Connection Factory" in the Administration Console Online Help.
The following files make up a simple stateless EJB session bean that uses the WebLogic JMS wrapper functions to send a transactional message (sendXATransactional
) when an EJB is called. Although this example uses a session bean, the same XML descriptors and bean class (with very few changes) can be used for a message-driven bean.
This section describes the EJB components. For the "JMS wrapper" code snippets 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"?>
<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<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>javax.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>javax.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>
This section declares matching resource-description
queue connection factory and queue destination elements that tell the J2EE container which JMS connection factory and destination to put in that location.
<?xml version="1.0"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC
"-//BEA Systems, Inc.//DTD WebLogic 8.1.0 EJB//EN"
"http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd">
<weblogic-ejb-jar>
<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>
<reference-descriptor>
<resource-description>
<res-ref-name>jms/QCF</res-ref-name>
<jndi-name>weblogic.jms.XAConnectionFactory</jndi-name>
</resource-description>
<resource-env-description>
<res-env-ref-name>jms/TESTQUEUE</res-env-ref-name>
<jndi-name>TESTQUEUE</jndi-name>
</resource-env-description>
</reference-descriptor>
<jndi-name>PoolTest</jndi-name>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>
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 javax.ejb.*;
public interface PoolTest extends EJBObject
{
public String sendXATransactional(String text)
throws RemoteException;
}
This section defines the "home" interface for the PoolTest
bean. It is required by the EJB specification.
package weblogic.jms.pool.test;
import java.rmi.*;
import javax.ejb.*;
public interface PoolTestHome
extends EJBHome
{
PoolTest create()
throws CreateException, RemoteException;
}
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 javax.ejb.*;
import javax.jms.*;
import javax.naming.*;
import javax.transaction.*;
public class PoolTestBean
extends PoolTestBeanBase
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, 0);
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
// this is necessary because the destination object
// may become invalid if the destination server has
// been shut down
qcf = null;
destination = null;
throw new RemoteException("Failure in EJB: " + e);
}
return id;
}
}
Another set of foreign JMS provider features makes it possible to create a "symbolic link" between a JMS connection factory or destination object in an third-party JNDI provider to an object inside the local WebLogic Server. This feature can also be used to reference remote instances of WebLogic Server in another cluster or domain in the local WebLogic JNDI tree.
There are three configuration MBeans for this task:
resource-reference
in an EJB or a servlet, with the "Container" mode of authentication. It creates non-replicated JNDI objects on each WebLogic Server instance to which the parent Foreign JMS Connection Factory MBean is targeted. (To create the JNDI object on every node in a cluster, target the parent MBean to the cluster.)For instructions on configuring these MBeans with the Administration Console, refer to "Accessing Foreign JMS Providers" in the Administration Console Online Help.
Once deployed, these "foreign" JMS MBeans work by creating objects in the local server's JNDI tree, which then perform the lookup of the referenced remote JMS objects whenever the foreign JMS MBeans are looked up. This means that the local server and the remote JNDI directory are never out of sync. However, from a performance perspective, it means that a JNDI lookup of one of these MBeans can potentially be expensive. The sections under Improving Performance Through Pooling describes some ways to improve the performance of these remote lookups.
![]() ![]() |
![]() |
![]() |