MokaByte 77 - 7mbre 2003 
JSP e Web User Interface
Custom Tag Library in ambienti J2EE
V parte
di
Lavinio Cerquetti
Un esempio pratico: design ed integrazione di una Custom Tag Library

Introduzione
Con questo numero inizia l'ultima parte della lunga serie di articoli intitolata "JSP e Web User Interface", nel corso della quale abbiamo rivolto la nostra attenzione ad alcune fondamentali tecnologie legate alla realizzazione dello strato di interfaccia utente di applicazioni Web: la JSP Standard Tag Library (JSTL), analizzata nei numeri [1] - [4], Java Server Faces ([5]-[6]) ed infine le Custom Tag Library (CTL), trattate in [7]-[10].
In particolare, dopo aver analizzato in [7] e [8] i principali elementi strutturali di una CTL, in [9] gli schemi di gestione di attributi, variabili di scripting e tag nesting, ed in [10] i meccanismi di gestione di eccezioni ed eventi, intendiamo nel presente e nel successivo articolo applicare le conoscenze così acquisite al design, all'integrazione ed all'implementazione di una Custom Tag Library rivolta ad un'estensione operativa della nostra applicazione d'esempio NetView.

Per la massima comprensione dei contenuti che andremo a trattare si consiglia di fare frequente riferimento all'applicazione NetView, i cui sorgenti commentati sono liberamente disponibili e scaricabili dalla sezione Risorse del presente articolo.

 

Il problema
Come sicuramente ricorderete, NetView è stata realizzata dietro richiesta della famigerata multinazionale ACME per venire incontro alle sue esigenze di gestione remota e web-based del proprio parco hardware e software.
La buona prova che il team di sviluppo di NetView ha dato di sé sembra avere stimolato ACME, che ci commissiona uno studio di fattibilità - con successiva implementazione - concernente un significativo ampliamento funzionale dell'applicativo.
Viene richiesta l'implementazione in NetView di una feature di accesso ed interrogazione diretta dei singoli dispositivi monitorati, al fine di fornire all'utente le principali impostazioni di configurazioni e statistiche di attività di ogni nodo, aggiornate in tempo reale. In un futuro non immediato tale sottosistema software sarebbe presumibilmente destinato a venire esteso, affiancando alle possibilità di query analoghe funzionalità di configurazione dei dispositivi sotto controllo.

 

Analisi del problema
Risulta immediatamente evidente la complessità della proposta di ACME: i diversi nodi monitorati sono infatti raggiungibili, interrogabili e gestibili tramite un ampio spettro di protocolli, diversi per struttura e caratteristiche di base.
Volendo fare qualche esempio, NetView dovrebbe essere in grado di utilizzare il protocollo SNMP per accedere ad un router, mentre la gestione di un database server avverrebbe tramite un apposito set di comandi SQL o simil-SQL inviati in contesto JDBC; ancora diverse sarebbero le metodologie di amministrazione di server di rete, application server e directory server.
Tuttavia il team di sviluppo di NetView, le cui core competence sono indirizzate allo sviluppo di soluzioni Java in ambito distribuito, non possiede una così vasta ed approfondita esperienza di protocolli e problematiche sistemistiche, e si trova quindi nell'impossibilità di fatto di venire incontro alle esigenze del cliente.
Tale insieme di conoscenze è, d'altronde, proprio del settore IT della stessa ACME, il quale per impostazione professionale è in possesso sia degli skill teorici sia della necessaria esperienza sul campo, trovandosi ogni giorno a dover gestire tale parco hardware e software.
Una ottimale soluzione alle richieste del cliente può quindi, nel nostro esempio, risultare solamente dallo sfruttamento sinergico delle conoscenze dei due diversi gruppi di lavoro: al reparto di IT di ACME, in possesso delle necessarie competenze, dovrebbe venire demandata l'implementazione dei meccanismi di comunicazione e gestione dei dispositivi, la cui integrazione all'interno dell'applicativo - tanto dal punto di vista funzionale quando di interfaccia utente - spetterebbe al team di sviluppo di NetView.
Scendendo nel dettaglio, apposite sessioni di analisi - estese a rappresentanti del settore IT di ACME - individuano in una Custom Tag Library la soluzione ideale al problema. In particolare risultano decisive le seguenti considerazioni:

L'utilizzo di una CTL permette ai due gruppi di lavoro di mantenere la massima autonomia e consente loro di focalizzarsi unicamente su un aspetto del problema, valorizzando ed esaltando i propri skillset;

la separazione delle attività dei due gruppi di lavoro consente la difesa e la salvaguardia dei rispettivi patrimoni di conoscenze;

il contesto di API nel quale sono inquadrate le Custom Tag Library è caratterizzato da una notevole semplicità. In particolare non si richiede all'autore di una CTL di comprendere e padroneggiare concetti avanzati di programmazione parallela o distribuita, né viene richiesta una conoscenza approfondita della struttura di J2EE e delle sue API più caratterizzanti, quali EJB, JMS e JCA;

alla luce della precedente considerazione lo sviluppo di CTL può venire intrapreso con successo anche da gruppi di lavoro in possesso di una conoscenza moderata del linguaggio Java; di più, se necessario, gli opportuni skillset possono venire trasmessi in numero limitato di ore-uomo;

le Custom Tag Library così realizzate e distribuite in archivi JAR indipendenti rappresentano a tutti gli effetti dei componenti completamente scissi dalle particolarità dell'applicazione al cui interno vengono utilizzate e, se basate su di un design sufficientemente curato, risultano utilizzabili con efficacia e semplicità all'interno di nuovi progetti.

Decisa quindi la divisione di compiti tra il reparto IT di ACME ed il team di sviluppo di NetView per quanto riguarda, rispettivamente, l'implementazione della Custom Tag Library e la sua integrazione all'interno dell'applicativo, i due gruppi di lavoro passano alla fase di messa a punto congiunta del design della CTL.

 

Design della Custom Tag Library
Alla luce della volontà comune di separare il layer di comunicazione verso i dispositivi da quello di gestione dello strato di presentazione, si decide - secondo le linee da noi espresse in [7] - di liberare completamente il reparto IT da ogni problematica di interfaccia utente.
Tale obiettivo viene raggiunto già in fase di design, stabilendo che i tag non genereranno alcun output, ma utilizzeranno quale unico strumento di comunicazione con la vista JSP delle variabili di scripting, trattate in [9] e tramite le quali gli sviluppatori NetView saranno in grado di utilizzare i contenuti generati dalla CTL con la massima libertà ed in funzione delle necessità applicative.
Si noti che, in virtù del fine formativo di questo articolo, ammetteremo una deroga a tale norma limitatamente al solo tag <sendCommand>, come avremo modo di discutere in seguito.
La Custom Tag Library che implementa i necessari comportamenti di query dei dispositivi remoti - denominata nvtags - è basata su di un numero ristretto di tag, che andiamo ora ad esaminare.

Il tag fondamentale è <nodeInfo>, tramite cui una pagina JSP stabilisce una connessione ad un dispositivo e ne ottiene delle informazioni di base. Il dispositivo viene identificato attraverso i due attributi obbligatori nodeId e catId, contenenti il primo il codice del nodo ed il secondo il suo codice di categoria.
Il tag <nodeInfo>, facendo uso del protocollo più adatto - i cui dettagli tecnici risultano comunque completamente invisibili a NetView - provvede a contattare il dispositivo ed a ritornarne a JSP in tempo reale la configurazione sotto forma di variabili di scripting.
Nella nostra limitata implementazione di esempio ci limiteremo a due variabili di scripting: la stringa productName, contenente il nome completo del prodotto, ed una variabile di tipo java.util.Date denominata lastBoot, la quale specifica la data di ultimo boot del nodo.
Al fine di semplificare l'utilizzo della Custom Tag Library si decide di avvalersi delle funzionalità di tag nesting supportate da JSP e da noi analizzate in [9] per definire il concetto di nodo corrente. L'utilizzo di un tag <nodeInfo> renderà quindi corrente il nodo specificato attraverso gli attributi nodeId e catId; i successivi tag di gestione, innestati all'interno del tag <nodeInfo>, faranno implicitamente riferimento al medesimo dispositivo, senza che sia necessario dichiararne nuovamente codice e categoria.
Si noti che gli attributi nodeId e catId verranno valorizzati solamente a run-time, in base al nodo selezionato in maniera dinamica dall'utente; come vedremo in seguito, questa funzionalità viene attivata a livello di Tag Library Descriptor impostando l'attributo <rtexprvalue> a true.
Data la sua semplicità <nodeInfo> può venire implementato come un tag base, rispondente all'interfaccia javax.servlet.jsp.Tag.

Il secondo tag definito in nvtags è <interfaceIterator>, il quale espone alla vista JSP informazioni concernenti le interfacce di rete di un nodo di tipo router o network server. Il nome del tag ne tradisce già un'importante caratteristica: disponendo in linea di principio un nodo di un numero a piacere di interfacce di rete, <interfaceIterator> dovrà essere in grado di ritornare alla pagina JSP un insieme strutturato di dati. Tale requisito viene rispettato implementando il tag <interfaceIterator> come tag iterativo (interfaccia javax.servlet.jsp.IterationTag).
Ad ogni iterazione verranno resi disponibili alla vista JSP sotto forma di variabili di scripting i dati di configurazione di una particolare interfaccia di rete e l'iterazione proseguirà sino all'avvenuta enumerazione di tutte le interfacce.
Secondo quanto stabilito in precedenza <interfaceIterator> viene sempre innestato all'interno di un tag <nodeInfo>, dal quale ottiene i dati del dispositivo su cui operare.
L'implementazione d'esempio del tag <interfaceIterator> conosce due variabili di scripting: ipAddress e snMask, rappresentanti rispettivamente l'indirizzo IP e la subnet mask di un'interfaccia.

Il terzo ed ultimo tag implementato dalla Custom Tag Library nvtags è <sendCommand>, il quale implementa una feature di utilizzo generale, consistente nell'invio ad un dispositivo - tramite il protocollo più confacente - di un comando a piacere, specificato come corpo del tag. Dovendo il tag <sendCommand> operare sul proprio contenuto, esso viene implementato come content tag, secondo gli schemi dell'interfaccia javax.servlet.jsp.BodyTag.
Esattamente come nel caso di <interfaceIterator> anche <sendCommand> non possiede attributi propri nodeId e catId, ma va sempre utilizzato in congiunzione ad un tag <nodeInfo>, al cui interno viene innestato.
Si noti che nella nostra implementazione, a fini d'esempio, <sendCommand> non comunica con la pagina JSP tramite variabili di scripting: esso è infatti l'unico tag della nostra CTL a generare output, inviando direttamente al motore Java Server Pages i risultati di esecuzione del comando.

Per tutte le variabili di scripting definite dai suddetti tag si decide di far uso dello scope NESTED, il cui utilizzo viene generalmente raccomandato; esso limita la visibilità di tali variabili all'interno del body dei rispettivi tag e minimizza il rischio di conflitti con altri tag o Custom Tag Library JSP.

Ultimato il design della CTL nvtags, passiamo ad analizzarne gli aspetti pratici. Seguendo una tradizionale logica top-down astrarremo la sua esistenza e ci concentreremo ora sulle sue modalità di utilizzo dall'interno di una web application, rimandando al prossimo articolo la discussione della sua implementazione.

Application Developer: utilizzo di una Custom Tag Library
Nel processo di ampliamento ed integrazione dell'applicativo oggetto del presente articolo, il ruolo svolto dal team di sviluppo di NetView coincide con quello di Application Developer identificato a livello di specifiche J2EE: in sostanza gli autori di NetView si configurano come utenti della Custom Tag Library nvtags.

Avendo i due gruppi di lavoro in sede di design - ed in linea con quanto da noi consigliato in [8] - optato per la distribuzione della CTL sotto forma di archivio JAR autonomo, la sua integrazione in NetView a livello architetturale è elementare, e si limita all'installazione dell'archivio JAR all'interno della struttura di directory dell'applicativo, tipicamente nella directory WEB-INF/lib. A questo punto sarà sufficiente dichiarare tale libreria in tutte le viste JSP che intendano farne uso.
Si noti, in particolare, che non è richiesto alcun intervento a livello di deployment descriptor (file web.xml) il quale rimane totalmente inalterato.

L'implementazione in NetView delle modifiche richieste si basa sostanzialmente sulla definizione della nuova vista JSP getinfo.jsp, la quale - come vedremo nella prossima sezione - provvede all'estrazione ed alla presentazione dei dati di configurazione di un dispositivo. La struttura della nostra applicazione rende quindi banale l'integrazione di tale pagina all'interno dello scheletro generale di NetView, per cui è sufficiente definire una nuova azione getinfo a livello di FrontControllerServlet ed implementare il dispatcher GetInfoDispatcher, il quale invoca la view getinfo.jsp fornendole come attributi di richiesta HTTP i seguenti parametri, tutti di tipo String:

A livello di interfaccia desideriamo collegare la vista getinfo.jsp al resto dell'applicazione a partire dalla view nodes.jsp, la quale fornisce all'utente un elenco di nodi. Implementiamo quindi, accanto ai preesistenti link di modifica e cancellazione, un nuovo link informativo, attraverso il quale l'utente potrà attivare la pagina getinfo.jsp per ottenere dati di configurazione in tempo reale su di un nodo a sua scelta.
Questa funzionalità si concretizza, in nodes.jsp, nella semplice aggiunta di un elemento href alla tabella che provvede a visualizzare i dispositivi in base alla selezione corrente, avendo l'accortezza di passare a getinfo.jsp, ovvero al suo dispatcher, il codice del nodo da esaminare nel parametro id:

<td>
<a href="/netview/main?action=getinfo&id=<c:out
value='${node.id}' />" title="Informazioni">
<img src="Information24.gif" border="0"
alt="Informazioni" /></a\>
</td>

La vista JSP getinfo.jsp: integrazione di Custom Tag Library
Procediamo ora all'analisi della vista JSP getinfo.jsp, la quale sfrutta la CTL nvtags per implementare le seguenti funzionalità:

Visualizzazione dei dati generali di configurazione di un nodo productName e lastBoot, descritti in sede di analisi ed ottenuti per via del tag <nodeInfo>;
per i soli nodi di tipo router e network server, enumerazione delle interfacce di rete e visualizzazione delle relative impostazioni grazie al tag <interfaceIterator>;
per i nodi di tipo network server elenco degli utenti attualmente collegati, ottenuto attraverso il tag <sendCommand>;
per i nodi di tipo database server lista dei database attivi, anch'essa risultante dall'utilizzo del tag <sendCommand>.

In apertura viene dichiarato l'utilizzo della libreria JSP nvtags:

<%@ taglib prefix="nvtags" uri="/WEB-INF/lib/nvtags.jar" %>

L'attributo uri punta all'archivio JAR contenente la Custom Tag Library, il cui percorso viene specificato a partire dalla root directory dell'applicazione web.
Come prefisso può, al solito, venire utilizzata una stringa a piacere. Nel nostro caso rispettiamo la convenzione secondo la quale il prefisso collima con l'attributo <short-name> dichiarato nel Tag Library Descriptor della CTL, che avremo modo di esaminare nel prossimo numero.

Seguono quindi le usuali definizioni di testata, l'inclusione di topbar.jsp e la visualizzazione dei dati del nodo corrente; per quest'ultima funzionalità vengono impiegati i parametri di request di getinfo.jsp:

<tr>
<td width="150">Nodo:</td>
<td width="300"><b><c:out value="${id}" /> (<c:out value="${desc}" />)</b></td>
</tr>

<tr>
<td width="150">Categoria:</td>
<td width="300"><b><c:out value="${catId}" /> (<c:out value="${catDesc}" />)</b></td>
</tr>

Rendiamo poi disponibili a livello di JSP sotto forma di variabili di scripting i parametri di request id e catId, che dovremo fornire al tag <nodeInfo>:

<jsp:useBean id="id" scope="request" class="String"/>
<jsp:useBean id="catId" scope="request" class="String" />

Facciamo uso del tag <nodeInfo> per ricevere nelle variabili di scripting productName e lastBoot le informazioni di configurazione del nodo specificato, che provvediamo quindi a visualizzare.
Si notino in particolare sia l'utilizzo degli attributi nodeId e catId, valorizzati dinamicamente al valore delle variabili di scripting id e catId - corrispondenti agli omonimi parametri di request in virtù dei precedenti tag <useBean> - sia il fatto che il tag <nodeInfo> venga volutamente lasciato aperto. Sarà infatti al suo interno, in modalità innestata, che utilizzeremo i tag <interfaceIterator> e <sendCommand> per estrarre ulteriori informazioni dal dispositivo.

Avendo in sede di design stabilito di utilizzare lo scope NESTED per le nostre variabili di scripting, la view getinfo.jsp si preoccupa di impiegarle solamente all'interno dei rispettivi tag.

<nvtags:nodeInfo nodeId='<%=id%>' catId='<%=catId%>' >
<tr>
<td width="150">Nome prodotto:</td>
<td width="300"><b><%=productName%></b></td>
</tr>

<tr>
<td width="150">Data ed ora di boot:</td>
<td width="300"><b><%=lastBoot%></b></td>
</tr>

Successivamente, se abbiamo a che fare con un router o un network server - corrispondenti rispettivamente alle categorie 1 e 2 - ne visualizziamo la configurazione delle interfacce di rete nelle variabili di scripting ipAddress e snMask grazie al tag iterativo <interfaceIterator>. Si noti come sintassi e semantica di questo tag ricordino da vicino quelle del tag <for> di JSTL: l'utilizzo ripetuto dei medesimi idiomi, che nel tempo diventano familiari agli sviluppatori, è uno dei punti di forza del paradigma JSP.

Come stabilito in fase di design il tag <interfaceIterator> si avvale delle funzionalità di tag nesting di JSP: innestato all'interno di un tag <nodeInfo> ne eredita automaticamente gli attributi nodeId e catId, che non vanno così ripetuti.

Si noti infine che il test di uguaglianza sul parametro catId viene eseguito in modalità alfanumerica, venendo quest'ultimo passato alla vista JSP sotto forma di stringa.

<c:choose>
<c:when test="${catId=='1' or catId=='2'}">
<tr>
<td colspan="2" width="450">
Interfacce di rete:
</td>
</tr>

<nvtags:interfaceIterator>
<tr>
<td width="150" align="center">
<%=ipAddress%>
</td>
<td width="150" align="center">
<%=snMask%>
</td>
</tr>
</nvtags:interfaceIterator>
</c:when>
</c:choose>

Provvediamo quindi ad estrarre ulteriori informazioni dal dispositivo: se abbiamo a che fare con un network server (categoria 2) vogliamo visualizzare l'elenco degli utenti collegati, funzionalità che otteniamo utilizzando il tag <sendCommand> per inviare al server remoto un comando "who" e presentarne il risultato all'utente. Si noti che la Custom Tag Library nvtags ci permette di astrarre completamente dalle modalità fisiche di invio di tale comando, che nel nostro caso potrebbe nella realtà delle cose avvenire tramite ssh, rlogin o telnet.

Come abbiamo già fatto notare, a fini esemplificativi l'implementazione proposta del tag <sendCommand> non comunica alla vista JSP il risultato del comando tramite un'apposita variabile di scripting, ma provvede in prima persona all'invio dell'output all'engine Java Server Pages, che racchiudiamo in un tag <pre> onde evitare problemi di formattazione.

<c:choose>
<c:when test="${catId=='2'}">
<tr>
<td colspan="2" width="450">
Utenti collegati:
</td>
</tr>

<tr>
<td colspan="2" width="450">
<pre>
<nvtags:sendCommand>
who
</nvtags:sendCommand>
</pre>
</td>
</tr>
</c:when>

In maniera analoga qualora il nodo selezionato sia un database server (categoria 4) utilizziamo il tag <sendCommand> per inviare al nodo il comando fittizio "LIST DABASES", a fronte del quale viene generato in output l'elenco dei database attivi.

<c:when test="${catId=='4'}">
<tr>
<td colspan="2" width="450">
Database attivi:
</td>
</tr>

<tr>
<td colspan="2" width="450">
<pre>
<nvtags:sendCommand>
LIST DATABASES
</nvtags:sendCommand>
</pre>
</td>
</tr>
</c:when>
</c:choose>

Avendo completato la presentazione di tutte le informazioni richieste possiamo considerare concluso il processing del dispositivo e provvediamo quindi a chiudere il tag <nodeInfo> e con esso il contesto del nodo corrente.

</nvtags:nodeInfo>

 

Conclusioni
Terminata lo scorso mese la trattazione teorica delle JSP API concernenti le Custom Tag Library abbiamo iniziato nel presente articolo a mettere in pratica le conoscenze acquisite, dedicandoci al design, integrazione ed utilizzo di una CTL rivolta ad un'estensione funzionale della nostra applicazione d'esempio NetView.
In particolare abbiamo in questo numero voluto concentrare la nostra attenzione sulle fasi di progettazione ed integrazione, analizzando le motivazioni e le scelte operate in fase di design e le modalità di utilizzo di una nuova Custom Tag Library all'interno di una web application preesistente, mettendo in luce la chiarezza e l'intuitività dei passi richiesti, così come la loro confortante assonanza con sintassi ed idiomi a noi noti e consueti nel mondo JSTL.
Nel prossimo numero - con il quale giunge al termine la presente serie di articoli "JSP e Web User Interface" - concluderemo l'esempio pratico qui iniziato, soffermandoci sull'attività pratica di codifica di una Custom Tag Library, la quale nel paradigma J2EE caratterizza il ruolo del Tool Developer.

 

Bibliografia e riferimenti
[1] Lavinio Cerquetti: "JSP 1.2: JSTL - I parte", Mokabyte N. 67 - Ottobre 2002
[2] Lavinio Cerquetti: "JSP 1.2: JSTL - II parte", Mokabyte N. 68 - Novembre 2002
[3] Lavinio Cerquetti: "JSP 1.2: JSTL - III parte", Mokabyte N. 69 - Dicembre 2002
[4] Lavinio Cerquetti: "JSP 1.2: JSTL - IV parte", Mokabyte N. 70 - Gennaio 2003
[5] Lavinio Cerquetti: "JSF: Java Server Faces - I parte", Mokabyte N. 71 - Febbraio 2003
[6] Lavinio Cerquetti: "JSF: Java Server Faces - II parte", Mokabyte N. 72 - Marzo 2003
[7] Lavinio Cerquetti: "Custom Tag Library in ambienti J2EE - I parte", Mokabyte N. 73 - Aprile 2003
[8] Lavinio Cerquetti: "Custom Tag Library in ambienti J2EE - II parte", Mokabyte N. 74 - Maggio 2003
[9] Lavinio Cerquetti: "Custom Tag Library in ambienti J2EE - III parte", Mokabyte N. 74 - Giugno 2003
[10] Lavinio Cerquetti: "Custom Tag Library in ambienti J2EE - IV parte", Mokabyte N. 74 - Luglio-Agosto 2003

 

Risorse
Scarica qui il codice presentato nell'articolo (NetView versione 1.3).
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.

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 lcerquetti@mokabyte.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