CTO Speak

Infrequent - but hopefully useful and/or interesting - musings on a range of technical issues.

Wednesday, 27 January 2010

Debugging in SwiftMQ

Debugging your first Swiftlet or JMS application within the SwiftMQ environment can be quite a challenge.

For that reason the company behind SwiftMQ, IIT Software, developed the Trace Swiftlet for tracing and providing "detailed information about internal processing of Swiftlets".

The idea is to sprinkle your code liberally with tracing code such as:

if (ctx.traceSpace.enabled) {
     ctx.traceSpace.trace(PREDICATE, debuginfo);

and observe the trace output to track processing and variable values.

The reason debugging from within your favourite IDE is "impossible" is that SwiftMQ has a built-in hot deployment feature that prevents you connecting via a remote debugger.

So for months that's exactly how I'd debug SwiftMQ applications: make some changes, build and wait for SwiftMQ hot deployment cycle to kick in and deploy the changes, then observe the trace output to check for expected behaviour. 

But recently I discovered JRebel (formerly JavaRebel) and I thought I'd revisit the "Debugging in SwiftMQ" problem.

And the result is I've changed my debugging ways for good! Here's how:

First, grab yourself a 30-day evaluation copy (I can guarantee it won't be long before you buy a full licence!) of JRebel from the ZeroTurnaround website at http://www.zeroturnaround.com/jrebel/ and install it on your development machine. For the sake of this tutorial, we'll assume that the SwiftMQ Router you are developing against is on the same machine, but since we're going to debug using a remote connection (even on the local machine) it should work equally well regardless of the location of your SwiftMQ Router.

In order to debug directly from within the Java IDE of your choice (Netbeans, Eclipse and IntelliJ are all well supported), you should also install the appropriate IDE plugin, see http://www.zeroturnaround.com/reference-manual/ for specific instructions.

Now, using your favorite text editor change the start script for the SwiftMQ Router and add the following Java command flags:

-noverify -javaagent:/Applications/ZeroTurnaround/JRebel/jrebel.jar

to enable the JRebel classloader to take precedence over SwiftMQ's hot deployment mechanism; and
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8888,suspend=n

to enable remote debugging.

In my environment, my smqr1  shell script reads:

java -noverify -javaagent:/Applications/ZeroTurnaround/JRebel/jrebel.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8888,suspend=n -server -Xmx512m -cp ../../jars/swiftmq.jar:../../jars/jndi.jar:../../jars/jms.jar:../../jars/jsse.jar:../../jars/jnet.jar:../../jars/jcert.jar:../../jars/dom4j-full.jar:../../jars/jta-spec1_0_1.jar com.swiftmq.Router ../../config/router1/routerconfig.xml

with the required additional JVM flags bolded.

Note the suspend=n flag which determines whether the JVM application waits for a debugger to attach before starting. We'll set it to start without an attached debugger to begin with: more on this setting later.

So far we've installed JRebel on our development machine, together with the JRebel plugin for our favourite editor, and we've changed the SwiftMQ startup script to enable the JRebel classloader and allow us to connect with a remote debugger.

We're now ready to do some serious debugging!

Start your favourite IDE and load the SwiftMQ project you are working on. Again I'm going to make an assumption, in this case that you have some sort of working Swiftlet or JMS application that you are able to deploy to the SwiftMQ environment. You can read the SwiftMQ website to find out how to create a Swiftlet or JMS application, so that is outside the scope of this article.

In order for JRebel to work its magic, it needs to be "told" what to do and that requires a simple XML file called rebel.xml in the root of the JAR file you want to debug.

In the example below, I am working in Netbeans 6.7.1 on a Swiftlet called sdx-router with the compiled classes being placed in the directory highlighted in bold:

By adding a dir node in rebel.xml and setting the name to the location of my IDE's compiler output I am telling JRebel where it should look for classes to load whenever they are updated, for example when I make a change in the IDE and compile a Java class. 

The partial screen grab below shows the rebel.xml file sitting in the root directory (default package) of the source tree of the sdx-router Netbeans project: 

At this stage, if you have not already deployed your Swiftlet or JMS application for the first time, you must do so now so that the rebel.xml file is deployed into the SwiftMQ environment in the root of the Swiftlet JAR.

Once this initial deployment (which will use SwiftMQ's standard hot-deployment mechanism) has taken place, any subsequent changes you make in your IDE will be deployed by JRebel's classloader within fractions of a second of them being compiled! 

Even if you're not in a heavy debugging mode this is a huge improvement on the standard edit-build-SwiftMQ hot deploy cycle which can take a matter of minutes each time.

Onto debugging...

Earlier in this article we added some JVM flags to enable remote debugging using Java's standard debugging: if you've ever tried this with SwiftMQ before, you will have been disappointed as SwiftMQ's hot deployment mechanism makes it impossible to connect a remote debugger.

But with JRebel's classloader taking precedence, and with the JRebel plugin installed in your favourite IDE you are ready to go!

In Netbeans, simply attach your debugger by choosing Debug->Attach Debugger and the following screen will be shown:

The default settings are just fine if you are debugging against your development machine on port 8888. If you are working against another machine, or on another port, you should change the entries appropriately.

Click OK and within a few seconds the debugger connects to the running SwiftMQ Router and you are debugging.

Within your IDE you can now make changes to and compile a single class and JRebel's classloader will allow you to bypass the SwiftMQ hot deployment process, loading the new class directly into memory and allowing you to debug through it instantly, just as you would a standard Java application.

Even when you're in the middle of a debugging session you can make a change in a class as the Swiftlet is running, compile the single class and the next time the changed class is invoked the debugger kicks in - very impressive indeed!

Remember the suspend=n flag? 

Whilst that setting is fine if you are debugging a MessageProcessor, what if you want to debug parts of your Swiftlet which run only at startup, for example the startup method?

Simple. Just change the flag to suspend=y and SwiftMQ will "wait" for you to attach your IDE's debugger before launching - you can debug any part of your Swiftlet or JMS application as easily as a standard Java application!

In my next article, I'll post a video of SwiftMQ and JRebel in action - stay posted.