Introduzione
Negli articoli precedenti abbiamo presentato la JSP
Standard Tag Library [1] ed analizzato il nuovo ruolo
del framework JSP [2] all'interno di applicazioni Java
Enterprise.
Sulla base di tali conoscenze abbiamo quindi realizzato
una semplice Web Application, concentrando in particolare
la nostra attenzione sullo strato di interfaccia utente,
interamente realizzato in JSP / JSTL e senza alcun ricorso
agli ormai obsoleti scriptlet JSP.
In questo articolo svilupperemo ulteriormente la nostra
applicazione NetView arricchendola di funzionalità
basate sui componenti XML di JSTL.
Per la massima comprensione dei contenuti che andremo
a trattare si consiglia di fare frequente riferimento
sia agli articoli precedenti [3] e [4] sia all'applicazione
NetView, i cui sorgenti commentati sono liberamente
disponibili e scaricabili da [4].
Il
problema
Dopo un imprecisato numero di reiterazioni del consueto
ciclo test-debug-edit abbiamo finalmente rilasciato
la nostra applicazione NetView nella sua prima versione
definitiva e possiamo goderci un po' di meritato riposo,
cullandoci nella soddisfazione di aver messo in piedi
un sistema effettivamente in grado di migliorare la
vita dell'impresa committente, la famigerata ditta ACME.
Tutti sappiamo, però, che al mondo esiste una
sola cosa peggiore di un cliente insoddisfatto, vale
a dire un cliente soddisfatto.
In poche settimane cominciano quindi ad impilarsi sulla
nostra scrivania richieste di espansioni, miglioramenti
e nuove funzionalità per NetView.
Dopo le consuete sessioni di incontro con il cliente
viene stilata una lista di nuove funzionalità
da aggiungere alla nostra applicazione:
Tutti i dispositivi, tranne i router, inviano dei log
operativi ad un repository centrale legacy; gli operatori
che utilizzano NetView devono essere messi in grado
di analizzare e filtrare tali log;
la ditta ACME possiede già un sistema di monitoraggio
e controllo del traffico, il quale preleva i relativi
dati dai router aziendali e li consolida in totali giornalieri,
archiviandoli in un database relazionale; si richiede
a NetView di interfacciarsi a tale database - ovviamente
in sola lettura - in maniera da permettere agli operatori
di effettuare analisi elementari del traffico senza
essere costretti a lasciare l'applicazione;
l'iniziale successo di NetView ha convinto ACME a valutare
l'opportunità di ampliarne le funzionalità
e di promuoverne l'utilizzo in un gruppo scelto di filiali
estere; ciò comporta necessariamente l'internazionalizzazione
della nostra piccola applicazione, la quale deve essere
messa in grado di adattarsi automaticamente alla lingua
ed alle convenzioni di rappresentazione di numeri e
date di paesi diversi.
Si
noti che, almeno in una prima fase, tali funzionalità
aggiuntive vanno considerate sperimentali; ACME desidera
verificare sul campo l'efficacia di NetView in questi
nuovi contesti prima di decidersi ad ampliamenti strutturali
della nostra applicazione.
Analisi
del problema
In qualsiasi libro di teoria, magari dedicato allo sviluppo
di applicazioni distribuite, si consiglierebbe a questo
punto di riconsiderare globalmente l'applicazione alla
luce dei nuovi requisiti funzionali: introdurremmo quindi
nuove grandezze, quali sistemi di persistenza o strati
di EJB legati alla rappresentazione ed all'accesso ai
log dei dispositivi ed ai dati di traffico dei router,
alle luce dei quali potrebbe rivelarsi necessario apportare
modifiche strutturali all'architettura di NetView.
L'approccio che seguiremo in questa sede sarà
tuttavia diverso, e non solamente per ovvi motivi di
aderenza al tema della nostra serie di articoli.
Decideremo
infatti di mantenere l'architettura di NetView immutata
e di minimizzare le modifiche al codice Java: il nostro
fine è quello di implementare tutte le funzionalità
richieste lavorando quasi esclusivamente a livello di
interfaccia utente, ovvero di View JSP.
Tale
scelta, nel nostro caso, è chiaramente motivata
dal carattere sperimentale delle estensioni richieste,
che ci induce a ridurre al minimo i costi implementativi
delle nuove funzionalità: il carico di lavoro
derivante da una ristrutturazione generale dell'applicazione
non sarebbe giustificato qualora il cliente, effettuate
le sue valutazioni, decidesse di non procedere oltre
e di ritornare allo status quo dell'applicazione.
Si
noti che il criterio di scelta presentato può
venire applicato, con immutata legittimità, ad
una casistica di situazioni piuttosto ampia. Tempi di
sviluppo particolarmente stretti, scarsa disponibilità
di risorse umane di buon livello, budget limitati da
parte del committente possono in generale precludere
o sconsigliare il passaggio attraverso una nuova fase
globale di analisi, sintesi e codifica dell'applicazione.
In queste situazioni, che anche per via dell'attuale
sfavorevole congiuntura economica tendono a divenire
sempre più la norma nello sviluppo di applicazioni
distribuite per la piccola e media impresa, la ricerca
della soluzione teoricamente perfetta si dimostra nei
fatti semplicemente impraticabile.
Viceversa un approccio votato ad una maggiore semplicità,
anche al prezzo di alcune, inevitabili imperfezioni
permette di ridurre notevolmente i costi di sviluppo
velocizzando il rilascio delle soluzioni finali, con
ovvi vantaggi a breve e medio termine tanto per il committente
quanto per il fornitore.
Alla
luce di queste considerazioni decidiamo quindi di implementare
le nostre modifiche nello strato di interfaccia utente,
avvalendoci a questo fine della ricchezza semantica
di JSP e JSTL.
Effettuata questa scelta di fondo, approcciamo nel concreto
le richieste del cliente alla ricerca di possibili cammini
implementativi.
Per
quello che concerne i log operativi archiviati in un
repository legacy, il problema è dato dalle possibilità
di comunicazione con questo genere di ambienti, a volte
limitate e comunque legate al possesso di skill tecnici
specifici ed estranei alle nostre competenze; una soluzione
basata su JCA [5], il framework J2EE dedicato alle esigenze
di interazione di questa natura, non è percorribile
a causa della novità di tale API, non ancora
supportata dal produttore dell'ipotetico repository
in questione.
Adotteremo in questa sede una classica soluzione a due
stadi ed in tempo non reale, ipotizzando che il personale
IT di ACME abbia provveduto a realizzare dietro nostra
richiesta una procedura batch di esportazione dei dati,
che vengono resi disponibili alla nostra applicazione
ad intervalli fissi ed in formato XML, secondo un DTD
di interscambio da noi appositamente realizzato. Il
compito di NetView consisterà quindi nell'accedere
e filtrare i contenuti di tale archivio XML secondo
le richieste dell'utente.
In considerazione della nostra scelta di fondo decidiamo
di utilizzare a tale fine il componente XML della libreria
JSTL, centralizzando quindi le modifiche a livello di
JSP e minimizzando l'impatto sul codice Java di NetView,
al cui livello non vogliamo in alcun modo modellare
queste nuove entità.
La
questione della realizzazione di funzioni di reportistica
basate sul database del sistema di controllo e monitoraggio
del traffico trova in ambiente J2EE molteplici soluzioni
possibili, basate sulla vasta gamma disponibile di protocolli
di accesso ai dati, quali EJB, JDO e JDBC.
Ognuna di queste tecnologie richiede tuttavia interventi
significativi alla Java codebase di NetView, interventi
che abbiamo escluso a priori: la nostra sarà
quindi una soluzione interamente JSTL, basata sul solo
utilizzo dei componenti per l'accesso a database relazionali
della JSP Standard Tag Library, e tutte le funzionalità
di interfacciamento ai dati saranno implementate unicamente
a livello di View JSP.
Infine
risolveremo la terza ed ultima questione, relativa all'internazionalizzazione
di NetView - che realizzeremo a titolo esemplificativo
per un'unica View - sfruttando il componente di internazionalizzazione
di JSTL, il cui fine è proprio quello di isolare
l'interfaccia utente dalle specificità geografiche
dei contesti di fruizione.
Ancora una volta tali modifiche si ripercuoteranno in
misura assolutamente irrilevante sulla Java codebase
della nostra applicazione e verranno centralizzate nello
strato di interfaccia utente.
Funzionalità
XML di JSTL
Le funzionalità XML di JSTL - nello spirito di
questa Tag Library - sono caratterizzate da una estrema
semplicità e linearità, ed il loro apprendimento
viene grandemente semplificato dalla stretta analogia
dei tag XML con quelli, a noi già noti, del componente
Core di JSTL.
La
volontà di utilizzare i componenti XML di JSTL
all'interno di una Web Application va innanzitutto specificata
a livello di deployment descriptor:
<taglib>
<taglib-uri>http://java.sun.com/jstl/xml</taglib-uri>
<taglib-location>/WEB-INF/x.tld</taglib-location>
</taglib>
La
Tag Library XML di JSTL va quindi dichiarata in ogni
pagina JSP da cui si intenda far uso di tali funzionalità:
<%@
taglib prefix="x" uri="http://java.sun.com/jstl/xml"
%>
Le
possibilità di ricerca ed estrazione di informazioni
da documenti XML vengono implementate nella JSP Standard
Tag Library tramite espressioni in linguaggio XPath
[6].
Tale linguaggio, sviluppato e standardizzato in seno
al World Wide Web Consortium (W3C [7]) offre possibilità
di selezione ed analisi di informazioni in formato XML
estremamente vaste e sofisticate, che noi non utilizzeremo
se non in minima parte.
È interessante notare come l'expression language
di volta in volta utilizzato sotto JSTL - nei nostri
esempi sempre EL, ma quanto detto vale anche per RT
- risulti perfettamente integrato con XPath, dall'interno
del quale è possibile fare riferimento a variabili
ed oggetti definiti in contesto JSTL. Espressioni XPath
vengono accettate dai tag XML di JSP Standard Tag Library
nell'attributo select.
La libreria XML di JSTL non supporta né DTD né
XML Schema e non effettua, né in fase di parsing
né in fase di applicazione di stylesheet XSLT,
alcuna validazione di documenti XML.
La
gestione di informazioni XML viene implementata dai
seguenti tag:
- Il
tag <x:parse> ha il compito di effettuare il
parsing di un documento XML, rendendone visibili i
contenuti alla pagina JSP; tale documento può
essere fornito sotto forma di variabile String - tipicamente
valorizzata da una sorgente esterna mediante un precedente
tag <c:import> - o sotto forma di Reader predisposto
dallo strato superiore della nostra applicazione Java
secondo i ruoli FrontController, Dispatcher, Helper,
Business Delegate, etc.); per evitare i problemi di
performance legati all'interpretazione di documenti
XML complessi è possibile specificare un filtro
in formato org.xml.sax.SAXFilter che verrà
applicato alla sorgente XML precedentemente alla vera
e propria fase di parsing;
- <x:out>:
esattamente analogo al tag <c:out>, visualizza
il valore di un'espressione specificata in linguaggio
XPath nell'attributo select;
- <x:set>:
analogo al tag <c:set>, assegna ad una variabile
il valore di un'espressione Xpath;
- <x:if>,
<x:choose>, <x:when>, <x:otherwise>:
corrispondono agli omonimi tag del componente Core
di JSTL, ma operano su espressioni XPath;
- <x:forEach>:
itera su di un'espressione XPath, ritornando uno ad
uno tutti i nodi di un documento XML selezionati da
tale espressione;
- Il
tag <x:transform> permette di applicare uno
stylesheet XSLT ad un documento XML, in maniera eventualmente
parametrica e visualizzando ovvero assegnando i risultati
ad una variabile JSTL.
Implementazione
delle funzionalità di analisi dei log
Entriamo nel vivo dell'analisi dei sorgenti partendo
dall'implementazione della visualizzazione dei log prodotti
dai diversi dispositivi.
Le modifiche al codice Java sono di scarsa rilevanza
e consistono unicamente nella definizione di un nuovo
Dispatcher associato all'azione querylogs e in una modifica
alla classe FrontControllerServlet affinché essa
inserisca nel contesto di request, prima di invocare
il Dispatcher, un riferimento all'oggetto ServletConfig
corrente.
Il nuovo Dispatcher QueryLogsDispatcher, sfruttando
tale riferimento, si preoccupa di predisporre un Reader
verso il file XML contenente i log, archiviati in un
formato di questo genere:
<log>
<entry producer="3" area="authentication"
level="info">
<timestamp>
Mon Nov 2 15:18:43 CET 2002
</timestamp>
<message>
Utente simone, login effettuato
</message>
</entry>
.
.
</log>
Il
file XML in questione - logData.xml - si trova nella
directory WEB-INF, in maniera da risultare nascosto
ai browser.
Passiamo
ora all'analisi della View querylogs.jsp: la prima sezione
del file si occupa semplicemente di visualizzare gli
eventuali parametri di ricerca impostati dall'utente:
.
.
<c:choose>
<c:when test="${empty logLevel}">
<c:set var="logLevel" value=""
/>
(Nessun criterio)
</c:when>
<c:otherwise>
<c:out value="${logLevel}" />
</c:otherwise>
</c:choose>
.
.
Successivamente
viene effettuato il parsing del documento XML a partire
dalla sorgente dati - nel nostro caso un Reader - impostata
dal Dispatcher QueryLogsDispatcher:
<x:parse
xml="${logData}" var="parsedLog"
scope="request" />
L'aver
optato per un'interazione basata su Reader è
dovuto unicamente a motivi didattici; nello spirito
della nostra scelta iniziale sarebbe stato perfettamente
possibile, e legittimo, rimuovere del tutto tale codice
di gestione dal Dispatcher e da FrontControllerServlet
- relativamente alla memorizzazione dell'istanza di
ServletConfig nella request corrente - e leggere direttamente
il documento XML dal file logData.xml per poi effettuarne
il parsing:
<c:import
url="WEB-INF/logData.xml" var="logData"
scope="request" />
<x:parse xml="${logData}" var="parsedLog"
scope="request" />
Si
noti come le variabili definite in contesto Core JSTL
risultino trasparentemente utilizzabili all'interno
di tag XML JSTL.
Ora
che i dati in formato XML sono disponibili in forma
normalizzata nella variabile parsedLog è possibile
provvedere all'estrazione delle righe di log. La logica
consiste semplicemente nell'estrarre tutti i tag <entry>
e nel visualizzare solamente quelli che soddisfano i
criteri di ricerca impostati dall'utente:
<x:forEach
select="$parsedLog//entry" var="entry">
<x:if select="($queryNodeId=-1 or $entry/@producer=$queryNodeId)
and
($area='' or $entry/@area=$area) and
($logLevel='' or $entry/@level=$logLevel)" >
<!-- Estraiamo il valore dell'attributo producer
in una variabile JSTL -->
<x:set select="string($entry/@producer)"
var="producerId" />
<tr>
<td>
<a href="/netview/main?action=nodeform&subaction=edit&id=<c:out
value='${producerId}' />">
<c:out value="${producerId}" />
</a>
</td>
<td><x:out select="$entry/@area"
/></td>
<td><x:out select="$entry/@level"
/></td>
<td><x:out select="$entry/timestamp"
/></td>
<td><x:out select="$entry/message"
/></td>
</tr>
</x:if>
</x:forEach>
Il
senso del codice proposto risulta immediatamente chiaro
grazie alla stretta analogia dei tag XML (x:forEach,
x:if, x:set e x:out) con i corrispondenti tag Core.
Le espressioni XPath utilizzate hanno il seguente significato:
- "$parsedLog//entry"
corrisponde ad ogni tag <entry> all'interno
del documento contenuto nella variabile parsedLog,
a qualsiasi livello di profondità;
- il
valore di "$entry/@producer" è semplicemente
il valore dell'attributo producer del tag XML contenuto
nella variabile entry; tale variabile, specificata
come iteratore nel tag forEach, contiene di volta
in volta il contenuto del tag <entry> corrente;
similmente le espressioni "$entry/@area"
e "$entry/@level" valgono rispettivamente
il valore dell'attributo area e level;
- il
valore delle espressioni "$entry/timestamp"
e "$entry/message" corrisponde al contenuto
dei tag <timestamp> e <message> figli
del tag contenuto nella variabile entry.
L'espressione
XPath "($queryNodeId=-1 or $entry/@producer=$queryNodeId)
and ($area='' or $entry/@area=$area) and ($logLevel=''
or $entry/@level=$logLevel)" assume il valore true
se le seguenti tre condizioni sono soddisfatte:
-
l'utente non ha specificato alcun nodo tra i parametri
di ricerca (parametro queryNodeId) o il nodo della
voce di log corrente corrisponde al nodo richiesto;
- l'utente
non ha specificato alcun area di log tra i parametri
di ricerca o l'area del nodo di log corrente corrisponde
a quella richiesta;
- l'utente
non ha specificato alcun livello di log tra i parametri
di ricerca o il livello del nodo di log corrente corrisponde
a quello richiesto.
Il
tag <x:set select="string($entry/@producer)"
var="producerId" /> viene utilizzato per
assegnare il valore dell'attributo producer alla variabile
JSTL producerId; tale valore viene considerato come
stringa, vale a dire senza alcuna interpretazione da
parte dell'engine XPath. Da questo momento in avanti
la variabile producerId è manipolabile attraverso
i normali tag Core, come il tag <c:out> utilizzato
subito dopo per inviarne il valore in output.
Infine
la View querylogs.jsp disegna a piè di pagina
il form che permette all'utente di lanciare una nuova
ricerca; il relativo codice JSP non presenta particolari
di interesse, se non per il fatto che la list box che
permette all'utente di selezionare un nodo viene costruita
priva dei nodi di tipo router - categoria 1 - che per
definizione non generano log in formato XML:
<select
name="nodeId">
<option value="-1" selected="selected">
(Nessun criterio)
</option>
<c:forEach var="nodeIterator"
items="${informationSystem.allNodes}">
<c:if test="${nodeIterator.catId!=1}">
<option value="<c:out value='${nodeIterator.id}'/>">
<c:out value="${nodeIterator.id}" />
(<c:out value="${nodeIterator.desc}" />)
</option>
</c:if>
</c:forEach>
</select>
Conclusioni
Nel presente articolo abbiamo analizzato le possibilità
offerte dalla libreria XML di JSP Standard Tag Library,
utilizzandola in concreto per estendere le funzionalità
di NetView.
Nel prossimo articolo sfrutteremo i restanti componenti
di JSTL per proseguire nell'evoluzione della nostra
Web Application, interfacciandola a database relazionali
SQL e provvedendo alla sua internazionalizzazione.
Bibliografia
[1] JSTL - JSP Standard Tag Library: http://java.sun.com/products/jsp/jstl
[2] JSP - JavaServer Pages: http://java.sun.com/products/jsp
[3] Lavinio Cerquetti: "JSP 1.2: La JSP Standard
Tag Library - I parte", Mokabyte N. 67 - Ottobre
2002
[4] Lavinio Cerquetti: "JSP 1.2: La JSP Standard
Tag Library - II parte", Mokabyte N. 68 - Novembre
2002
[5] JCA - J2EE Connector Architecture: http://java.sun.com/j2ee/connector
[6] XPath: http://www.w3.org/TR/xpath
[7] W3C - World Wide Web Consortium: http://www.w3.org
Risorse
Scarica
qui il codice presentato
nell'articolo (NetView versione 1.1).
L'implementazione di esempio di NetView è stata
testata sotto JBoss 3.0.3 e Tomcat 5 (Milestone 5.0.0)
in ambiente Linux con database IBM DB2, e comprende
le funzionalità SQL e di internazionalizzazione
che verranno presentate nel prossimo articolo.
Lavinio
Cerquetti si occupa di design e sviluppo del software
in ambienti distribuiti ed in architetture J2EE multi-tier.
Può essere contattato all'indirizzo di e-mail
lavinio.cerquetti@netixitalia.it
|