Remote Debugging with Jetty
March 07, 2007
Jetty is one of those fantastic tools which seems to lack readily available information. Specifically, I am talking about remote debugging. Now that Jetty works so nicely with Maven 2, it is almost a no-brainer to use it to run your application right from the project tree. If you go down that path, you won't stray far before you start trying to figure out how to enable remote debugging. That is where things get a little tricky.
Before we go there, let's take a look at the Maven 2 plugin configuration for running jetty.
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <dependencies> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-j2ee_1.4_spec</artifactId> <version>1.1</version> </dependency> </dependencies> <configuration> <connectors> <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>3600000</maxIdleTime> </connector> </connectors> <scanIntervalSeconds>5</scanIntervalSeconds> </configuration> </plugin>
Now, if you are already familiar with this configuration, I want you to look it over again, rather than proudly skipping it. This time, pay attention to the geronimo dependency and the maxIdleTime setting, because I will be discussing both of them shortly.
Once this plugin configuration is installed in your pom.xml file, running jetty is simply a matter of executing the command mvn jetty:run. That sure beats having to deploy it to Tomcat or JBoss, especially when you are doing iterative development.
I want to start by mentioning the geronimo dependency. When deploying your application to a full appserver, the JavaEE APIs are already going to be loaded. However, on a servlet container like Jetty (or Tomcat), these APIs have to be supplied. In Tomcat you can stuff the jar file in the shared library directory, but with Jetty running directly from Maven 2, the library has to be supplied at runtime. The dependency nodes within the Jetty plugin allow you to include these extra libraries, if your application requires them, of course.
I promised that I would answer the question about how to remote debug, so here it is. Unfortunately, because of details regarding how the JVM enables remote debugging, the configuration for enabling it lies outside the scope of Maven 2 and thus the Jetty plugin as well. Instead, arguments must be supplied to the Maven 2 command itself. If you have ever setup remote debugging of another Java application, these arguments should look familiar to you.
export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n" mvn jetty:run
As you can see, details about the port and the suspend flag cannot be configured within the pom.xml. Instead, they are passed to maven using the MAVEN_OPTS environment variable. I tend to setup a script called run-jetty.sh to handle all of these details for me, because I certainly don't want to be typing them every time I need to kick off jetty in debug mode. Once invoked, you then hook to the running server using the normal method in your IDE of choice.
Ah, it keeps dying! That was my initial reaction when I first began as well. When debugging with Tomcat, I would routinely leave the debugger hanging at a breakpoint while I leisurely strolled to a long developer meeting. Upon my return, the debugger would still be waiting patiently for me to allow it to continue. (It would also greet me as "master"). On the other hand, remote debugging with Jetty wouldn't even stick around long enough to let me inspect the code while I was giving it dedicated attention. As it turns out, the default timeout in Jetty for idle connections is conservatively low. That's where the maxIdleTime setting comes into play. This value is the number of seconds that Jetty will allow you to hang on to a connection idly. You are going to want to bump this up to a really high number, especially since you are likely using Jetty strictly for development purposes. Allow it to be your servant. Make it wait. Unfortunately, there doesn't seem to be a way to set it to an infinite value, but any large number will be sufficient. Eventually, you have to get back to coding.
Now that the Jetty force is with you, go forth and be productive!
Update: If you're working under windows, don't put the double quotation marks when you set the MAVEN_OPTS variable. Otherwise the variable will not be taken.
set MAVEN_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n