So was just thinking some interesting topic which will catch interest of all in developer community of my team. I did not want to come up with some new emerging technology with lots of technical jargon and theories. After all am not preparing a presentation for any (dumb) client who gets impressed by such not-so-useful articles/papers. My target audience is a bunch of smart and intelligent Java developers. So I was thinking of a topic which can catch the interest of these worldly creatures who then can use it for some better purpose to ease their development/debugging life. So the idea struck to my mind was Remote Debugging. I know this is nothing new to Java world, but still I find this feature very exciting, helpful & powerful. Remote debugging has always been an integral part of JDK or J2SE SDK since long and perhaps most unused or less explored by most of us. So what's remote debugging and why we actually need it?
Many a times, it so happens that some nasty bug shows up on the functional testing or the business testing environment, which are typically on the customer network infrastructure with very limited or no debugging tools? To make matters worse, when you try to reproduce the same bug on your local development environment, it won't show up and system will behave just fine. This is a scenario which each and every software project and its developers face without exception.
Traditionally, in such scenarios, we tend to put in a lot of debug statements (logger.debug or System.out.println) or in the case of web applications some hidden buttons which just the developer is aware of and on click of which a specific business scenario is executed and the logs, thereof, are generated. This approach has many flaws, as the typical algorithm illustrates:
1. Correctly identify [or guess] the piece of code which might be causing the bug to show up on env other then dev.
2. Code change to introduce debug statements. [Prone to introduce more bugs.]
3. Code change means extra round of unit/sanity testing and then new deployment.
4. Once deployed, collect the log and carefully analyze it to find the root cause. If nothing found, introduce more detailed debug statements i.e. Go back to step 1/2 and repeat.
5. If the root cause is found, fix it locally assuming that the bug is fixed [if the bug is not reproducible locally], remove all the debug statements introduced as a part of step 2 and generate a new build after successful unit/sanity testing.
6. Pray hard and hope the bug does not resurface again in your lifetime!
This technique is highly error prone and time consuming. To overcome this, as with most of the other matured programming languages, Java too ships with a capability to debug a remotely deployed application from your local development environment using your favorite IDE such as Eclipse. Java debugging capabilities is a result of JPDA (Java Platform Debugger Architecture). For more details on JPDA please refer JPDA Architecture
In this article, I won't dig much into the details of JPDA, but will focus on how to remotely debug an application using our good old friend – Eclipse. Here, I take up three common application scenarios and describe the remote debugging steps for each of them:
1. A standalone application. (such as a batch application)
2. A web application deployed on Apache Tomcat Servlet container.
3. A web application deployed on IBM WebSphere 5.1 application server.
To debug any application remotely, you need to...
- Make sure you are building (compiling) your application with available debug information.
- In Eclipse you can control these settings from Windows -> Preferences -> Java -> Compiler.
- If you are building your application with an ANT build script, then in the <javac> task, set the “debug” attribute to “true”.
- If you are building your application with Maven, set the debug configuration tag for maven-compiler-plugin to “true”.
- Deploy the application in the debug mode. This opens up a port for your debugger to get attached to the application's process and do its magic1. This is done by passing some pre-defined VM arguments in the application's launch scripts. The commonly used VM arguments are:
|-Xrunjdwp:<options>||Loads the JPDA implementation residing in targeted JVM.|
The commonly used <options>...
1. transport: Transport to use in connecting to debugger application. There can be many transport implementations out of which Sun comes with two mostly used implementation - A socket transport based on TCP/IP and a shared memory transport.
2. address: Transport address for the debugger application to attach.
3. server: If set to 'y'(mostly used), it listens for a debugger application to attach. Otherwise, attach to the debugger application at the specified address.
4. suspend: If set to 'y', JVM starts in suspended mode and stays suspended until a debugger is attached to it.
For a full JVM-version-specific list of 'runjdwp' options, please refer to:
- Java 1.4.2: http://java.sun.com/j2se/1.4.2/docs/guide/jpda/conninv.html#Invocation
- Java 1.5: http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html#Invocation
- Java 6: http://java.sun.com/javase/6/docs/technotes/guides/jpda/conninv.html#Invocation
To remotely debug a standalone application, the first thing you need to do is compile the application source with available debug information, as mentioned in the above section and package it as a .jar file.
For example your jar file name is sandbox.jar and the class launches the application is foo.bar.Test, then you can run Test as
java -cp ./sandbox.jar foo.bar.TestHowever, with just this, the application will not allow remote debugging. In order to debug Test application remotely, start it in the debug mode by providing the additional VM arguments, as shown below:
-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=10007,suspend=nThese options will allow the Test application to execute in debug mode with a socket open on port 10007 for a debugger to attach to.
To verify if your application is truly running in debug mode and has opened a socket on 10007 listening for debugger to attach, on windows machine open a command prompt and type netstat /a or on Unix/Linux environment netstat -a and you should be able to see a TCP socket on port 10007 with state as LISTEN.
In a real life scenario, you won't be starting your standalone application directly by issuing java command. You will have a script file which will be invoking your application. So to avoid any script change to run application under debug mode or not, you can set an environment variable _JAVA_OPTIONS before your script invokes java command. For instance, if your application is launched by a script file called sandbox.sh then before executing sandbox.sh you can simply set _JAVA_OPTIONS environment variable as:
set _JAVA_OPTIONS="-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=10007,suspend=n"Once you are done with your debugging, you can reset _JAVA_OPTIONS to blank and then re-execute sandbox.sh
Once your application is running in debug mode, you can switch back to Eclipse and open the Debug configuration dialogue from Run -> Debug Configuration... menu item. Over there, select “Remote Java Application” from the left navigation tree. Right click and select New.
On the right hand side, under Connect tab, select the project which holds the foo.bar.Test class which you intend to debug remotely. Select connection type as "Standard (Socket Attach)"
Under the 'Connection Properties', enter the host name or IP where you are running your foo.bar.Test class in debug mode and port as 10007. Depending upon your remote JVM settings and your wish you can check/uncheck "Allow termination of remote VM" checkbox.
Click Debug button and your eclipse debugger is now connected to your remote application and ready to debug. Insert debug points in your program and you are ready to debug your application remotely.
Remote debugging a web application on Apache Tomcat
The steps for compiling and building your application remains the same, as described above. What changes is how we start the Tomcat server. To start Tomcat in the debug mode, set two environment variables as...
JPDA_TRANSPORT=dt_socketAfter setting the above system environment variables, start Tomcat using following command…
%CATALINA_HOME%\bin\catalina.bat jpda startOR on Linux...
$CATALINA_HOME/bin/catalina.sh jpda startOnce the server is up, it's in debug mode with a socket on port 10007 open for any debugger to attach for debugging the deployed application. Once Tomcat is running in debug mode you can follow the Eclipse Configuration steps to debug the web application. Once you are done with debugging, you can stop the server by:
%CATALINA_HOME%\bin\catalina.bat stopOR on Linux...
Remote debugging a web application on IBM WebSphere 5.1
Once again, the steps for compiling and building your application remains the same, as described above. What changes is how we inform WebSphere to run in debug mode. To do this:
1. Open WebSphere admin console.
2. Navigate to Servers -> Application Servers -> <YOUR_SERVER_NAME> -> Process Definitions -> Java Virtual Machine.
3. Scroll down and check the Debug Mode check box.
4. You can either accept the default debug arguments in Debug Arguments textbox or can provide as per your requirement.
5. Restart your application server.
6. Done !!!
Once your WebSphere is running debug mode, follow Eclipse Configuration to do debug the web application.
Some times you might get an error message while you try to connect to remote JVM through Eclipse...
Failed to connect to remote VM. Connection refused.The most likely case if you are on a Windows machine is that, you have the Windows firewall on and it is preventing you from connecting. Stop the firewall and try to reconnect. Don’t forget to turn your firewall on once you are done, else you might end up debugging your Windows for mysterious behavior if some hacker gets hold of your PC ;-)
Here’s to smarter debugging!
Links to JPDA documentation & debug options across J2SE 1.4.2., 1.5.0 & 6:
Detailed article on Remote Debugging in Java:
Remote Debugging with Eclipse