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.

Another important setting is the scanIntervalSeconds. Since Jetty is running directly from the project tree, you would expect it to be able to detect changes. Even without this setting, any web resource that has changed, such as a JSP, Facelet template, Javascript, CSS, or image file, will automatically be made available to the running application. With this setting enabled, Jetty will scan your Java source directories at the interval provided. If it detects a change in a Java class or Java property file, it will reload the application to update the classpath. If you do not want this feature, simply set the value to 0.

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

Thanks dux!

Posted at 12:58 AM in Java | Permalink Icon Permalink

20 Comments from the Peanut Gallery

1 | Posted by Nitin on May 21, 2007 at 04:08 PM EST

Thanks for this information. However I am not able to get it to work. I get the following error:

[DEBUG] Building Maven global-level plugin registry from: 'C:\maven-2.0.6\bin\..\conf\plugin-registr y.xml' [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [ERROR] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Invalid task 'debug': you must specify a valid lifecycle phase, or a goal in the format plugi n:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal [INFO] ------------------------------------------------------------------------ [DEBUG] Trace org.apache.maven.BuildFailureException: Invalid task 'debug': you must specify a valid lifecycle pha se, or a goal in the format plugin:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal at org.apache.maven.lifecycle.DefaultLifecycleExecutor.getMojoDescriptor(DefaultLifecycleExe cutor.java:1515) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.segmentTaskListByAggregationNeeds(Def aultLifecycleExecutor.java:386) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java :138) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:334) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:125) at org.apache.maven.cli.MavenCli.main(MavenCli.java:272) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315) at org.codehaus.classworlds.Launcher.launch(Launcher.java:255) at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430) at org.codehaus.classworlds.Launcher.main(Launcher.java:375)

2 | Posted by Dan Allen on May 22, 2007 at 02:27 AM EST

My bad, I had the command listed incorrectly. It is not possible to pass the Java options directly to maven. Instead, you need to set the MAVEN_OPTS environment variable (which is similar to JAVA_OPTS), and then run the maven command to start jetty in the next command. The nice part is that once you set this environment variable in your shell, you don't have to do it again, so subsequent starts are done simply by executing the maven command to start jetty.

3 | Posted by Marcelo Alcantara on July 19, 2007 at 05:28 PM EST

Hi, Do you know how can I set up MAVNE_OPTS using m2eclipse? Thanks!

4 | Posted by Lee on July 27, 2007 at 06:11 PM EST

Marcelo,

Set MAVEN_OPTS in the environment before you run eclipse. Then it will be available when you run maven inside eclipse.

5 | Posted by ken on December 31, 2007 at 01:16 PM EST

Have anybody had success trying this with the tomcat/cargo plugin? After starting the container (with successful war deployment), I launch the remote debugger and attach to the application. However, the JVM does not halt on any of the breakpoints i set. I'm using eclipse, and verified my source is setup correctly. Any suggestions?

6 | Posted by Linuxerianer on January 06, 2008 at 01:38 PM EST

"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, [...] Unfortunately, there doesn't seem to be a way to set it to an infinite value, but any large number will be sufficient."

Try a value of "0". This worked for me.

7 | Posted by Jahid on March 20, 2008 at 09:26 AM EST

Well, its working fine, and thanks a lot for such a good tutorial. But the auto update is not working after 5 seconds. I am using Echo3 framework to develop application. But when I make some changes, its not reflected. Seems like jetty is not reloading project changes. But it loads changes if I do "mvn clean && mvn install" the project, which means if i build the project it loads, otherwise it doesnt. Any idea?

8 | Posted by Dan Allen on March 20, 2008 at 04:30 PM EST

Is it reflecting changes to static files like JavaScript or CSS? If so, then it might just be an Echo 3 thing. If you aren't seeing changes to static files either, then something seems awry with the Jetty configuration.

9 | Posted by Iago on June 11, 2008 at 07:28 AM EST

Thanks a lot

10 | Posted by james on August 14, 2008 at 03:28 PM EST

Hello,

I am very new to Maven. I use Jetty as a container for a web application that I'm working on, an I use Ant to build the project.

I want to enable remote debugging. I've done so with YourKit, but YourKit costs $500 per year. I'd like to enable remote debugging without having to modify the structure of my project if possible and without losing an arm and a leg. I like YourKit because I didn't have to change a thing in my project to set it up!

I also notice that there are countless configuration files in Jetty. I don't know where you are seeing the Maven 2 plugin configuration for running jetty configuration? Where is this? What file is it in? Is the the pom.xml in jetty_home or is it part of your project? I've grepped for it and am coming up with nothing? What version of Jetty are you using? I'm on 6.1.5. Does this make a difference? Where can I get the Geronimo dependency?

I'm trying to run the mvn -e jetty:run command and get the following stack trace:

[INFO] Searching repository for plugin with prefix: 'jetty'. [INFO] ------------------------------------------------------------------------ [ERROR] BUILD ERROR [INFO] ------------------------------------------------------------------------ [INFO] The plugin 'org.apache.maven.plugins:maven-jetty-plugin' does not exist or no valid version could be found [INFO] ------------------------------------------------------------------------ [INFO] Trace org.apache.maven.lifecycle.LifecycleExecutionException: The plugin 'org.apache.maven.plugins:maven-jetty-plugin' does not exist or no valid version could be found at org.apache.maven.lifecycle.DefaultLifecycleExecutor.verifyPlugin(DefaultLifecycleExecutor.java:1303) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.getMojoDescriptor(DefaultLifecycleExecutor.java:1542) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.segmentTaskListByAggregationNeeds(DefaultLifecycleExecutor.java:405) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:137) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:336) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:129) at org.apache.maven.cli.MavenCli.main(MavenCli.java:287) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315) at org.codehaus.classworlds.Launcher.launch(Launcher.java:255) at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430) at org.codehaus.classworlds.Launcher.main(Launcher.java:375) Caused by: org.apache.maven.plugin.version.PluginVersionNotFoundException: The plugin 'org.apache.maven.plugins:maven-jetty-plugin' does not exist or no valid version could be found at org.apache.maven.plugin.version.DefaultPluginVersionManager.resolvePluginVersion(DefaultPluginVersionManager.java:229) at org.apache.maven.plugin.version.DefaultPluginVersionManager.resolvePluginVersion(DefaultPluginVersionManager.java:91) at org.apache.maven.plugin.DefaultPluginManager.verifyPlugin(DefaultPluginManager.java:171) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.verifyPlugin(DefaultLifecycleExecutor.java:1274) ... 14 more [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4 seconds [INFO] Finished at: Thu Aug 14 13:05:41 PDT 2008 [INFO] Final Memory: 5M/9M [INFO] ------------------------------------------------------------------------

Can you or someone point me in the right direction? I know this is a lot of questions but the documentation isn't immediately clear.

Thanks for any help :) James

11 | Posted by dux on September 28, 2009 at 08:47 AM EST

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

12 | Posted by Javin @ HashMap vs Hashtable in Java on April 11, 2011 at 06:41 AM EST

Nice article ,From my experience Remote Debugging is very useful for troubleshooting production issues on large enterprise application, specially if your application is live and you don't have proper development environment setup in your dev box.

Thanks Javin

13 | Posted by KK@ java enum tutorials, examples on February 14, 2012 at 10:17 AM EST

Good amount of informaiton on maven remote debugging,

This is very useful when we are not sure about other debugging options enabled. We can enable debugging in many ways.

14 | Posted by pranav on March 18, 2012 at 10:54 PM EST

Very nice article, very useful because remote debugging is now a days extremely necessary due to lack of exact environments which client have.

15 | Posted by EC on April 03, 2012 at 06:00 AM EST

Very useful post, I was looking for this specific issue. The thing is... I follow the steps but when I run the debug configuration in Eclipse I get: "Fail to connect to remote VM. Connection refused". Do you have any tips?

16 | Posted by EC on April 03, 2012 at 06:21 AM EST

I have checked that the port is not in use

17 | Posted by Emilio Cuberos (EC) on April 03, 2012 at 07:29 AM EST

Hi, I found the problem: I use Mac OS and I exported the MAVEN_OPS variable in a different terminal tab (and from a different path) than the one I used for running jetty. The value of the variable was not updated when I ran jetty. I have to find out why this happens, but anyway now I can continue working :)

18 | Posted by simacho on June 06, 2012 at 03:28 PM EST

@Emilio Cubreros the cause of this is the same that windows when you modify an env variable in a console just exporting the value the life of it is bond to the console life. You should do a proper export that is permanent. Check this blog: http://forgettabledev.blogspot.com.ar/2012/06/how-to-define-system-variable-in-mac-os.html

19 | Posted by rahul on September 11, 2012 at 10:38 PM EST

hi I have tried this both ways. I set an env-variable by going to windows environment properties and creating a new environment variable called MAVEN_OPTS, appended it to the path variable and then created a new remote java application in Eclipse which tries to attach to this JVM at port 4000 but my connection keeps timing out. I have these maven tasks created in eclipse through m2eclpse..and one of them is jetty:run. Over in that task I tried to add this stuff as a new env variable but I am not able to attach. The only thing I have not tried is passing them as -D values to JVM but I am not sue if we need that. Can you tell me where I am going wrong.

20 | Posted by Sunil on September 16, 2013 at 11:36 AM EST

Do you know whether Maven could be installed in the server with normal user privileges? I've a jetty hosting with OC https://www.onlinecares.com . But they are not activate Maven by default. Or do I need to go for a VPS or dedicated server with private jetty support?