MokaByte 74- Maggio 2003 
JSP e Web User Interface
Custom Tag Library in ambienti J2EE
II parte

di
Lavinio Cerquetti
   
Introdotto nel numero precedente [1] l'importante tema delle Custom Tag Library ed avendone in particolare analizzati il ruolo e le finalità in ambienti J2EE proseguiamo con questo articolo la trattazione dell'argomento rivolgendo la nostra attenzione ai principali elementi strutturali di questa API nel contesto della versione 1.2 del framework Java Server Pages.

Il Tag Library Descriptor
Dopo i tag handler, di cui ci siamo occupati il mese scorso, il secondo elemento fondamentale di una Custom Tag Library (detta anche CTL) è dato dal tag library descriptor - in breve TLD - ossia un documento XML avente suffisso .tld e contenente, in forma dichiarativa, le specifiche funzionali della CTL stessa.
La conoscenza degli elementi caratterizzanti del linguaggio XML, in virtù del suo sempre più ampio utilizzo in questi ultimi anni, è oggi così diffusa da non richiedere ulteriori chiarimenti in questa sede. Per lo stesso motivo non ci soffermeremo sui vantaggi derivanti dall'utilizzo di XML in termini di standardizzazione, riuso, accesso e presentazione delle informazioni in simbiosi con tecnologie quali XSTL e XPath, vantaggi che lo hanno reso un componente essenziale all'interno del framework J2EE nel ruolo di strumento per la redazione di specifiche funzionali di componenti software.

Il ruolo di un Tag Library Descriptor consiste nella descrizione delle caratteristiche strutturali, sintattiche ed implementative di una Custom Tag Library. Tali informazioni vengono fornite tramite un insieme di tag XML descritti da un apposito DTD. Nel presente articolo faremo esclusivo riferimento al nuovo TLD Document Type Definition facente parte integrante delle specifiche di Java Server Pages 1.2 [2].
Si noti che JSP 1.2 garantisce la piena backward compatibility con le Custom Tag Library realizzate per la precedente versione 1.1 di JSP, il cui DTD [3] continua ad essere pienamente supportato. Una CTL dichiara la propria intenzione di servirsi del nuovo Document Type Definition di JSP 1.2 semplicemente facendo ad esso riferimento nel suo tag <!DOCTYPE>.

Nel caso di Custom Tag Library complesse il Tag Library Descriptor tende a divenire un documento piuttosto lungo e prolisso, la cui stesura viene tipicamente automatizzata dagli wizard dell'application server ovvero dell'ambiente di sviluppo utilizzato. Per questa ragione non analizzeremo in dettaglio tutti i numerosi tag supportati dal TLD Document Type Definition di JSP 1.2, ma ci limiteremo ad analizzarne i tre principali.

Il root element di un TLD è rappresentato dal tag <taglib>, la cui definizione legge:

<!ELEMENT taglib (tlib-version, jsp-version?, short-name, uri?, display-name?, small-icon?, large-icon?, description?, validator?, listener*, tag+) >

I principali elementi di tale definizione sono:

  • tlib-version: numero di versione della Custom Tag Library, gestito dal suo autore;
    jsp-version: versione opzionale del framework JSP per cui la CTL corrente è stata realizzata;
  • short-name: identificatore associato alla tag library. Tipicamente viene scelto ed utilizzato a tal fine il prefisso con cui per convenzione si importa la Custom Tag Library nelle pagine JSP che ne fanno uso;
  • description: descrizione libera ed opzionale della tag library;
    validator: questo elemento opzionale rappresenta una novità di JSP 1.2 e permette di specificare un'apposita classe Java - sulle cui caratteristiche ci soffermeremo più tardi nel corso del presente articolo - deputata alla validazione a livello globale delle pagine JSP che fanno uso della Custom Tag Library in oggetto;
  • listener: tramite gli elementi opzionali listener, anch'essi introdotti a partire da JSP 1.2, è possibile stabilire un binding tra tag CTL e gli eventi globali dell'applicazione definiti a partire dalla versione 2.3 della Servlet API;
  • tag: la definizione dei tag caratterizzanti una CTL viene fornita tramite uno o più elementi <tag> innestati nel root tag <taglib>. Il tag <tag> costituisce il singolo elemento di maggiore importanza di un TLD:

<!ELEMENT tag (name, tag-class, tei-class?, body-content?, display-name?, small-icon?, large-icon?, description?, variable*, attribute*, example?) >

  • name: identificatore del tag;
  • tag-class: riferimento al tag handler che implementa il tag in oggetto, specificato attraverso il nome completo della classe Java che ne fornisce l'implementazione;
  • tei-class: se presente fa riferimento ad una classe figlia di javax.servlet.jsp.tagext.TagExtraInfo, utilizzata - come avremo l'occasione di vedere - per definire variabili di scripting ed implementare un meccanismo di validazione tag-based in fase di traduzione di pagina;
    • body-content: questo elemento consente di specificare la natura del corpo del body del tag in oggetto e può assumere tre valori:
    • JSP: questo è il valore di default, e comunica al JSP container che il body content del tag deve venire processato a run time in maniera analoga al contenuto di ogni altro tag JSP e nel rispetto della sua sintassi;
    • tagdependent: il contenuto del tag non va interpretato in conformità agli standard JSP. Il JSP engine non ne effettuerà quindi nessuna elaborazione a run time, ma si limiterà a trasmetterlo, integralmente e senza modifiche di sorta, al tag handler;
    • empty: la presenza di un qualsiasi body content per il tag in oggetto è da considerarsi un errore e provocherà l'interruzione del processing della pagina JSP.
  • variable: se presente, consiste in un elenco di variabili di scripting definite dal tag corrente e che verranno rese disponibili alle pagine JSP che ne fanno uso;
  • attribute: sottoalbero opzionale composto di tag <attribute>, tramite i quali vengono specificati gli attributi accettati dal tag in oggetto;
  • example: questo elemento opzionale costituisce una novità di JSP 1.2 e permette di fornire una descrizione libera - ed eventualmente comprensiva di esempi - della sintassi e della semantica del tag.
    L'ultimo elemento su cui desideriamo soffermarci è il tag <attribute>, il quale dichiara le caratteristiche degli attributi accettati da un tag:

    <!ELEMENT attribute (name, required?, rtexprvalue?) >

  • name: identificatore dell'attributo;
  • required: true se quest'attributo è obbligatorio, false in caso contrario. Se non diversamente specificato ogni attributo è opzionale;
  • rtexprvalue: questo elemento indica se il valore fornito per questo attributo va calcolato staticamente all'atto della compilazione della pagina o se esso può essere il risultato di un'espressione JSP da processare in sede di run time. La possibilità di valorizzare gli attributi in fase run time - attivata impostando a true questo elemento - è evidentemente di fondamentale importanza ed è su di essa che si basa la sincronizzazione dinamica tra il contenuto di pagine JSP e lo stato di oggetti JavaBean. D'altro canto l'impostazione di rtexprvalue a false - valore di default - rende disponibile agli eventuali strati di validazione tutte le informazioni circa l'utilizzo di un tag già in translation time, consentendone una completa analisi semantica.

Si noti che il valore di ogni attributo fornito ai tag delle Custom Tag Library deve essere delimitato da apici singoli o doppi, nel rispetto delle norme XML.

 

Utilizzo di una Custom Tag Library
L'intenzione di servirsi di una Custom Tag Library dall'interno di una pagina JSP va dichiarata tramite un'apposita direttiva <taglib>:

<%@ taglib uri="<uri>" prefix="<prefisso>" %>

L'attributo uri specifica la posizione del TLD associato alla Custom Tag Library, mentre l'attributo prefix comunica al motore JSP il prefisso con il quale si desidera fare riferimento ai tag della Custom Tag Library nella pagina corrente. Si noti che il prefisso associato ad una CTL non rappresenta una proprietà della tag library stessa, ma può venire liberamente scelto dall'autore della pagina JSP. Dal momento che ciò può rendere il codice confuso e difficilmente comprensibile, si consiglia di scegliere ed utilizzare i prefissi delle CTL in maniera logica e coerente, attenendosi alla relativa documentazione. Uno standard comunemente accettato consiste nell'utilizzare prefissi chiari e significativi, facendo possibilmente uso al loro interno del nome della propria azienda o organizzazione - in maniera sostanzialmente analoga a quanto avviene per i package Java - al fine di evitare possibili conflitti nella nomenclatura delle Custom Tag Library.
In questa sede vale la pena di notare che i prefissi jsp:, jspx:, java:, javax:, servlet:, sun: e sunw: sono da considerarsi riservati e non possono venire assegnati a tag library di terze parti.

Una pagina JSP può contenere un numero qualsiasi di direttive <taglib>, le quali possono venire collocate liberamente all'interno della pagina, purché antecedentemente al primo utilizzo di un tag della Custom Tag Library cui fanno riferimento. Per chiarezza si preferisce usualmente raggruppare tutte le direttive <taglib> utilizzate in testa alla pagina.
L'importazione di una Custom Tag Library il cui TLD risulti irraggiungibile in fase di traduzione ovvero l'utilizzo di un tag il cui tag handler non sia reperibile a run time fanno scattare delle apposite eccezioni ed interrompono il processing della pagina JSP corrente.

 

Deploying di una Custom Tag Library
Il deploying di una Custom Tag Library può avvenire secondo tre diverse modalità.
Il meccanismo consigliato, particolarmente adatto a Custom Tag Library di utilizzo generale per le quali si intende garantire il decoupling da una particolare applicazione o contesto Web, consiste nel distribuire la CTL sotto forma di un archivio JAR contenente le classi tag handler ed il Tag Library Descriptor, collocato nella directory /META-INF con il nome di taglib.tld.

A tale archivio JAR - tipicamente innestato nella directory /WEB-INF/lib/ delle Web Application per evitare problemi a livello di CLASSPATH - faranno quindi riferimento le direttive <taglib> delle diverse pagine JSP, secondo la seguente sintassi:

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

Nel caso di Custom Tag Library strettamente accoppiate ad un'applicazione e non concepite in un'ottica di riusabilità è altresì possibile far uso di un meccanismo alternativo, consistente nell'innesto della CTL all'interno del file WAR della Web Application che ne fa uso. Per sfruttare questa tecnica è necessario inserire un riferimento alla Custom Tag Library nell'Application Deployment Descriptor dell'applicazione (ossia il file web.xml) secondo la seguente sintassi:

<taglib>
  <taglib-uri><identificativo uri della CTL></taglib-uri>
  <taglib-location><percorso del TLD></taglib-location>
</taglib>

La scelta dell'identificativo (uri) con cui la Custom Tag Library verrà importata dalle pagine JSP è completamente libera, ed il percorso del Tag Library Descriptor - vale a dire del documento XML con suffisso .tld contenente le specifiche della CTL - viene specificato relativamente al file WAR. Tipicamente, per proteggerne i contenuti da accessi indesiderati, tutti i Tag Library Descriptor utilizzati da un'applicazione vengono collocati nella directory /WEB-INF o in una sua sottodirectory, ad esempio /WEB-INF/tlds/.

Il terzo ed ultimo meccanismo - utilizzato nel contesto di sistemi ed applicazioni particolarmente semplici o di test - consiste nel rendere il Tag Library Descriptor di una Custom Tag Library accessibile tramite una URL pubblica ed esterna alla Web Application, impostando al contempo il contesto di esecuzione in maniera che le relative classi tag handler si trovino nel CLASSPATH dell'applicazione stessa.
In questo caso la direttiva <taglib> ha il seguente aspetto:

<%@ taglib uri="<URL pubblica del file .tld>" prefix="<prefisso>" %>

L'utilizzo di quest'ultimo meccanismo è assolutamente sconsigliato in ambienti di produzione, dal momento che esso non contempla in alcuna forma il concetto di packaging di una Custom Tag Library come entità indipendente. Si noti inoltre che il Tag Library Descriptor della CTL risulta pubblicamente accessibile.

 

Validazione di una Custom Tag Library
Per validazione si intende la possibilità di stabilire dei vincoli di correttezza sintattica e semantica nell'utilizzo di una Custom Tag Library, il mancato rispetto dei quali porti ad intraprendere apposite azioni correttive o faccia scattare opportune procedure di errore.
Un primo livello di validazione è dato, in maniera abbastanza evidente, dallo stesso Tag Library Descriptor, il quale è in grado di stabilire con una certa precisione i limiti sintattici dell'utilizzo di un tag CTL.
E d'altronde chiaro che a volte si rendono necessari dei meccanismi di verifica e controllo più flessibili, legati non solamente ad una rigida definizione sintattica di un tag, ma al contesto semantico in cui esso viene utilizzato.

Il framework Java Server Pages offre due diversi meccanismi di custom validation, il primo dei quali è disponibile sin dalla versione 1.1 di JSP e consiste nell'istanziazione di una classe figlia di javax.servlet.jsp.tagext.TagExtraInfo, utilizzata per effettuare la verifica del corretto utilizzo di un tag in translation time. Tale meccanismo - per quanto di semplice implementazione - permette di definire politiche di validazione sufficientemente espressive per i bisogni della maggior parte delle Custom Tag Library.
Come abbiamo già visto in precedenza, l'associazione di un oggetto di classe TagExtraInfo ad un tag di una Custom Tag Library viene dichiarata a livello di TLD tramite il subtag <tei-class> del tag <tag>, il quale contiene il nome completo della relativa classe Java, la quale verrà istanziata in sede di traduzione della pagina dal motore JSP.

L'implementazione di funzionalità di validazione tramite un oggetto TagExtraInfo richiede l'overriding di tre metodi:

public void setTagInfo(javax.servlet.jsp.tagext.TagInfo tagInfo)

L'accessore setTagInfo() viene invocato dal JSP engine sull'oggetto TagExtraInfo prima della validazione vera e propria. L'oggetto TagInfo, che normalmente le implementazioni di questo metodo si limitano a salvare in vista di un successivo utilizzo, contiene tutte le informazioni circa il tag corrente fornite nel Tag Library Descriptor.

public javax.servlet.jsp.tagext.TagInfo TagInfo getTagInfo()

Il compito di getTagInfo() consiste semplicemente nel ritornare l'oggetto TagInfo precedentemente associato ad un oggetto di classe TagExtraInfo.

public boolean isValid(javax.servlet.jsp.tagext.TagData tagData)

isValid() implementa il vero e proprio meccanismo di validazione. Tale metodo viene invocato durante la fase di traduzione dal motore JSP per ogni utilizzo del tag all'interno di una pagina JSP, ed il suo contratto consiste nel ritornare true qualora l'utilizzo del tag sia da ritenersi corretto e false altrimenti. L'argomento tagData è sostanzialmente un data container per mezzo del quale isValid() può accedere alle informazioni circa gli attributi ed i valori loro assegnati nel contesto corrente del tag. Si noti che, avvenendo la validazione in translation time, le informazioni di valorizzazione non risultano disponibili per gli attributi dinamici, ossia per quegli attributi il cui corrispondente elemento rtexprvalue nel Tag Library Descriptor è stato impostato a true secondo quanto descritto in precedenza .

Al meccanismo di custom validation appena visto e basato sulla classe TagExtraInfo la nuova versione 1.2 di Java Server Pages affianca il concetto di validazione a livello di Custom Tag Library. In altre parole, si introduce la nozione di uno schema di validazione i cui confini semantici non siano limitati all'orizzonte del tag corrente, ma possano prendere in considerazione l'intera tag library e la sua dinamica di utilizzo all'interno di una singola pagina JSP.

A questo fine - come già visto in precedenza - è possibile dichiarare nel Tag Library Descriptor di una Custom Tag Library una classe globale di validazione tramite l'attributo validator del root tag <taglib>. Essa deve estendere la classe astratta javax.servlet.jsp.tagext.TagLibraryValidator e viene invocata dal motore JSP tramite il seguente metodo di callback, definito dalle Servlet API 2.3:

javax.servlet.jsp.tagext.ValidationMessage[] validate(String prefix, String uri, javax.servlet.jsp.tagext.PageData pageData)

Il JSP engine provvederà ad invocare il metodo validate() in fase di traduzione per ogni tag utilizzato, fornendo all'oggetto di classe TagLibraryValidator le seguenti informazioni:

L'identificatore e l'uri del tag corrente, rispettivamente memorizzati nei parametri prefix e uri;
un oggetto di classe javax.servlet.jsp.tagext.PageData contenente l'intera pagina JSP corrente sotto forma di documento XML.

Il metodo validate() è tenuto a ritornare null se l'utilizzo del tag è da ritenersi corretto ed un vettore di messaggi di errore ValidationMessage in caso contrario.

Si noti che la precedente versione 2.2 delle Servlet API definisce il metodo validate() in maniera leggermente diversa, consentendo a quest'ultimo di ritornare un unico messaggio di errore codificato direttamente come oggetto String:

String validate(String prefix, String uri, javax.servlet.jsp.tagext.PageData pageData)

L'introduzione della classe TagLibraryValidator in JSP 1.2 rappresenta una novità particolarmente significativa ed utile nell'ambito di Custom Tag Library ricche e complesse, in quanto consente a queste ultime di arrivare ad una decisione circa la validità del particolare utilizzo di un tag tenendo conto di tutti gli elementi della pagina (tag a qualsiasi livello di nesting, scriptlet, body content) e - se necessario - ritornando messaggi di errore chiari, comprensibili e localizzati.

 

Conclusione
Con il presente articolo abbiamo ultimato la trattazione dei componenti fondamentali delle Custom Tag Library, trattando in particolare il Tag Library Descriptor - vale a dire il documento XML contenente le specifiche funzionali di una CTL - ed i meccanismi di custom validation dei contenuti di una pagina JSP, fronte sul quale la versione 1.2 di Java Server Pages introduce il concetto di un front-end globale di validazione.
Nel prossimo articolo rivolgeremo la nostra attenzione agli altri elementi che caratterizzano una Custom Tag Library; in particolare, analizzeremo la gestione degli attributi, degli errori, degli eventi, del tag nesting e delle variabili di scripting.

Bibliografia
[1] Lavinio Cerquetti: "Custom Tag Library in ambienti J2EE - I parte", Mokabyte N. 73 - Aprile 2003
[2] JSP 1.2 - Tag Library Descriptor Document Type Definition: http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd
[3] JSP 1.1 - Tag Library Descriptor Document Type Definition: http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd

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