The exception listener on JMS connections is never called

Since we were having a few issues with memory leaks while handling JMS connections, we wanted to setup am Exception Listener (using the setExceptionListener method of the connection) to handle a connection failure and reconnect. Especially, if you consume messages asynchronously, this seems like a good way to learn that your connection has failed and reconnect.

Unfortunately, we never were notified although we could clearly see that the connection failed.

The problem is that we were in a JBoss container and as the J2EE specification says:

This method must not be used in a Java EE web or EJB application. Doing so may cause a JMSException to be thrown though this is not guaranteed.

This is actually true not only for setExceptionListener but for all the following methods of the JMS Connection class:

  • createConnectionConsumer
  • createSharedConnectionConsumer
  • createDurableConnectionConsumer
  • createSharedDurableConnectionConsumer
  • setClientID
  • setExceptionListener
  • stop

The first four of them are not allowed because of restrictions on the use of threads in the container. The other ones because they may interfere with the
connection management functions of the container.

In the past this was not always enforced by J2EE container vendors but in order to pass the compatibility test suites, they have started really enforcing this policy. So with many containers, you will get an IllegalStateException or a JMSException when invoking setExceptionListener (which states that you’re not allowed to call it).

JBoss: Get data using JMX

First in order to get data from JBoss using JMX, you need to add jboss-client.jar to your classpath. You can find this JAR file in the client directory of your JBoss installation.

In order to open a connection, you first need to create a URL:

String host = "localhost";
String port = "9010";
String urlString = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";

You can then use this URL to get a connection to the MBean server:

JMXServiceURL serviceURL = new JMXServiceURL(urlString);
JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL, null);
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

With this MBean server connection, you can now for example get the number of MBeans:

int count = connection.getMBeanCount();
System.out.println("MBean count = " + count);

Now to get some more useful information, you’ll need to get an Object Name and can then use it to query some attributes of an MBean e.g.:

ObjectName objectName = new ObjectName("jboss.system:type=ServerInfo");
Integer activeThreadCount = (Integer) connection.getAttribute(objectName, "ActiveThreadCount");
System.out.println("activeThreadCount = " + activeThreadCount);

Similarly, you can also call a function of an MBean:

objectName = new ObjectName("org.hornetq:address=\"jms.queue.DLQ\",module=Core,name=\"jms.queue.DLQ\",type=Queue");
Object obj = connection.invoke(objectName, "listMessages", new Object[] {""}, new String[] { "java.lang.String" });
System.out.println("messages = "+obj.toString());

The parameters to the invoke method of the connection are:

  1. The object name specifying which MBean you want to work with.
  2. The name of the MBean method you want to call.
  3. An array containing the parameters of the method (in our case an empty String).
  4. The signature of the method (in our case, the method takes a String as parameter).

Once you are done, you just need to close the JMX connection:

jmxConnector.close();

List number of errors per class in JBoss log file

If you have a log file with the following format:

date time loglevel [class] ...

e.g.:

2012-05-27 12:38:04,345 INFO [org.jboss.web.tomcat.service.TomcatDeployer] deploy, ctxPath=/hessian, warUrl=.../deploy/hessian.war/

and want to find out how many errors are generated per class, you can use the following:

 # grep " ERROR " jboss.log | awk '{ count[$4]++ } END { for(class in count) print class, count[class] }'
[org.jboss.aop.deployment.AspectDeployer] 1
[org.jboss.deployment.scanner.URLDeploymentScanner] 3
[org.jboss.remoting.transport.Connector] 1
[org.jboss.jdbc.DerbyDatabase] 3

Of course replace jboss.log by the path to the log file you want to analyze. If you do not want to group by the 4th component of the log file, change $4 to something else.

What it does is basically the following:

  • grep ” ERROR ” jboss.log: extract only ERROR lines
  • { count[$4]++ }: for each line in the file, get the 4th component and increase the count for this component. After you’ve gone through the whole file, you end up with an array containing the class as key and the count as value
  • END { for(class in count) print class, count[class] }: when you’ve gathered data from the whole file, just go through the array and print the class and the count

If you want to sort by descending count, add a “sort -n -k2 -r”:

 # grep " ERROR " jboss.log | awk '{ count[$4]++ } END { for(class in count) print class, count[class] }' | sort -n -k2 -r
[org.jboss.jdbc.DerbyDatabase] 3
[org.jboss.deployment.scanner.URLDeploymentScanner] 3
[org.jboss.remoting.transport.Connector] 1
[org.jboss.aop.deployment.AspectDeployer] 1
  • -n is used so that a numerical comparison is performed
  • -k2 sorts using the second component i.e. the count
  • -r sorts in descending order