Friday, April 10, 2009

Remote Debugging JVM Applications

Recently, I just solved a tricky issue I was having with Canoo WebTest using the JVM's remote debugging capabilities, and thought it might be useful to write a brief how-to.

The JVM provides a useful feature called the Java Platform Debugger, or JPDA. When enabled via startup options, it provides a port through which debuggers (e.g. Eclipse) are able to connect and do their thing. This allows you to debug practically any application, such as Tomcat, or in my case, WebTest (running inside of an Ant process).

There are a bunch of available options for JPDA, but here is the basic incantation:

  • jdwp: Java Debug Wire Protocol
  • transport: indicates that debuggers should attach using sockets, as opposed to the other option, which is shared memory (dt_shmem)
  • server: when 'y', listens for a debugger application to attach (otherwise, attempt to connect to the specified debugging instance)
  • suspend: the JVM should wait for a debugger application to attach before starting up
  • address: the port to expose for debugger applications (or address to connect to in order to debug)

*Note: you may have seen a different set of arguments for starting a JVM in debug mode:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8008
This is required for Java versions < 5.

You can find a full list of options here.

Now that you have your application faithfully waiting for you to debug it ('Listening for transport dt_socket at address: 8008'), you just have to set up a remote debug configuration in Eclipse and run it:
  1. Run > Debug Configurations... > new Remote Java Application
  2. Connection Type: Standard (Socket Attach)
  3. Connection Properties: host and port of JDWP application
  4. Allow termination of remote VM: if selected, when you are finished with the debugger and click the terminate button in Eclipse, the remote application will exit also
That's it!

So back to my original issue. I had written a WebTest to upload images on a particular page, verify them and then click the save button, which makes use of an ajax validation mechanism to perform a server-side page validation before actually submitting the form. The problem was, my <clickButton/> wasn't doing anything. No errors, warnings, debug information of any kind in any log, simply nothing. I was already debugging the web application, so I knew that there wasn't any server-side activity holding it up, so I fired up WebTest in debug mode, downloaded the source (as well as htmlunit, which it is built on), and set some breakpoints (a few in ClickButton, as well as in htmlunit's XMLHttpRequest implementation). It turned out that XMLHttpRequest was throwing a ClassCastException, which was being silently swallowed, because my <verifyImages/> step had inadvertently changed the current response to UnexpectedPage, when HtmlPage was expected. Remove the verifyImages step, and voila, everything works!

Update: I forgot to mention that I created a JIRA issue for the verifyImages step: