Depurar Servlets

Podemo depurar servlets con los mismos comandos jdb usados para depurar un applet o una aplicación. JSDK "JavaTM Servlet Development Kit" proporciona una programa llamado servletrunner que nos permite ejecutar un servlet sin un navegador web. En la mayoría de los sistemas, este programa simplemente ejecuta el comando java sun.servlet.http.HttpServer. Por lo tanto, podemos arrancar la sesión jdb con la clase HttpServer.

Un punto importante a recordar cuando depuramos servlets es que el servidor Web Java y servletrunner realizan la carga y descargas de servlets, pero no incluyen el directorio servlets en el CLASSPATH. Esto significa que los servlets se cargan usando un cargador de clases personalizado y no por el cargador de clases por defecto del sistema.


Ejecutar servletrunner en Modo Depuración

En este ejemplo, se incluye el directorio de ejemplos servlets en el CLASSPATH. Configuramos el CLASSPATH en modo depuración de esta forma:
Unix

$ export CLASSPATH=./lib/jsdk.jar:./examples:$CLASSPATH  

Windows

$ set CLASSPATH=lib\jsdk.jar;examples;%classpath%
Para arrancar el programa servletrunner, podemos ejecutar el script de arranque suministrado llamado servletrunner o simplemente suministramos las clases servletrunner como parámetros de jdb. Este ejemplo usa el parámetro servletrunner.
$ jdb sun.servlet.http.HttpServer
Initializing jdb...
0xee2fa2f8:class(sun.servlet.http.HttpServer)
> stop in SnoopServlet.doGet
Breakpoint set in SnoopServlet.doGet
> run
run sun.servlet.http.HttpServer
running ...
main[1] servletrunner starting with settings:
port = 8080
backlog = 50
max handlers = 100
timeout = 5000
servlet dir = ./examples
document dir = ./examples
servlet propfile = ./examples/servlet.properties
Para ejecutar SnoopServlet en modo depuración, introducimos la siguiente URL donde yourmachine es la máquina donde arrancamos el servletrunner y 8080 es el número d puerto mostrado en las selecciones de salida.
http://yourmachine:8080/servlet/SnoopServlet
En este ejemplo jdb para en la primera línea del método doGet del servlet. El navegador espera una respuesta de nuestro servlet hasta que se pase el timeout.
main[1] SnoopServlet: init

Breakpoint hit: SnoopServlet.doGet (SnoopServlet:45)
Thread-105[1] 
Podemo usar el comando list para saber dónde se ha parado jdb en el fuente.
Thread-105[1] list
41        throws ServletException, IOException
42        {
43      PrintWriter     out;
44    
45 =>   res.setContentType("text/html");
46      out = res.getWriter ();
47    
48      out.println("<html>");
49      out.println("<head>
                     <title>Snoop Servlet
                     </title></head>");
Thread-105[1] 
El servlet puede continuar usando el comando cont.
Thread-105[1] cont

Ejecutar el Java Web Server en Modo Depuración

La versión JSDK no contiena las clases disponibles en el Java Web Server y también tiene su propia configuración servlet especial. Si no podemos ejecutar nuestro servlet desde servletrunner, otra opción puede ser ejecutar el servidor web Java en modo depuración.

Para hacer esto añadimos la bandera -debug como el primer parámetro después del programa java. Por ejemplo en el script bin/js cambiamos la línea Java para que se parezca a esto. En versiones anteriores de la plataforma java 2, también tendremos que cambiar el puntero del programa a la variable $JAVA a java_g en vez de a java.

Antes:

exec $JAVA $THREADS $JITCOMPILER $COMPILER $MS $MX \

Depués:

exec $JAVA -debug $THREADS $JITCOMPILER 
                                  $COMPILER $MS $MX \

Aquí está como conectar remotamente con el Java Web Server. La password de agente es generada sobre la slaida estandard desde el Java Web Server pero puede ser redirigida a un fichero en cualquier lugar. Podemos encontrar dónde chequeando los scripts de arranque del Java Web Server.
jdb -host localhost -password <the agent password>
Los servlets se cargan por un cargador de clases separado si están contenidos en el directorio servlets, que no está en el CLASSPATH usado cuando se arrancó el Java Web server. Desafortunadamente, cuando depuramos en modo remoto con jdb, no podemos controlar el cargador de clases personalizado y solicitarle que cargue el servlet, por eso tenemos que incluir el directorio servlets en el CLASSPATH para depurar o cargar el servlet requiriéndolo a través de un navegador y luego situando un punto de ruptura una vez que el servlet está ejecutando.

En este siguiente ejemplo, se incluye el jdc.WebServer.PasswordServlet en el CLASSPATH cuando se arranca el Java Web server. El ejemplo selecciona un punto de ruptura para parar el método service de este servlet, que es el método de proceso principal.

La salida estándard del Java Web Server standard produce este mensaje, que nos permite seguir con la sesión remota de jdb:

Agent password=3yg23k

$ jdb -host localhost -password 3yg23k
Initializing jdb...
> stop in jdc.WebServer.PasswordServlet:service
Breakpoint set in jdc.WebServer.PasswordServlet.service
> stop
Current breakpoints set:
        jdc.WebServer.PasswordServlet:111
El segundo stop lista los puntos de ruptura actuales en esta sesión y muestra el número de línea donde se encuentan. Ahora podemos llamar al servlet a través de nuestra página HTML. En este ejemplo, el servlet está ejecutando una operación POST:
<FORM METHOD="post" action="/servlet/PasswordServlet">
<INPUT TYPE=TEXT SIZE=15 Name="user" Value="">
<INPUT TYPE=SUBMIT Name="Submit" Value="Submit">
</FORM>
Obtenemos el control del thread del Java Web Server cuando se alcanza el punto de ruptura, y podemos continuar depurando usando las mismas técnicas que se usarón en la sección Depuración Remota.
Breakpoint hit: jdc.WebServer.PasswordServlet.service 
(PasswordServlet:111) webpageservice Handler[1] where
[1] jdc.WebServer.PasswordServlet.service 
                               (PasswordServlet:111)
[2] javax.servlet.http.HttpServlet.service 
                               (HttpServlet:588)
[3] com.sun.server.ServletState.callService 
                               (ServletState:204)
[4] com.sun.server.ServletManager.callServletService 
                               (ServletManager:940)
[5] com.sun.server.http.InvokerServlet.service 
                               (InvokerServlet:101)
Un problema común cuando se usan el Java WebServer y otros entornos de servlets es que se lanzan excepiones pero son capturadas y manejadas desde fuera del ámbito del servlet. El comando catch nos permite atrapar todas estas excepciones.
webpageservice Handler[1] catch java.io.IOException
webpageservice Handler[1] 
Exception: java.io.FileNotFoundException        
 at com.sun.server.http.FileServlet.sendResponse(
                                FileServlet.java:153)
 at com.sun.server.http.FileServlet.service(
                                FileServlet.java:114)
 at com.sun.server.webserver.FileServlet.service(
                                FileServlet.java:202)
 at javax.servlet.http.HttpServlet.service(
                                HttpServlet.java:588)
 at com.sun.server.ServletManager.callServletService(
			        ServletManager.java:936)
 at com.sun.server.webserver.HttpServiceHandler
           .handleRequest(HttpServiceHandler.java:416)
 at com.sun.server.webserver.HttpServiceHandler
           .handleRequest(HttpServiceHandler.java:246)
 at com.sun.server.HandlerThread.run(
                                HandlerThread.java:154)
Este sencillo ejemplo fue generado cuando los ficheros no se encontraban pero esta técnica puede usarse para problemas con datos posteados. Recordamos usar cont para permitir que el servidor web continúe. Para limpiar está trampa usamos el comando ignore.
webpageservice Handler[1] ignore java.io.IOException
webpageservice Handler[1] catch
webpageservice Handler[1] 

Ozito