JMS Archive

Topic Send and Topic Receive demo on JBoss Application Server

1 )Create a topic

Add the following in the \deploy\messaging\destinations-service.xml file

 

    <mbean code="org.jboss.jms.server.destination.TopicService"
      name="jboss.messaging.destination:service=Topic,name=Topic1"
      xmbean-dd="xmdesc/Topic-xmbean.xml">
	<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
	<depends>jboss.messaging:service=PostOffice</depends>
   </mbean>

2) Copy the TopicSend and TopicReceive programs to a folder.

TopicSend

import java.io.*;
import java.util.*;
import javax.transaction.*;
import javax.naming.*;
import javax.jms.*;
import javax.rmi.PortableRemoteObject;

public class TopicSend
{
// Defines the JNDI context factory.
public final static String JNDI_FACTORY=”org.jnp.interfaces.NamingContextFactory”;
// Defines the JMS connection factory.
public final static String JMS_FACTORY=”ConnectionFactory”;
// Defines the topic.
public final static String TOPIC=”/topic/Topic1″;

protected TopicConnectionFactory tconFactory;
protected TopicConnection tcon;
protected TopicSession tsession;
protected TopicPublisher tpublisher;
protected Topic topic;
protected TextMessage msg;

public void init(Context ctx, String topicName)
throws NamingException, JMSException
{
tconFactory = (TopicConnectionFactory)
PortableRemoteObject.narrow(ctx.lookup(JMS_FACTORY),
TopicConnectionFactory.class);
tcon = tconFactory.createTopicConnection();
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic) PortableRemoteObject.narrow(ctx.lookup(topicName), Topic.class);
tpublisher = tsession.createPublisher(topic);
msg = tsession.createTextMessage();
tcon.start();
}

public void send(String message) throws JMSException {
msg.setText(message);
tpublisher.publish(msg);
}

public void close() throws JMSException {
tpublisher.close();
tsession.close();
tcon.close();
}

public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println(“Usage: java TopicSend jboss_url”);
return;
}
InitialContext ic = getInitialContext(args[0]);
TopicSend ts = new TopicSend();
ts.init(ic, TOPIC);
readAndSend(ts);
ts.close();
}

protected static void readAndSend(TopicSend ts)
throws IOException, JMSException
{
BufferedReader msgStream = new BufferedReader (new InputStreamReader(System.in));
String line=null;
do {
System.out.print(“Enter message (\”quit\” to quit): \n”);
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
ts.send(line);
System.out.println(“JMS Message Sent: “+line+”\n”);
}
} while (line != null && ! line.equalsIgnoreCase(“quit”));
}

protected static InitialContext getInitialContext(String url)
throws NamingException
{
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);

return new InitialContext(env);
}

}

TopicReceive

import java.io.*;
import java.util.*;
import javax.transaction.*;
import javax.naming.*;
import javax.jms.*;
import javax.rmi.PortableRemoteObject;

public class TopicReceive implements MessageListener {

// Defines the JNDI context factory.
public final static String JNDI_FACTORY=”org.jnp.interfaces.NamingContextFactory”;
// Defines the JMS connection factory for the topic.
public final static String JMS_FACTORY=”ConnectionFactory”;
// Defines the topic.
public final static String TOPIC=”/topic/Topic1″;

private TopicConnectionFactory tconFactory;
private TopicConnection tcon;
private TopicSession tsession;
private TopicSubscriber tsubscriber;
private Topic topic;
private boolean quit = false;

/**
* Message listener interface.
* @param msg message
*/
public void onMessage(Message msg) {
try {
String msgText;

if (msg instanceof TextMessage) {
msgText = ((TextMessage)msg).getText();
} else {
msgText = msg.toString();
}

System.out.println(“JMS Message Received: “+ msgText );

if (msgText.equalsIgnoreCase(“quit”)) {
synchronized(this) {
quit = true;
this.notifyAll(); // Notify main thread to quit
}
}
} catch (JMSException jmse) {
System.err.println(“An exception occurred: “+jmse.getMessage());
}
}

public void init(Context ctx, String topicName)
throws NamingException, JMSException
{
tconFactory = (TopicConnectionFactory)
PortableRemoteObject.narrow(ctx.lookup(JMS_FACTORY),
TopicConnectionFactory.class);
tcon = tconFactory.createTopicConnection();
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic)
PortableRemoteObject.narrow(ctx.lookup(topicName),
Topic.class);
tsubscriber = tsession.createSubscriber(topic);
tsubscriber.setMessageListener(this);
tcon.start();
}

/**
* Closes JMS objects.
*
* @exception JMSException if JMS fails to close objects due to internal error
*/
public void close() throws JMSException {
tsubscriber.close();
tsession.close();
tcon.close();
}

public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println(“Usage: java TopicReceive jboss_url”);
return;
}
InitialContext ic = getInitialContext(args[0]);
TopicReceive tr = new TopicReceive();
tr.init(ic, TOPIC);

System.out.println(“JMS Ready To Receive Messages (To quit, send a \”quit\” message).”);

// Wait until a “quit” message has been received.
synchronized(tr) {
while (! tr.quit) {
try {
tr.wait();
} catch (InterruptedException ie) {}
}
}
tr.close();
}

private static InitialContext getInitialContext(String url)
throws NamingException
{
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);

return new InitialContext(env);
}

}

 

3) Start your Jboss Server

D:\JBossEnterprisePlatform-5.1.1_New\jboss-eap-5.1\jboss-as\bin>run -c server1 -b 0.0.0.0

4) Open two command prompts, compile and run the two programs as follows.

C:\JBoss\LABS>set classpath=D:\JBossEnterprisePlatform-5.1.1_New\jboss-eap-5.1\jboss-as\client\*;.;
C:\JBoss\LABS>javac TopicReceive.java
C:\JBoss\LABS>java TopicReceive jnp://localhost:1099

C:\JBoss\LABS>set classpath=D:\JBossEnterprisePlatform-5.1.1_New\jboss-eap-5.1\jboss-as\client\*;.;
C:\JBoss\LABS>javac TopicSend.java
C:\JBoss\LABS>java TopicSend jnp://localhost:1099

You can have multiple receivers.

Please let us know if you face any issues.

Cheers!
Wonders Team

Weblogic JMS Test plan using Apache JMETER

With the ever growing business needs, today’s companies need their web applications to be robust and  highly scalable.

Before deploying the JMS resources in production environment, companies do performance testing.

This post provides a brief insight about how we can load test to measure performance of a Weblogic Server JMS resource using JMeter.

You can download the apache JMeter from the below site.

https://jmeter.apache.org/download_jmeter.cgi

Steps:-

1. Create a Weblogic JMS queue.

You can refer the below post for creating a JMS queue in WebLogic server.

http://weblogic-wonders.com/weblogic/2010/11/30/weblogic-jms-feature-using-a-queue

2. Start the Apache JMeter

Execute jmeter.bat from <JMETER_HOME>/bin

3. Define a Thread Group from the JMeter console:

Click on the Test Plan  –> Add  –> Threads (Users) –>  Thread Group

Define the below properties:

Number of threads (Users): Number of users to be defined.

Ramp-Up Period : Leave the default value of 1 second. This property tells JMeter how long to delay between starting each user.

ThreadGroup

Apache JMeter

ThreadGroup-Defination

 

4. Create a sampler for JMS Point-to-Point

Add a sampler for the JMS Point-to-Point to be load tested by right clicking on the created Thread Group –>  Sampler  –>  JMS Point to Point.

Define the below properties:

QueueConnection Factory :  This should be the connection factory created in the Weblogic JMS resources section

JNDI name Request Queue: This should be the JMS Queue created in the Weblogic JMS resources section

Initial Context Factory:  weblogic.jndi.WLInitialContextFactory  for WebLogic Server

Provider URL:  Server URL ex: t3://localhost:8001

Add-JMS-PointToPoint

JMS-Conf

 

5.  Create a JMeter Thread Group  listener:

Right Click on the Thread Group  –>  Add  –> Listener  –> Graph Result.

 

Add-Listener

 

6. Start the load test

Start the load test by click on the Start icon from the JMeter.

7. Test the result.

You can observe the result from the graph output.

Graph-result

 

 

Enable log viewer:   Click on Options tab –> Log viewer

Log-Viewer

 

Further reading:

http://jmeter.apache.org/usermanual/build-jms-point-to-point-test-plan.html

 

Common problems you might run into:

1.

Caused by: java.lang.ClassNotFoundException: weblogic.jndi.WLInitialContextFactory

Caused by: java.lang.ClassNotFoundException: weblogic.security.acl.UserInfo

Remedy:  Make sure you put <WEBLOGIC_HOME>\wlserver_12.1\server\lib\wlclient.jar in the classpath.

Classpath

 

2. If you are using http protocol to connect to weblogic server, you might run into the below exception.

 

ERROR – jmeter.protocol.jms.sampler.JMSSampler: Couldn’t connect to the specified host : Tunneling result unspecified – is the HTTP server at host: ‘127.0.0.1’ and port: ‘8001’ a WebLogic Server? javax.naming.NamingException: Couldn’t connect to the specified host : Tunneling result unspecified – is the HTTP server at host: ‘127.0.0.1’ and port: ‘8001’ a WebLogic Server

Remedy: Enable http tunneling for the WebLogic Server.

http://docs.oracle.com/cd/E17904_01/web.1111/e13701/web_server.htm#CNFGD217

 

Cheers,

Wonders Team.

JMS Based Webservice on Weblogic Server

By default, client applications use HTTP/S as the connection protocol when invoking a WebLogic Web Service. You can, however, configure a WebLogic Web Service so that client applications can also use JMS as the transport when invoking the Web Service.When a WebLogic Web Service is configured to use JMS as the connection transport:The generated WSDL of the Web Service contains two port definitions: one with an HTTP/S binding and one with a JMS binding. When you invoke the Web Service in your client application, you can choose which port, and thus which type of transport, you want to use.Warning:     Non-WebLogic client applications, such as a .NET client, will not be able to invoke the Web Service using the JMS binding.The clientgen Ant task creates a Service implementation that contains two getPortXXX() methods, one for HTTP/S and one for JMS.Note:    You can configure any WebLogic Web Service to include a JMS binding in its WSDL. This feature is independent of JMS-implemented WebLogic Web Services.

Read more

Step1 :-

Create JMS Server and target it to the AdminServer running on http://localhost:7001Create a ConnectionFactory and a Queue targeted to the JMSServer.For Screenshots follow this article.
Step2 :- Create a JWS File

package demo;
import weblogic.jws.*;

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;

@WebService(name=”HelloWorldPortType”,serviceName=”HelloWorldService”,      targetNamespace=”http://www.bea.com”)
@SOAPBinding(style=SOAPBinding.Style.DOCUMENT,use=SOAPBinding.Use.LITERAL,           parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)

@WLJmsTransport(serviceUri=”HelloWorldService”,portName=”HelloWorldServicePort”,                    contextPath=”HelloWorldService”,connectionFactory=”ConnectionFactory-0″,queue=”Queue-0″)

public class HelloWorldImpl {
@WebMethod()
public String sayHello(String s) {
return “Hello ” + s;
}
}

Step 3:- Create a Client

package demo;
import demo.HelloWorldService;
import demo.HelloWorldService_Impl;
import demo.HelloWorldPortType;
import weblogic.wsee.jaxrpc.WLStub;
import java.util.ArrayList;
import java.util.List;
import javax.xml.rpc.Stub;

public class HelloWorldClient
{
public static void main(String[] args) throws Throwable {

String wsdl = args[0];
HelloWorldService service = new HelloWorldService_Impl(wsdl);
HelloWorldPortType port = service.getHelloWorldServicePort();
Stub stub = (Stub)port;
stub._setProperty(WLStub.JMS_TRANSPORT_JNDI_URL, “t3://localhost:7001”);
String response = port.sayHello(“World”);
System.out.println(“response = ” + response);
}
}

Step 4:- Create a build

<project name=”Sample JMS Webservice” default=”all” basedir=”.”>

<property name=”ws.file” value=”HelloWorldImpl” />
<property name=”ear.dir” value=”./ear” />
<property name=”client.dir” value=”./client” />

<path id=”client.class.path”>
<pathelement path=”${client.dir}”/>
<pathelement path=”${java.class.path}”/>
</path>

<taskdef name=”jwsc”
classname=”weblogic.wsee.tools.anttasks.JwscTask” />

<taskdef name=”clientgen”
classname=”weblogic.wsee.tools.anttasks.ClientGenTask” />

<target name=”all” depends=”clean,
server,
client,
deploy” />

<target name=”build” depends=”clean,
server,
client” />

<target name=”clean”>
<delete dir=”${ear.dir}”/>
<delete dir=”${client.dir}”/>
</target>

<target name=”server”>
<mkdir dir=”${ear.dir}”/>
<jwsc
srcdir=”${basedir}”
destdir=”${ear.dir}”
classpath=”${java.class.path}”
fork=”true”
keepGenerated=”true”
deprecation=”${deprecation}”
debug=”${debug}”
verbose=”false”>
<jws file=”${ws.file}.java” explode=”true”/>
</jwsc>
</target>

<target name=”deploy”>
<wldeploy action=”deploy”
source=”${ear.dir}” user=”weblogic”
password=”weblogic” verbose=”true”
failonerror=”${failondeploy}”
adminurl=”t3://localhost:7001″
targets=”AdminServer” />
</target>

<target name=”client”>
<mkdir dir=”${client.dir}”/>
<clientgen
wsdl=”${ear.dir}/${ws.file}/WEB-INF/HelloWorldService.wsdl”
destDir=”${client.dir}”
classpath=”${java.class.path}”
packageName=”demo”/>
<javac
srcdir=”${client.dir}” destdir=”${client.dir}”
classpath=”${java.class.path}”
includes=”demo/**/*.java”/>
<javac
srcdir=”${basedir}” destdir=”${client.dir}”
classpath=”${java.class.path};${client.dir}”
includes=”HelloWorldClient.java”/>
</target>

<target name=”run” >
<java fork=”true”
classname=”demo.HelloWorldClient”
failonerror=”true” >
<classpath refid=”client.class.path”/>
<arg line=”http://localhost:7001/HelloWorldService/HelloWorldService?WSDL” />
</java>
</target>

</project>

Step 5:- Create a folder and keep the source files and the build.xml in that directory. Also copy the setWLSEnv.cmd from WL_HOME\bin to this folder. Create two folders in this folder, ear and client.

Step 6 :- Set the Environment

D:\Replications\jmswebservice>setWLSEnv.cmd

Step 7:- Create the Webservice Ear

D:\Replications\jmswebservice>ant server
Buildfile: build.xml

server:
[jwsc] JWS: processing module /HelloWorldImpl
[jwsc] Parsing source files
[jwsc] Parsing source files
[jwsc] 1 JWS files being processed for module /HelloWorldImpl
[jwsc] JWS: D:\Replications\jmswebservice\HelloWorldImpl.java Validated.
[jwsc] Since compiler setting isn’t classic or modern,ignoring fork setting
.
[jwsc] Compiling 2 source files to C:\Users\khanf\AppData\Local\Temp\_bxr00
h
[jwsc] Since compiler setting isn’t classic or modern,ignoring fork setting
.
[jwsc] Copying 1 file to D:\Replications\jmswebservice\ear\HelloWorldImpl\W
EB-INF
[jwsc] Copying 22 files to D:\Replications\jmswebservice\ear\HelloWorldImpl
\WEB-INF
[jwsc] Copying 2 files to D:\Replications\jmswebservice\ear\HelloWorldImpl\
WEB-INF\classes
[jwsc] Copying 1 file to D:\Replications\jmswebservice\ear\HelloWorldImpl
[jwsc] [EarFile] Application File : D:\Replications\jmswebservice\ear\META-
INF\application.xml
[AntUtil.deleteDir] Deleting directory C:\Users\khanf\AppData\Local\Temp\_bxr00h

BUILD SUCCESSFUL
Total time: 16 seconds

Step 8:- Deploy the Ear

D:\Replications\jmswebservice>ant deploy
Buildfile: build.xml

deploy:
[wldeploy] weblogic.Deployer -verbose -noexit -source D:\Replications\jmswebser
vice\ear -targets AdminServer -adminurl t3://localhost:7001 -user weblogic -pass
word ******** -deploy
[wldeploy] weblogic.Deployer invoked with options: -verbose -noexit -source D:
\Replications\jmswebservice\ear -targets AdminServer -adminurl t3://localhost:70
01 -user weblogic -deploy
[wldeploy] <Apr 30, 2011 2:06:34 PM IST> <Info> <J2EE Deployment SPI> <BEA-2601
21> <Initiating deploy operation for application, ear [archive: D:\Replications\
jmswebservice\ear], to AdminServer .>
[wldeploy] Task 3 initiated: [Deployer:149026]deploy application ear on AdminSe
rver.
[wldeploy] Task 3 completed: [Deployer:149026]deploy application ear on AdminSe
rver.
[wldeploy] Target state: deploy completed on Server AdminServer
[wldeploy]
[wldeploy] Target Assignments:
[wldeploy] + ear AdminServer

BUILD SUCCESSFUL

Step 9 :- Compile the client

D:\Replications\jmswebservice>ant client
Buildfile: build.xml

client:
[clientgen] Ignoring JAX-WS options – building a JAX-RPC client
[clientgen]
[clientgen] *********** jax-rpc clientgen attribute settings ***************
[clientgen]
[clientgen] wsdlURI: file:/D:/Replications/jmswebservice/ear/HelloWorldImpl/WEB-
INF/HelloWorldService.wsdl
[clientgen] serviceName : null
[clientgen] packageName : demo
[clientgen] destDir : D:\Replications\jmswebservice\client
[clientgen] handlerChainFile : null
[clientgen] generatePolicyMethods : false
[clientgen] autoDetectWrapped : true
[clientgen] jaxRPCWrappedArrayStyle : true
[clientgen] generateAsyncMethods : true
[clientgen]
[clientgen] *********** jax-rpc clientgen attribute settings end ***************

[clientgen] Package name is demo
[clientgen] DestDir is D:\Replications\jmswebservice\client
[clientgen] class name is HelloWorldPortType_Stub
[clientgen] service class name is HelloWorldService
[clientgen] Porttype name is HelloWorldPortType
[clientgen] service impl name is HelloWorldService_Impl
[javac] Compiling 4 source files to D:\Replications\jmswebservice\client
[javac] Note: D:\Replications\jmswebservice\client\demo\HelloWorldPortType_S
tub.java uses unchecked or unsafe operations.
[javac] Note: Recompile with -Xlint:unchecked for details.
[javac] Compiling 1 source file to D:\Replications\jmswebservice\client

BUILD SUCCESSFUL

Step 10:- Run the Client

D:\Replications\jmswebservice>ant run
Buildfile: build.xml

run:
[java] response = Hello World

BUILD SUCCESSFUL
Total time: 10 seconds

If you have a look at the WSDL

http://localhost:7001/HelloWorldService/HelloWorldService?WSDL

The service will look like this.

<s0:service name=”HelloWorldService”>
<s0:port binding=”s1:HelloWorldServiceSoapBinding” name=”HelloWorldServicePort”>
<s2:address location=”jms://192.168.3.8:7001/HelloWorldService/HelloWorldService?URI=Queue-0&FACTORY=ConnectionFactory-0″ />
</s0:port>
</s0:service>

Let me know if you face any issues while running this sample.

 

Download the source here : jmswebservice

Unit Of Order with Distributed Destinations

The below post gives a good understanding of need of Unit Of Order with Distributed Destinations and ways to configure the same.

Message Unit-of-Order is a WebLogic Server value-added feature that enables a stand-alone message producer, or a group of producers acting as one, to group messages into a single unit with respect to the processing order (a sub-ordering). This single unit is called a Unit-of-Order (or UOO) and requires that all messages from that unit be processed sequentially in the order they were created.

The message processing of a single message is complete when a message is acknowledged, committed, recovered, or rolled back. Until message processing for a message is complete, the remaining unprocessed messages for that Unit-of-Order are blocked.

This feature works best when a group of messages from a single producer must be processed sequentially.

For example, producer A sends messages X, Y, and Z in a unit-of-order. An MDB picks up message X and processes it. Until the MDB instance commits the transaction, message Y and Z remain on the queue. Only when the MDB commits message X does message Y get processed, and so on.

Prerequisites:

1. A distributed destination pre configured.

You can refer the below link to do so.

http://weblogic-wonders.com/weblogic/2011/02/17/uniform-distributed-destinations-udd-feature-in-weblogic/

 

There are two ways to create a unit-of-order

1. Programmatically.

2. Administratively

Programmatically:  WebLogic supplies a weblogic.jms.extensions.WLMessageProducer class, which implements the javax.jms.MessageProducer interface.

It has two methods:

getUnitOfOrder(): To get the name of the current Unit Of Order.

setUnitOfOrder()To set the name of the Current Unit Of Order.

Usage:


queue = (Queue)(ctx.lookup(destName));
qsender = (WLMessageProducer) qs.createProducer(queue);
qsender.setUnitOfOrder();
uooname = qsender.getUnitOfOrder();
System.out.println("Using UnitOfOrder :" + uooname);

Administrative ways:

Steps to implement Unit Of Order feature with Distributed Destination:

NOTE: You can configure the Unit Of Order name at two levels.

1 . Connection Factory

2. Distributed Destination.

1:  Unit Of Order at Connection Factory level:

There are two types of Unit Of Order naming that is done.

User Defined Unit Of Order:  You can configure the Connection Factories to use User-Defined Unit Of Order name. All producers created from such a connection factory have Unit-of-Order enabled.

a.  Login into the admin console, navigate to the ConnectionFactory  –> Click on the DefaultDelivery subtab –> Select the type of Unit Of Order name that you would like to configure.

b. If you are selecting User Defined Unit Of Order, you need to specify the value for “User-generated Unit-of-Order Name:” attribute.

System Defined: If you want you can configure the system defined Unit Of Order name for the Connection Factory for each sessions.

2. Unit Of Order at the Distributed Destination level:

a. Navigate to the Distributed Destination –> General –> Advanced properties.

Enable the “Auto-generate Unit Of Order Name ” attribute.

Specify the routing algorithm as well. By default it would be Hash.


Hash, indicates that a message producer computes the member destination from the hashCode of the unit-of-order.

PathService indicates that the configured Path Service determines the member destination.

This completes the JMS Unit Of Order configuration.

This typical Unit Of Order configuration would look like below in the  config/jms/<systemModule>-jms.xml file.

 

<uniform-distributed-queue name="DistributedQueue-1">

<sub-deployment-name>subManaged</sub-deployment-name>

<default-targeting-enabled>false</default-targeting-enabled>

<attach-sender>supports</attach-sender>

<production-paused-at-startup>false</production-paused-at-startup>

<insertion-paused-at-startup>false</insertion-paused-at-startup>

<consumption-paused-at-startup>false</consumption-paused-at-startup>

<jndi-name>DistQueue</jndi-name>

<default-unit-of-order>true</default-unit-of-order>

<saf-export-policy>All</saf-export-policy>

<messaging-performance-preference>25</messaging-performance-preference>

<unit-of-work-handling-policy>PassThrough</unit-of-work-handling-policy>

<incomplete-work-expiration-time>-1</incomplete-work-expiration-time>

<load-balancing-policy>Round-Robin</load-balancing-policy>

<unit-of-order-routing>Hash</unit-of-order-routing>

<forward-delay>-1</forward-delay>

<reset-delivery-count-on-forward>true</reset-delivery-count-on-forward>

</uniform-distributed-queue>

 

Further reading:

http://download.oracle.com/docs/cd/E12840_01/wls/docs103/jms/uoo.html#wp1040259

Cheers,

Wonders Team. 🙂

 

 

Uniform Distributed Destinations (UDD) feature in WebLogic

The below post depicts a sample configuration of a Uniform Distributed Destination (Queue/ Topic) for the WebLogic JMS feature.

A Distributed Destination represents a group of physical queues, or topics, whose members are hosted by JMS servers in a WebLogic cluster. The distributed destination is bound to a logical JNDI name in the cluster-wide JNDI tree. Applications that use a distributed destination are more highly available than applications that use standalone destinations because it provides.

1.Load balancing

2.Failover for the members of a distributed destination in a cluster.

Pre-requisites:

  1. A cluster configured with at least 2 managed servers.
  2. A persistent store configured for each of the managed server in the cluster.

Steps:

1. Create two JMS Servers and target each one to the migratable servers in the cluster.

a. Login into the Admin Console  –> Navigate to Messaging  –> JMS Servers in the left panel, click new.

b. Create the JMS Server and associate it with the Migratable managed servers of the cluster.

Target each JMS Server to an individual Migratable Managed server.

Note: You need to specify the Persistent store for the JMS Server if it is targeted to a Migratable Server.

You can use the below WLST script to create a FileStore.

from java.util import *

from javax.management import *

import javax.management.Attribute

print 'starting the script ....'

username = 'weblogic'

password = 'weblogic'

AdminURL= 't3://localhost:8001'

connect(username,password,AdminURL)

edit()

startEdit()

cd('/')

cmo.createFileStore('FileStore-0')

cd('/FileStores/FileStore-0')

cmo.setDirectory('G:\\BEA103\\user_projects\\domains\\Wonders_Domain')

set('Targets',jarray.array([ObjectName('com.bea:Name=MS1 (migratable),Type=MigratableTarget')], ObjectName))

activate()

 

Alternatively you can configure the FileStores from the Admin Console as well.

Refer the below link for further info.

http://download.oracle.com/docs/cd/E12840_01/wls/docs103/config_wls/store.html#wp1142183

c. Similarly create another JMS  Server and target it to the other managed server in the cluster.

2. Create a JMS Module.

a. Create a JMS Module and target it to the cluster.

3. Create a Sub Deployment.

a. Click on the newly created JMS Module and you will see the ‘SubDeployment’ tab.

b. Click on ‘new’ to create a new Sub Deployment.

c. Target the sub deployment to the two  JMS Servers.

4. Create JMS Resources. (Connection Factory and JMS Destinations).

a. Create a JMS Connection Factory.

b.  Target the Connection Factory to set of JMS Servers created.

5. Create the Distributed Destination (Either Queue or Topic).

a. From the JMS Module page, create a Distributed Queue.

NOTE:  Make sure you enable the “Allocate Members Uniformly” tag to setup Uniform Distributed Queue.

For more details refer the below link.

http://download.oracle.com/docs/cd/E11035_01/wls100/jms_admin/advance_config.html#wp1067272

b. Specify the Sub Deployment and target it to the set of JMS Servers created.

This completes the Uniform Distributed Destination (UDD) configuration.

6. Test the setup.

a.  Compile and execute the below QueueSend.java program.

*******************************************************

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class QueueSend
{
public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
public final static String JMS_FACTORY="CF1";
public final static String QUEUE="DistributedQueue";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueSender qsender;
private Queue queue;
private TextMessage msg;
public void init(Context ctx, String queueName)
throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qsender = qsession.createSender(queue);
msg = qsession.createTextMessage();
qcon.start();
}
public void send(String message) throws JMSException {
msg.setText(message);
qsender.send(msg);
}
public void close() throws JMSException {
qsender.close();
qsession.close();
qcon.close();
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("Usage: java examples.jms.queue.QueueSend WebLogicURL");
return;
}
InitialContext ic = getInitialContext(args[0]);
QueueSend qs = new QueueSend();
qs.init(ic, QUEUE);
readAndSend(qs);
qs.close();
}
private static void readAndSend(QueueSend qs)
throws IOException, JMSException
{
BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
String line=null;
boolean quitNow = false;
do {
System.out.print("Enter message (\"quit\" to quit): \n");
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
qs.send(line);
System.out.println("JMS Message Sent: "+line+"\n");
quitNow = line.equalsIgnoreCase("quit");
}
} while (! quitNow);
}
private static InitialContext getInitialContext(String url)
throws NamingException
{
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
}

*******************************************************

NOTE: We are sending messages to the url of first managed server in the cluster

b. Compile and execute the below QueueReceive.java program.

*******************************************************

import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class QueueReceive implements MessageListener
{
public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
public final static String JMS_FACTORY="CF1";
public final static String QUEUE="DistributedQueue";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueReceiver qreceiver;
private Queue queue;
private boolean quit = false;

public void onMessage(Message msg)
{
try {
String msgText;
if (msg instanceof TextMessage) {
msgText = ((TextMessage)msg).getText();
} else {
msgText = msg.toString();
}
System.out.println("Message Received: "+ msgText );

if (msgText.equalsIgnoreCase("quit")) {
synchronized(this) {
quit = true;
this.notifyAll(); // Notify main thread to quit
}
}
}
catch (JMSException jmse) {
System.err.println("An exception occurred: "+jmse.getMessage());
}
}

public void init(Context ctx, String queueName) throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qreceiver = qsession.createReceiver(queue);
qreceiver.setMessageListener(this);
qcon.start();
}

public void close()throws JMSException
{
qreceiver.close();
qsession.close();
qcon.close();
}

public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("Usage: java examples.jms.queue.QueueReceive WebLogicURL");
return;
}
InitialContext ic = getInitialContext(args[0]);
QueueReceive qr = new QueueReceive();
qr.init(ic, QUEUE);
System.out.println("JMS Ready To Receive Messages (To quit, send a \"quit\" message).");
synchronized(qr) {
while (! qr.quit) {
try {
qr.wait();
}
catch (InterruptedException ie) {}
}
}
qr.close();
}

private static InitialContext getInitialContext(String url) throws NamingException
{
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
}

 

*******************************************************

NOTE: We are listening to the JMS messages from the URL of the second managed server in the cluster.

We  are able to read the JMS message successfully on  a different server in the cluster. 🙂

Reference:

http://download.oracle.com/docs/cd/E11035_01/wls100/jms_admin/advance_config.html#wp1079177

Regards,

Wonders Team. 🙂

Working with JMS and the Standard Issues in JMS

Java Messaging Service acronymised as JMS is popular in both development and administration world. Different vendors have got different implementations for this JMS API. Oracle Weblogic Server implements this JMS for communication between two different weblogic server domains. This implementation although looks simple but its usage is very high (especially if you are working for a telecom domain).

In the process of implementation of this we get many issues. This article mainly targets to the standard issues of JMS for which, most of us take the assistance from Oracle BEA customer support by raising tickets with them as Sev2 or Sev1.

 

Standard Issue -1: JMS Messages Redelivery

Why does a JMS message get redelivered?

The JMS Server could redeliver messages because of any of the following reasons:

  • A java.lang.Error or java.lang.RuntimeException has been thrown from the Receiver/MDB’s onMessage method
  • User has made a call to ejbcontext.setRollbackOnly() in his MDB’s onMessage method (this applies to Container Managed Transaction only)
  • MDB participating in a Transaction failed for some reason. For example, if the MDB calls another EJB, which makes a JDBC call and the database operation fails. In this case the transaction in which MDB is participated will be rolled back and the message will be redelivered.
  • MDB is transactional but takes a long time to process, the transaction monitor will timeout the transaction and rolls it back – consequently, JMS will redeliver the message.
  • Slow down in the server process. This could happen because of a poison message.
  • User has made a call to Session.recover() from a standalone receiver
  • Session.acknowledgement() is not called from a standalone receiver with client acknowledgement

Application Coding Diagnostics

Add the following code snippet to the receiver’s onMessage method to find out if the message is being redelivered.

Public void onMessage (javax.jms.Messsage msg)

{

try{

System.out.println (“Mesg ID:”+msg.getJMSMessageID ());

System.out.println (“Is Message redelivered:”+msg.getJMSRedelivered ());

}

}

If the same message ID is getting printed multiple times and the JMSRedelivered() flag is returning true, then it confirms that the messages are getting redelivered.

Checklist for Troubleshooting JMS Redelivery Problems

  • Check the receiver type – is it an MDB, Standalone Synchronous, Asynchronous Receiver?
  • Check the acknowledgement options of the receiver:
    • Are Acknowledgement-modes, Transacted Sessions, orBean Managed being used?
    • Transactions or container managed transactions?
  • In case of MDBs with CMT, BMT or standalone receivers, verify whether the onMethod () is returned successfully.
  • If there’s a java.lang.Error or java.lang.RuntimeException thrown from the standalone receiver’s/MDB’s onMessage() method then the JMS server did not receive the acknowledgement and the message will be redelivered by the JMS Server.
  • If the MDB (CMT only) calls ejbcontext.setRollbackOnly() this will force the JMS Server to redeliver the message, as the transaction has been marked for rollback.
  • In the case of an MDB invoking an EJB (Session or Entity) that involves a JDBC call and if the DB operation fails for some unknown reasons the transaction in which MDB is enlisted will be rolled back and message gets redelivered. To avoid this problem, investigate the problems on the EJB side.
  • In the case of long running processes in the MDB’s onMessage() method (CMT only) the transaction will be timed out and it will be rolled back. Consequently the JMS Server will redeliver the message.
  • JMS message redelivery mostly happens due to the application coding errors. By enabling the following debug flags, the root cause of message redelivery can be found.
    • DebugJMSMessagePath=”true”  DebugJMSXA=”true”
    • In the config.xml, under the ServerDebug tag of the each Server tag, set the following debug flags:
      • <ServerDebug DebugJMSXA=”true” DebugJMSMessagePath=”true” />
      • Make sure the stdOutSeverity level of the server is INFO and StdoutDebugEnabled is set to “true”.
  • If the session.recover() is invoked with CLIENT_ACKNOWLEDGE Mode (standalone receivers only), the JMS Server stops message delivery in this session, and restarts message delivery with the oldest unacknowledged message.
  • The following parameters may be configured for a JMS destination to avoid poison messages.
    • Redelivery Delay Time
      When a message is rolled back or recovered, the redelivery delay is the amount of time a message is put aside before an attempt is made to redeliver the message.
    • Redelivery Limit
      Redelivery limit is the number of times JMS server will try to redeliver the message to the receiver/MDB
    • Error Destination
      If the JMS server tried to redeliver more than “Redelivery Limit”, then the JMS Server will forward these undelivered messages to the ‘Error Destination’.If the error destination is not configured, and a message exceeds its redelivery limit, that  message is simply deleted.
  • Another way of debugging this problem is to print/log the exceptions in onMessage() method. The following code snippet will log an exception stack trace and also re-throws the exception from receiver’s onMessage() method.

public void onMessage(Message msg) {
try {
TextMessage tm = (TextMessage) msg;
String text = tm.getText();
System.out.println(“Msg: ” + text+” Mesg ID:”+msg.getJMSMessageID());
}
catch(JMSException ex) {
ex.printStackTrace();
}
catch(java.lang.RuntimeException ey) {
ey.printStackTrace();
throw ey;
}
catch(java.lang.Error ez) {
ez.printStackTrace();
throw ez;
}
}

Understanding JMS Message Redelivery Debug Info

The JMS debug flags DebugMessagePath and DebugJMSXA provide more details about the root cause of the message redelivery problem. DebugMessagePath will help identify if the message is getting redelivered or not.

<Feb 3, 2004 5:01:25 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Assigning to the backend consumer, message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:01:25 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Adding backend session’s unacked message list,
message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:01:25 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Dispatching to the frontend, message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:01:25 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
FRONTEND/FESession (id: <576180353183090550.27>) :
Pushing to the client, message ID:P<802808.1075856485894.0>>
>>Print From the onMessage method: I am the MESSAGE, MsgID: ID:P<802808.1075856485894.0>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<Feb 3, 2004 5:02:05 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Assigning to the backend consumer, message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:02:05 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Adding backend session’s unacked message list,
message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:02:05 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
BACKEND/BEQueue: Dispatching to the frontend, message ID:P<802808.1075856485894.0>>

<Feb 3, 2004 5:02:05 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging MSG_PATH!
FRONTEND/FESession (id: <576180353183090550.34>) :
Pushing to the client, message ID:P<802808.1075856485894.0>>
>>Print From the onMessage method: I am the MESSAGE, MsgID: ID:P<802808.1075856485894.0>From the above debug output, it can be seen that the same message is pushed multiple times to the receiver.

NOTE: This debug flag works in WLS 8.1 and later releases.

DebugJMSXA will help identify if the message is redelivered due to transaction related problems.

<Feb 3, 2004 5:16:10 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging XA ! XA(25421973,1007511,0000013BC2697F28EDB9) >RM-rollback() >

<Feb 3, 2004 5:16:10 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging XA ! XA(25421973,1007511,0000013BC2697F28EDB9) >TE-recv-startRollback() (TE-recv hash=31838215 xid=0000013BC2697F28EDB9 mId=<712671.1075857330638.0> queue=TestDest)>

<Feb 3, 2004 5:16:10 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging XA ! XA(25421973,1007511,0000013BC2697F28EDB9) <TE-recv-startRollback() (TE-recv hash=31838215 xid=0000013BC2697F28EDB9 mId=<712671.1075857330638.0> queue=TestDest)OK>

<Feb 3, 2004 5:16:10 PM PST> <Debug> <JMS> <BEA-040002> <JMS Debugging XA ! XA(25421973,27221567,0000013BC2697F28EDB9) >RM-rollback() >

Background

Once a JMS message is delivered to the consumer(s), JMS Server waits for acknowledgement from the consumer(s). There are four ways in which a consumer can send acknowledgement to the JMS Server:

  1. Transacted Sessions
  2. Container Managed Transactions
  3. Bean Managed Transactions (or) User Transaction
  4. Acknowledgement modes

 

Transacted Sessions

When JMS Transacted sessions are used for sending/receiving JMS messages, internally a local transaction is started. The scope of this transaction is limited to send/receive operations within this session. When the receiver/consumer uses Transacted session, message redelivery depends on commit/rollback call on the session. The receiver’s rollback call on the session will make the messages redelivered by the JMS server. The JMS Transacted Session’s transaction will not have any effect outside the scope of this session. Therefore, it cannot participate in the external transaction (BMT or CMT) (as far as it is not XA aware).

Container Managed Transaction (CMT)

Container Managed Transactions apply for Message Driven Beans. In order to run a MDB in container managed transaction, <transaction-type> tag in ejb-jar.xml and <transaction attribute> in weblogic-ejb-jar.xml should be set to “Container” and “Required” respectively. For example:

Before pulling the message from the destination, EJB container starts a transaction and delivers it to the MDB. Once the onMessage() method returns successfully without any exception, the transaction is committed by the container. EJB container is responsible for begin, commit and rollback for this transaction. If the TX gets rolled back for any reason, JMS server is required to immediately redeliver the message to the next available consumer.

Bean Managed Transaction (BMT) or User Transaction

Bean Managed Transactions apply for both MDBs and standalone consumers. For bean managed or User Transaction, the application code should take the responsibility for begin, commit, or rollback of the transaction. The following sample code snippet creates a User Transaction.

UserTransaction tx1 =(UserTransaction)context.lookup(“javax.transaction.UserTransaction”)
public void onMessage(Message msg)  {
try {
tx1.begin();
String msgText = ((TextMessage)msg).getText();
System.out.println(“Mesg ID:”+msg.getJMSMessageID()+”msg:”+msgText);
tx1.commit();
} catch (Exception jmse) {
jmse.printStackTrace();
}
}

In order to configure a MDB to run in Bean-Managed Transaction, the <transaction-type> tag value in ejb-jar.xml should be set to “Bean”.

In BMT, since the transaction started within MDB, pulling the message from the destination is not part of the transaction. Therefore, the outcome of the transaction, whether commit or rollback, has no impact on message redelivery. Even if the transaction is rolled back, the message is not redelivered.

Acknowledgement Modes

Acknowledgement Mode may be specified while creating a JMS session. Acknowledgement Mode may be set to any of the following values:

  • AUTO_ACKNOWLEDGE
  • DUPS_OK_ACKNOWLEDGE
  • CLIENT_ACKNOWLEDGE

The following sample code snippet sets the Acknowledgement Mode to AUTO_ACKNOWLEDGE.

QueueSession qs = qconn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

For AUTO_ACKNOWLEDGE and DUPS_OK_ACKNOWLEDGE, the message will be automatically acknowledged if it is successfully returned from the onMessage() method.

For CLIENT_ACKNOWLEDGE mode, call message.acknowledge() explicitly for the message to be acknowledged.

If MDB is not running in a Tx, then it may run with one of the following modes:

  • TO_ACKNOWLEDGE
  • DUPS_OK_ACKNOWLEDGE

CLIENT_ACKNOWLEDGE mode is not supported for MDBs

For standalone asynchronous listener, if the message is not acknowledged for any reason, then the message is redelivered only once. If the redelivery fails, then the message is implicitly deleted from the JMS server. As a result, the redelivery count does not get updated, and the message does not get redirected to the error destination (even if one is set for this destination).

Standard Issue -2: JMS Pending Message

What is the meaning of pending messages?

A message present on a JMS destination can be either current or pending. A current message is available for immediate consumption by consumers, whereas a pending message is unavailable. The current message can be referred to as an available message.

The JMS message lifecycle can be summarized as below with respect to the following two states:

  1. A message sent by a JMS producer
    • Without any associated transaction:
      It is immediately current.
    • Within a JTA transaction or transacted JMS session:
      It remains pending until the transaction is committed or rolled back. If the transaction is committed, the message becomes available, and if the transaction is rolled back, the message is removed from the destination.
    • With a specified TimeToDeliver property:
      It remains pending until the TimeToDeliver period expires. This is expected because the purpose of the TimeToDeliver property is to keep the message unavailable to consumers until the specified time period.
  1. A message received by a JMS consumer
    • Without any associated JTA transaction and in a non-transacted JMS session with an acknowledgement mode of NO_ACKNOWLEDGE or MULTICAST_NO_ACKNOWLEDGE:
      It is immediately removed from the destination.
    • Within a JTA transaction or transacted JMS session:
      It becomes pending until the transaction or session is committed or rolled back. On commit, the message is removed from the destination, and on rollback, the message becomes available again, unless a Redelivery delay is specified, in which case it continues to remain pending until the Redelivery delay.
    • Within a non-transacted JMS session with the acknowledgement mode of AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE, or CLIENT_ACKNOWLEDGE:
      It becomes pending until the acknowledgement is received.

To summarize, pending JMS messages means the messages that were:

  • Sent in a transaction but not committed
  • Received but not acknowledged
  • Received but not committed
  • Subject to a redelivery delay
  • Subject to a delivery time

 

 

Are pending messages harmful?

Having pending messages in your JMS destinations does not by itself have any adverse effect on your JMS server or destination. However, if you see an unusual increase in the number of pending messages, it does point to something not working correctly in the application or the WebLogic Server, which will be discussed in this pattern.

Monitoring Pending Messages

Pending Messages can be monitored as follows:

  • Using WebLogic Admin Console
    1. Browse to JMS -> Servers -> Monitoring -> Monitor Active JMS Destinations.
    2. “Messages pending” column shows the pending messages for each destination.
    3. “Bytes pending” column shows the pending bytes for each destination. This bytes count includes the size of only the properties and the body of the messages, and not that of the headers.
  • Using JMX
    1. Use JMSDestinationRuntimeMBean.getMessagesPending() to get pending messages.
    2. Use JMSDestinationRuntimeMBean.getBytesPending() to get pending bytes.
  • Set WebLogic Server environment in the command window. For example, you can run the setDomainEnv.cmd/sh script in your domain directory.
  • Run “java weblogic.WLST”.
  • Run the following WLST commands:
  • Connect to your WebLogic Server instance:
  • connect(‘[username]’,'[password]’,'[url]’)
  • Access the server runtime MBean hierarchy:
  • serverRuntime()
  • Change directory to your JMS server instance:
  • cd (‘JMSRuntime/examplesServer.jms/JMSServers/examplesJMSServer’)
  • ‘examplesServer’ is the name of your WebLogic Server instance, and ‘examplesJMSServer’ is the name of the JMS server instance.
  • Get the pending messages count on the JMS server:
  • get(‘MessagesPendingCount’)
  • Note: that “Bytes Current” and “Messages Current” numbers in the JMS -> Servers -> Monitoring -> Monitor Active JMS Destinations section of the Weblogic Server Admin Console do not include the pending bytes and pending messages respectively. In other words, “Messages Pending” and “Bytes Pending” count are over and above the “Messages Current” and “Bytes Current” count respectively.

Why does the problem occur and how do you troubleshoot it?

This section discusses the common causes for the increase in the number of pending messages, and the corresponding debug mechanisms.

  • Hang in asynchronous client
    1. Description
      One of the most common causes of pending messages is that an asynchronous consumer is lagging behind the producer. In other words, there is a hang or slowdown of message processing by asynchronous consumers.

Weblogic JMS pipelines messages that are delivered to asynchronous consumers, otherwise known as message listeners. This action aids performance as messages are aggregated when they are internally pushed from the server to the client.

This message buffer or pipeline can be configured using the MessagesMaximum parameter in the Connection Factory settings the default value of this parameter is 10 per instance of the asynchronous client. In case of a MDB, each instance is a separate client. Hence, if there are 15 instances of a given MDB, there can be 10*15=150 pending messages buffered on the MDB side in the message pipeline.

These buffered messages will not be consumed by the client if the client happens to be hanging for any reason while processing a message. Other messages in the pipeline will wait and contribute to the number of pending messages. Each such hanging thread corresponds to a hanging asynchronous client and contributes to pending messages equal to the MessagesMaximum parameter. For instance, 5 threads hanging in onMessage() method will contribute to 50 pending messages, assuming the MessagesMaximum parameter has the default value of 10.

    1. Debug steps
      the main problem is the hang in the client. The presence of pending messages is just an after effect. To find out if the client is hanging, take thread dumps as described in Generic Server Hang Pattern document. If you see threads hanging in the onMessage() method of the asynchronous consumer, it means that the client is hanging while processing a message in the queue.

You can always reboot the standalone client or redeploy the MDB. All the messages in the pipeline, including the message being currently processed, will again become available in the queue, ready to be processed by the receivers. This may or may not help resolve the hang depending upon the cause of the hang. For instance, if the hang occurs because the onMessage() code is waiting for an external resource that is currently unavailable, the client will continue to hang after the reboot/redeployment, until the resource becomes available.

You can also reduce the value of the MessagesMaximum property to 1 from the default value of 10. This means that there will be no messages in the pipeline. A second message will be delivered to the asynchronous consumer only after the first message processing has been completed. This can have some performance impact.

It is important to note that this MessagesMaximum property defined on the Connection Factory is different from the MessagesMaximum property for a JMS destination. The property for a JMS destination is used to configure the maximum number of messages that can be stored in the destination.

  • Absence of one or more durable subscribers for topics
    1. Description
      By definition, a topic message remains on the topic until it is consumed by all the currently available non-durable subscribers, and all the registered durable subscribers, irrespective of whether they are currently available or not. Once a topic message is consumed by any given subscriber, it becomes pending and remains so, until it has been consumed by all of these subscribers. This is especially important to understand in case of durable subscribers. Messages can stay in a pending state forever, if one or more of the registered durable subscribers never come back up. In contrast, a queue message is consumed by just one consumer. The message transitions to a pending state when it is delivered to the consumer, and is removed from the queue on acknowledgement of the receipt or commit of the concerned transaction.
    1. Debug steps
      Monitor if all the durable subscribers for the topic are “Active”.

      • On the WebLogic Admin Console, browse to:
        JMS -> Servers -> [JMS Server Name] -> Monitoring -> Monitor Active JMS Destinations -> Monitor all Durable Subscribers
      • Check if the “Active” column has a value of “true” for all durable subscribers.

If any of the subscribers are not active, bring them up. For MDB clients acting as durable subscribers, make sure that the MDBs are deployed correctly on the server(s), and for standalone JMS clients, ensure that they are running.

  • Long Redelivery Delay Period and frequent rollbacks on consumer side
    1. Description
      If the Connection Factory and/or the JMS Destination is configured with a long Redelivery Delay period, and messages are rolled back frequently by the consumers, it will lead to a high number of pending messages.
    1. Debug steps
      Investigate the cause of the rollbacks on the consumer side by analyzing the exceptions thrown. You can also reduce the Redelivery Delay period.
  • Infrequent acknowledgement when using CLIENT_ACKNOWLEDGE
    1. Description
      Another possible cause of pending messages is that clients using non-transacted sessions with CLIENT_ACKNOWLEDGE mode do not send acknowledgements frequently. This will cause the number of pending messages to increase as messages are delivered to and consumed by consumers, but there is no acknowledgement sent back to the JMS server.

Debug steps
Check the code which does the client acknowledgement to ensure that the acknowledgement is done frequently.

Additional information on pending messages

  • There is no way to purge current or pending messages in a JMS destination.
  • There is no mechanism to browse pending messages in a destination.
  • The storage mechanism of a message is independent of whether a message is pending or available. In both states, if the message is non-persistent, it stays in memory, whereas a persistent message is persisted to the underlying JMS store.
  • To avoid the server from running out of memory due to pending or current messages, configure ‘Paging’ for the connection factory. This ensures that the message properties and body may be persisted to the paging store depending upon the quotas specified for the destination. The message headers are always present in memory irrespective of paging.
  • If a JMS sender or receiver having an associated JTA transaction does not explicitly commit or rollback the transaction, the messages remain pending, until one of the following conditions takes place:
    1. The transaction times out causing a rollback or
    2. The transaction is explicitly rolled back through the WebLogic Admin Console.

In case of a transacted JMS session, the message will remain pending until the client JVM is alive. In cases, JTA transaction and transacted JMS session, if the client JVM exits without committing or rolling back the transaction, the server receives a PeerGoneException, rolls back the transaction, and the messages are rolled back. In case of a producer the messages are removed from the destination, and in case of a consumer they are moved from the pending to the current state in the destination.

  • Rolling back an associated JTA transaction or transacted JMS session multiple times does not cause double counting of pending messages, nor does an exception that set a JTA transaction as rollbackOnly followed by an actual rollback.
  • JMS filestore does not shrink in size – The size of a JMS filestore increases as messages are sent to the destinations included in its target JMS server. This can be controlled by tuning quotas and flow control to restrict the number of messages sent to the destinations. Unfortunately, the filestore does not shrink in size on message retrieval. The workaround is to delete the filestore when there are no messages on the store. The exact steps to follow for this purpose are:
    1. Check that there are no available or pending messages on the destination.
    1. Untarget the JMS server from the WebLogic Server instance. You can also just stop the WebLogic Server instance.
    2. Delete the filestore (*.dat file) from the filesystem.

Retarget the JMS server to the Weblogic Server instance. If you had stopped the Weblogic Server instance, restart it.

Debug information

The debug parameters that can be specified for diagnosis are: DebugJMSBackEnd, DebugJMSFrontEnd, DebugJMSCommon, DebugJMSDispatcher , and DebugJMSXA.

DebugJMSXA is required only when the pending messages are related to transacted JMS sessions and/or JTA transactions. You can add these parameters:

  • In the config.xml file:
<ServerDebug DebugJMSBackEnd=”true” DebugJMSFrontEnd =”true” DebugJMSCommon=”true” DebugJMSDispatcher=”true” DebugJMSXA=”true”/>
  • Or as startup parameters in your startWebLogic.cmd script:
-Dweblogic.Debug=weblogic.DebugJMSBackEnd,weblogic.DebugJMSFrontEnd,
weblogic.DebugJMSCommon,weblogic.DebugJMSDispatcher,weblogic.DebugJMSXA
  • Note that enabling this debug is recommended for a development type of environment due to the verbosity of the debug messages.

 
java.lang.RuntimeException: java.lang.OutOfMemoryError: Java heap space
at weblogic.messaging.kernel.internal.PersistenceImpl.readMessageBody(PersistenceImpl.java:848)
at weblogic.messaging.kernel.internal.MessageHandle.pin(MessageHandle.java:373)
at weblogic.messaging.kernel.internal.MessageElementImpl.(MessageElementImpl.java:83)

We get this exception on the server on which the queue is available when we try to open a large message from WLS Console.Issue is fixed in 12.2.1 and patches are available in other version. You can check this bug number Bug 19222117.