MokaByte 69 - Dicembre 2002 
La JSP Standard Tag Library
III parte - JSP e Web User Interface
di
Lavinio Cerquetti
Utilizziamo il supporto XML di JSTL per estendere le funzionalitą della nostra Web Application NetView

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.
O
ra 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

 

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it