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:
Il successivo comando
lancia il debugger
java. Per indicare a jdb la classe che vogliamo "debuggare" utilizziamo
il comando
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
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.
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. |