MokaByte Numero 23  -  Ottobre 98 

di
  Giacomo Cosenza 
Debugging di Servlet 


 
Ecco una tecnica veramente utile per risolvere uno dei problemi più pressanti della programmazione dei servlet: il debug. 

 
 


 
 


Da sempre il debugging del codice comporta un significativo impatto sul ciclo di vita delle applicazioni e le applicazioni web dinamiche non sfuggono certo a questa regola. Semmai, la necessità di integrare tra loro più tecnologie e di utilizzare architetture distribuite rende l’attività di debugging ancora più critica. Questo articolo descrive una semplice tecnica di debugging delle servlet java, i cui principi possono essere applicati in una qualsiasi configurazione di piattaforma, web server, servlet engine e ambiente di sviluppo.


 
Introduzione

Nel maggio del 1997 un nostro cliente ci chiese di realizzare un’applicazione web di internet home banking. Il cliente, partner di Oracle, desiderava che sviluppassimo l’applicazione web con OWAS 3.0 (Oracle Web Application Server) e tecnologia java su piattaforma Windows NT4.0. Avendo avuto precedenti esperienze di sviluppo con tale configurazione, in particolare con la cartridge java di OWAS, obiettammo che la versione 3.0 per NT di OWAS era tutt’altro che stabile e che i requisiti funzionali dell’applicazione non necessitavano dei servizi infrastrutturali di OWAS. Suggerimmo di utilizzare le servlet java, con le quali avevamo cominciato a giocare qualche mese prima, ma fu un dialogo tra sordi. Nella vana speranza che Oracle avrebbe corretto i malfunzionamenti di OWAS 3.0 per NT entro la data di consegna dell’applicazione, cominciammo lo sviluppo su piattaforma Solaris. A qualche giorno dall’inizio dei test, provammo ad eseguire il porting su piattaforma NT. Risultato: buca! In fretta e furia riscrivemmo l’applicazione con le servlet, ma Netscape FastTrack3.0 per NT aveva a sua volta un piccolo problema: si rifiutava di leggere la variabile di ambiente per l’inclusione della libreria dinamica (dll) di Oracle che implementa le chiamate native di JDBC-OCI. Avendo a disposizione anche il JDBC-thin (100% pure java), il problema era relativo, ma decidemmo comunque di fare un terzo porting dell’applicazione implementandola in java con le API CORBA-compliant di Netscape dette WAI (Web Application Interface). In quei giorni cominciammo a sentire l’esigenza di uno strumento di debugging del codice java server-side più sofisticato rispetto a delle semplici print sullo standard output. Ci trovammo infatti a "debuggare" tre implementazione della medesima applicazione e, per di più, su due differenti piattaforme: oltre che una scocciatura, un evidente danno economico. Al termine di quel progetto, la mia azienda mi chiese di cambiare ruolo e di passare ad occuparmi dello sviluppo di nuovi rapporti commerciali. Per qualche mese mi disinteressai del problema del debugging del codice java server-side, fino a quando nel luglio di quest’anno un progettista, di cui nutro molta stima, mi comunicò cha la sua azienda aveva intenzione di cominciare a sviluppare applicazioni web utilizzando le servlet java. Mi domandava se potevo suggerirgli un buono strumento di debugging delle servlet e, purtroppo, non fui in grado di rispondendergli. Per non perdere la sua stima, decisi di sfruttare il fatto che ad agosto non esiste commerciale che non si trovi a mollo in acque più o meno esotiche, per dedicare, indisturbato, qualche giorno a questo problema. Con grande sorpresa mi accorsi che la soluzione era di una banalità sconcertante e che, benchè alcuni produttori di software avessero cominciato a realizzare strumenti ad hoc per il debugging delle servlet (p.e. ServletDebugging di Livesoftware Inc.) , non ne avrei avuto alcun bisogno.
 
 

I principi della tecnica di debugging delle servlet

     
Qualsiasi programma java ha come punto di ingresso la classe che contiene il metodo statico main(). Per eseguire una qualsiasi applicazione java, alla linea di comando che chiama l’interprete java (la java virtual machine) dovete passare il nome della classe che contiene il metodo main(). Benchè nel caso delle applet cio’ non sia evidente, in quanto è il browser a lanciare l’interprete e a indicargli il punto di ingresso, la procedura di esecuzione è esattamente la medesima. Il principio rimane ancora una volta lo stesso anche per le servlet. Quando il web server riceve da un browser una richiesta corrispondente all’esecuzione di una servlet, quest’ultima viene eseguita all’interno di una java virtual machine associata al web server e precedentemente inizializzata con l’indicazione del punto di ingresso, ossia della classe che contiene il metodo main(). Nel caso in cui il web server sia a sua volta implementato in java (p.e. Java Web Server di Sun), le servlet vengono eseguite all’interno dell’interprete java in cui gira lo stesso web server. Questo è uno dei motivi per cui si ritiene che i web server java siano meno stabili rispetto a quelli nativi: se la servlet dovesse inopportunamente "uccidere" il processo in cui gira la virtual machine, crollerebbe anche il web server con il risultato di interrompere qualsiasi servizio, anche quello di semplici pagine HTML. Viceversa, nel caso dei web server nativi, il processo in cui girano le servlet è ben distinto da quello in cui gira il web server e i due processi comunicano tra loro via socket. Un’eventuale interruzione del processo in cui gira la virtual machine delle servlet consentirebbe al web server di continuare a fornire tutti gli altri servizi altrimenti implementati.
 

Esemplificazione della tecnica di debugging

     
Per esemplificare la tecnica di debugging cominceremo riferendoci al solo JDK1.1.6 e al JSDK2.0, per poi mostrare come, mutatis mutandis, la tecnica sia applicabile in un potente ambiente integrato di programmazione come VisualAge per Java (VAJ) di IBM. In luogo di VAJ avremmo potuto scegliere un qualsiasi altro IDE java, per esempio VisualCafe di Symantec, JBuilder di Inprise (ex Borland) o il corrispondente Valhalla di Oracle, ma l’IDE di IBM ha una caratteristica che a mio avviso lo rende, in particolare per il debugging, superiore a tutti gli altri: un compilatore java incrementale, di cui valuteremo qualche vantaggio più avanti.

La distribuzione di JDSK2.0 contiene anche un mini web server, il cui unico scopo è di fornire al programmatore un ambiente semplice e leggero per l’esecuzione dimostrativa delle potenzialità delle servlet. Questo mini web server si trova nella directory bin dell’installazione di JSDK2.0 sotto il nome di servletrunner. La distribuzione per UNIX di JSDK2.0 fornisce indicazioni molto più significative rispetto alla corrispondente distribuzione per Windows32. Mentre in quest’ultimo caso il servletrunner appare come un eseguibile, nella distribuzione per UNIX non è altro che uno script di shell dalla cui lettura risulta immediatamente che si tratta di una implementazione elementare di un web server java. Alla fine dello script servletrunner trovate infatti le seguenti linee di codice:

     
# Add JSDK classes to default class path.

CLASSPATH=$JSDK_HOME/classes:$JSDK_HOME/lib/jsdk.jar:$CLASSPATH

export CLASSPATH

# Start server and pass any arguments specified on command line.

cd $JSDK_HOME

$JAVA_HOME/bin/java sun.servlet.http.HttpServer $*

     
Il primo comando include nel CLASSPATH le classi necessarie alla compilazione e all’esecuzione delle servlet, mentre il secondo non fa altro che lanciare l’esecuzione della java virtual machine passandole come argomento la classe HttpServer che, guarda caso, contiene il metodo main(). Se espandete il contenuto di jsdk.jar, che si trova nella directory lib della distribuzione per UNIX, potete verificare la presenza della classe HttpServer nel package sun.servlet.http. Se espandete il corrispondente jsdk.jar della distribuzione per Windows32, troverete la medesima classe. Ciò dimostra che anche per la piattaforma Windows32, il servletrunner non è altro che un eseguibile che chiama la java virtual machine con punto di ingresso nel metodo main() della classe HttpServer.

A questo punto abbiamo tutte le informazioni necessarie per eseguire il debugging delle servlet. A titolo esemplificativo useremo, su piattaforma Windows32, la più semplice delle servlet di test fornite dalla Sun con la distribuzione di JSDK2.0: SimpleServlet. In Fig.1 sono mostrati i passi, di seguito commentati, del debugging di SimpleServlet.

     

Fig.1 – Procedura di debugging
 
 

In primo luogo è necessario compilare in debug mode la classe SimpleServlet con il seguente comando:

     

    C:\JSDK2.0\examples\javac –g SimpleServlet.java

Il successivo comando
    C:\JSDK2.0\examples\jdb
lancia il debugger java. Per indicare a jdb la classe che vogliamo "debuggare" utilizziamo il comando
    >load SimpleServlet
e per lanciare l’applicazione, il comando
    >run sun.servlet.http.HttpServer
che indica a jdb la classe che contiene il metodo main(). Il seguente output
    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

    main[1]
     

evidenzia che il debugger è di fatto diventato un mini web server che si mette in attesa delle richieste di un client browser. Prima di collegarsi con un qualsiasi browser al mini web server, inseriamo un breakpoint nel metodo doGet() della classe SimpleServlet con il comando
    main[1] stop in SimpleServlet.doGet
     
A questo punto non ci rimane che collegarci via browser al mini web server che sta girando in debug mode digitando l’URL http://localhost:8080/servlet/SimpleServlet. Noterete che il borwser rimane in attesa della risposta del mini web server. Riportate il focus alla shell dos e vi accorgerete che l’esecuzione si è interrotta, dopo l’inizializzazione di SimpleServlet, nel breakpoint precedentemente assegnato
 
    main[1] SimpleServlet: init

    Breakpoint hit: SimpleServlet.doGet (SimpleServlet:45)

    Thread-131[1]

Il succesivo comando list visualizza sia il listato del metodo in cui avete settato il breakpoint, che l’istruzione java in cui si è interrotta l’esecuzione del programma.
 

Thread-131[1] list

41 HttpServletResponse response

42 ) throws ServletException, IOException

43 {

44 PrintWriter out;

45 => String title "Simple Servlet Output";

46

47 // set content type and other response header fields first

48 response.setContentType("text/html");

49
Thread-131[1] 

     
Il comando next esegue l’istruzione corrente e il successivo comando list visualizza nuovamente il listato del metodo doGet(), evidenziando l’avanzamento dell’esecuzione del programma. Con il comando print title chiediamo al debugger di visualizzare a video il valore della variabile title. Il comando cont consente invece di continuare l’esecuzione del programma che visualizzerà nel browser l’output di SimpleServlet.

Applicazione della tecnica di debugging in ambiente VisualAge per Java di IBM

Il precedente paragrafo esemplificava la tecnica di debugging delle servlet in un ambiente di programmazione minimale e gratuito, costituito solamente da JDK1.1.6 e da JSDK2.0. Tuttavia il semplicissimo principo che l’esecuzione della java virtual machine comincia sempre da una classe che contiene il metodo main(), ci consente di generalizzarne l’approccio in un qualsiasi ambiente commerciale di sviluppo di applicazioni java. A questo scopo abbiamo scelto VisualAge per Java di IBM (VAJ), in quanto il suo compilatore incrementale permette di ottenere significative economie nello sviluppo e nel debugging delle applicazioni java. Se durante una sessione di debugging in VAJ modifichiamo il codice sorgente, al momento del salvataggio delle modifiche, VAJ ricompila solamente unità minimali di codice senza bisogno di terminare e rilanciare l’applicazione ricompilata. In altre parole, VAJ permette di programmare nel debugger, ossia di modificare on-fly il codice che stiamo debuggando. Naturalmente questa caratteristica va usata con molta discrezione e solamente per correggere i bachi, altrimenti si corre il rischio di ottenere codice estremamente confuso, frammentato e privo di struttura/architettura. Personalmente ritengo essenziale che l’attività di codifica venga sempre preceduta da una fase, eventualmente informale, di progettazione, allo scopo di abilitare flessibilità, estendibilità e riuso, non solo del codice, ma soprattutto di quella che la gang of four (GoF) chiama microarchitettura. Non essendo questo un intervento sulla programmazione object-oriented in java, ma sul debugging delle servlet, useremo ancora una volta la semplicissima SimpleServlet della distribuzione Sun di JSDK2.0. 

Una volta lanciato VAJ, la prima cosa da fare è creare un progetto. All’interno del progetto dovrete importare tutte le classi della distribuzione di JSDK2.0, compresa la classe SimpleServlet che abbiamo intenzione di debuggare. VAJ consente di importare in un progetto sia codice compilato che codice sorgente. Visto che la distribuzione di JSDK2.0, oltre ai compilati, contiene anche i sorgenti dei package javax.servlet e javax.servlet.http, vi consiglio di importare i sorgenti. Viceversa, i sorgenti del mini web server non sono invece disponibili e dovrete quindi importare le classi compilate che si trovano nei package sun.servlet, sun.servlet.http e sun.servlet.util di jsdk.jar. Dopo aver importato nel medesimo progetto anche le classi di esempio delle servlet, tra le quali SimpleServlet, individuate nel package sun.servlet.http la solita classe HttpServer che implementa il mini web server e che contiene il metodo main(). Lanciatela con il comando Run di VAJ. Nella console di VAJ noterete il medesimo output ottenuto utilizzando la tecnica di debugging con il debugger di JDK1.1.6. La java virtual machine di VAJ si è trasformata in un mini web server, cui potete collegarvi con un qualsiasi browser. Prima di fare ciò, selezionate il solito metodo doGet() della classe SimpleServlet e inserite un breakpoint in corrispondenza della seguente linea di codice
 
String title = "Simple Servlet Output"; 

Ora provate a collegarvi con il browser al solito URL http://localhost:8080/servlet/SimpleServlet Il browser rimane in attesa dell’esecuzione della servlet, che si è interrotta a causa del breakpoint appena inserito. Potete verificarlo riportando il focus nella finestra di debugging di VAJ, di cui in Fig.2 e’ mostrato uno snapshot.

     

Fig. 2 Tecnica di debugging in VisualAge for Java 
     
Eseguite il comando step over del debugger di VAJ per un paio di volte. Cambiate il testo della variabile title, salvate il cambiamento e fate continuare l’esecuzione dell’applicazione. Notate che al momento del salvataggio, VAJ ricompila automaticamente solo il metodo doGet() della classe SimpleServlet. Inoltre, non è necessario interrompere l’esecuzione del thread della classe HttpServer per vedere visualizzato nel browser il nuovo testo che avete assegnato alla variabile title. Questo è solo un piccolo esempio dei vantaggi di programmare nel debugger di VAJ.
 

Generalizzazione della tecnica di debugging

     
Entrambe le precedenti esemplificazioni della tecnica di debugging delle servlet utilizzano il mini web server fornito nella distribuzione di JSDK2.0. Come già accennato, servletrunner è solo un ambiente dimostrativo delle potenzialità delle servlet e le sue limitazioni sono tali da renderlo poco utile nello sviluppo di applicazione web dinamiche. E’ quindi il caso di provare a generalizzare la tecnica di debugging in un ambiente di sviluppo, la cui configurazione di web server e servlet engine sia gemella rispetto a quella dell’ambiente di produzione. Tra le tante opzioni di configurazione abbiamo scelto quella potenzialmente più diffusa: sistema operativo NT4.0 (desktop o server), web server Microsoft Internet Information Server 4.0 (IIS4.0) e servlet engine JRun2.2Pro della Livesoftware Inc. Anche in questo caso utilizzaremo VAJ come IDE java di sviluppo.
 
Se non lo avete già fatto, installate IIS4.0 (eventualmente va bene anche la versione personal) e JRun. Verificate che le due installazioni funzionino correttamente. In particolare dovete verificare che funzioni il servizio delle servlet digitando nel browser i seguenti URL: http://localhost/servlet/SimpleServlet e http://localhost:8000/servlet/SimpleServlet. Fatto ciò ed utilizzando i comandi di amministrazione di JRun, fermate il servizio default di JRun. Questa operazione è necessaria perchè, come vedremo tra breve, dovremmo lanciare tale servizio dall’interno di VAJ per fare in modo che quando con il browser ci si collegherà ad uno dei due precedenti URL, l’esecuzione della servlet avvenga nella java virtual machine di VAJ e non in quella dell’installazione di JRun. A questo punto, mutatis mutandis, dobbiamo ripetere la procedura di importazione corrispondente a quella utilizzata per servletrunner. Lanciate VAJ e, se non lo avete già fatto, create un nuovo progetto e importate al suo interno, come nel precedente paragrafo, le classi delle estensioni standard di java per le servlet, ossia le classi dei package javax.servlet e javax.servlet.http, e le servlet dimostrative della distribuzione di JSDK2.0. Nel medesimo progetto dovrete importare le classi che implementano il servlet engine di JRun. Quest’ultima è un’operazione piuttosto laboriosa, ma con un pò di attenzione otterrete il risultato desiderato. Cominciate importando i sorgenti delle classi che si trovano nella directory classes\Acme dell’installazione di JRun. Espandete con WinZip il contenuto del file jrun.jar che si trova nella directory lib dell’installazione di JRun. Notate che questo jar file contiene anche i package javax.servlet e javax.servlet.http che avete già importato in precedenza. Selezionate in WinZip tutti i file che non appartengono a questi due package ed al package com.livesoftware.jrun.nsapi ed estraeteli in un directory temporanea.. Tornate nel workbench di VAJ, importate nel solito progetto l’intero contenuto della directory temporanea e disinteressatevi del problema di importazione segnalato da VAJ e relativo al classe ServerManagerControlConnection del package com.livesoftware.jrun.service. Con gli strumenti di ricerca di VAJ, cercate all’interno del progetto tutte le classi che contengono il metodo main(). La Fig.3 mostra i risultati della ricerca.

    Fig.3 – Risultato della ricerca del metodo main() all’interno del progetto Servlet Debugging
     
     

Se avevate già sperimentato la tecnica di debugging delle servlet con servletrunner, tra i risultati della ricerca troverete anche la classe HttpServer, altrimenti troverete solamente le classi FetchURL, ParsingUtil, ServiceManager e ServiceManagerControl. La classe ServiceManager è quella che ci interessa e che rappresenta il punto di ingresso da passare alla java virtual machine di VAJ per eseguire il debugging delle servlet nella configurazione di sviluppo scelta. Prima di passare alla sessione di debugging vera e propria, è necessario fare una precisazione. Se provate a lanciare con il comando Run di VAJ l’esecuzione della classe ServiceManager, vi accorgerete che il processo termina immediatamente segnalando nella console di VAJ che manca l’indicazione della radice del servizio di JRun. Per risolvere questo problema, dovete indicare al metodo main() della classe ServiceManager la directory del servizio in questione. Nel caso in cui abbiate eseguito una installazione standard di JRun, la directory che dovete indicare sarà simile alla seguente:
    "C:\Program Files\Live Software\JRunPro\2.2\jsm-default"
Fatto quest’ultimo passo, siamo pronti a provare la tecnica di debugging delle servlet. Assegnate come al solito un breakpoint all’interno del metodo doGet() della classe SimpleServlet. Selezionate la classe ServiceManager e lanciate il comando Run di VAJ. La console di VAJ vi informerà del caricamento dei servizi ausiliari (Servlet Engine Service, Connector Proxy Servevice e Web Server Service) associati al servizio jsm-default di JRun. Non ci rimane che provare il collegamento ai medesimi URL utilizzati in precedenza per testare l’installazione di IIS4.0: http://localhost/servlet/SimpleServlet e http://localhost:8000/servlet/SimpleServlet . Nel primo caso il browser rimarrà in attesa della risposta di IIS4.0, mentre nel secondo rimarrà in attesa della risposta del java web server di JRun. In entrambi i casi il motivo è il medesimo: l’esecuzione della servlet si è interrotta in corrispondenza del breakpoint associato al metodo doGet() di SimpleServlet. Come prima, provate a modificare il codice di questo metodo, a salvare le modifiche e a continuare l’esecuzione della servlet: l’output visualizzato nel browser sarà quello da voi modificato senza aver avuto bisogno di interrompere il processo, di ricompilare il codice e di rieseguire il processo. Un vero e proprio risparmio di tempo e denaro.

L’intera procedura può naturalmente essere applicata, mutatis mutandis, in qualsiasi configurazione di web server, servlet engine e ambiente di sviluppo. In particolare, utilizzando JRun è possibile testare la debugging delle servlet con tutti i web server supportati da JRun su piattafroma UNIX e Windows32.

Conclusione

Il debugging delle servlet java è attività molto più semplice di quanto non appaia di primo acchito. La tecnica illustrata in questo articolo è applicabile in qualsiasi configurazione di piattaforma, web server, servlet engine e ambiente di sviluppo e si basa su un principio semplicissimo: qualsiasi applicazione java deve avere una classe nella quale sia definito il metodo statico main(), che rappresenta il punto di ingresso dell’esecuzione o il debugging dell’applicazione stessa. Utilizzando VisualAge per Java come IDE di sviluppo è inoltre possibile risparmiare una considerevole quantità di tempo grazie al suo compilatore java incrementale. La tecnica di debugging delle servlet può essere generalizzata e applicata in un ambiente distribuito (web server e servlet engine e ambiente di sviluppo che risiedono su macchine distinte), e i principi su cui tale generalizzazione si basa potranno essere utilizzati per implementare un sistema di load-balancing delle servlet allo scopo di risolvere dinamicamente eventuali problemi di performance. Questi ultimi due argomenti verranno affrontati in un prossimo articolo.


 
 

MokaByte Web  1998 - www.mokabyte.it 
MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it