MokaByte 73- Aprile 2003 
JSP e Web User Interface
Custom Tag Library in ambienti J2EE
I parte

di
Lavinio Cerquetti
   
Analizziamo caratteristiche funzionali ed opportunitą di utilizzo di Custom Tag Library in ambienti J2EE

Introduzione
In questo numero iniziamo la trattazione dell'argomento conclusivo della nostra serie di articoli dedicati al tema delle Web UI in Java. In particolare, dopo esserci sino ad ora concentrati sullo studio delle due fondamentali Tag Library JSTL - nelle sue sottolibrerie Core, XML, DB e I18N - e Java Server Faces, analizzeremo le opportunità e le potenzialità offerte agli sviluppatori J2EE dall'implementazione di Tag Library aggiuntive nel contesto della versione 1.2 del framework Java Server Pages.

 

Custom Tag Library
Per Custom Tag Library - in breve CTL - si intende un gruppo di classi Java che, unitamente ad un apposito document descriptor, implementano un'estensione della sintassi JSP consistente in uno o più tag XML-like.
Tali tag donano allo sviluppatore accesso ad un insieme di nuove funzionalità e di comportamenti originali che egli può utilizzare e combinare in maniera libera e creativa con altre Tag Library all'interno delle proprie pagine JSP.
Il meccanismo delle CTL - introdotto a partire dalla versione 1.1 di Java Server Pages - è estremamente flessibile e generico, e garantisce una potenza espressiva sostanzialmente illimitata, come testimoniato dall'assoluta varietà strutturale ed eterogeneità funzionale delle Custom Tag Library disponibili sul mercato.
Tra i campi in cui le CTL hanno trovato maggior utilizzo vanno sicuramente citati l'impiego come componenti di presentazione in applicazioni HTML ed il ruolo di information provider nei confronti degli strati di interfaccia utente, funzionalità spesso ottenute tramite accesso diretto e non mediato al data layer del sistema applicativo.
Tali modelli di utilizzo - i quali hanno sicuramente svolto un ruolo positivo ed a volte insostituibile nel corso dell'evoluzione tecnica e strutturale che ha caratterizzato il paradigma J2EE in questi ultimi anni - sono tuttavia da considerarsi ormai sorpassati e introducono la questione dell'attuale ruolo delle Custom Tag Library in contesti J2EE.

 

Ruolo e finalità delle Custom Tag Library in ambienti Java Server
L'attuale maturità e solidità strutturale del paradigma J2EE, così come l'emergere di Tag Library funzionalmente evolute e da considerarsi a tutti gli effetti come dei veri e propri 'building block' standard dei quali gli sviluppatori possono usufruire per costruire le proprie soluzioni software in tempi sempre più rapidi, richiedono una chiarificazione delle finalità delle Custom Tag Library all'interno del panorama delle tecnologie Java lato server. In sostanza si pone la questione di quale debba essere il ruolo di questa tecnologia in ambiti J2EE, e di come la sua indubbia - talvolta persino eccessiva - potenza possa venire sfruttata al meglio e nel rispetto dell'ormai consolidata architettura che caratterizza le soluzioni software multi-tier in Java.
Come già fatto notare, tra i principali utilizzi storici delle CTL riveste un ruolo di primo piano la generazione di contenuto HTML / XHTML, che le ha viste sfruttate come veri e propri elementi di interfaccia utente in applicazioni Web. Questo impiego delle Custom Tag Library è oggi assolutamente sconsigliato, da un lato in quanto poco flessibile ed in contrasto con il principio di separazione delle responsabilità proprio dell'architettura n-tier J2EE e dall'altro in quanto in sovrapposizione con uno standard ben più ambizioso e tecnicamente evoluto, qual è Java Server Faces - che già abbiamo analizzato in [1] e [2] - alla luce del quale ha ben poco senso per lo sviluppatore tentare di 'reinventare la ruota', concependo e realizzando l'ennesimo framework di interfaccia utente HTML basato su JSP.
Egualmente sconsigliabile ed obsoleto è l'utilizzo di CTL per la realizzazione di sistemi di templating in HTML / XHTML / XML, non solo per l'enorme varietà dell'attuale offerta in tal senso, quanto soprattutto per la disponibilità di uno strumento open-source, riconosciuto come standard ed estremamente completo ed estensibile qual è la JSP Standard Tag Library, cui abbiamo rivolto la nostra attenzione in [3] e [4].
Non meno criticabile e pericoloso è l'impiego di CTL in ruoli di controllo dell'accesso e come strumento per la realizzazione di 'business layer intermedi' che spesso predano paradigmi consolidati - quali EJB, JDO, il pattern Controller Servlet o i filter servlet disponibili a partire dalla versione 2.3 delle Servlet API - e scavalcano i rispettivi layer software stabilendo comunicazioni dirette con il data repository del sistema applicativo. Un tale approccio alle CTL risulta ammissibile solamente in circostanze estremamente particolari e tipicamente in fase di prototipazione: inoltre, persino la necessità di provvedere alla codifica diretta di tali comportamenti è venuta nella grande maggioranza dei casi definitivamente meno in seguito al rilascio del componente SQL della JSP Standard Tag Library, da noi commentato in [5].
È invece certamente corretto l'utilizzo delle Custom Tag Library per la realizzazione di moduli funzionali strettamente coesi, tipicamente dedicati all'implementazione di funzionalità generiche e riusabili che si desiderano rendere disponibili agli strati di interfaccia utente in JSP. Tuttavia anche in questo caso il mercato offre un'ampia scelta di soluzioni pronte all'uso, standard e di verificata solidità ed affidabilità: a partire dalle Tag Library 'ponte' verso il mondo XML ed i servizi di internazionalizzazione descritte nella nostra serie di articoli - e precisamente in [5] e [6] - per arrivare all'odierna vasta disponibilità di prodotti commerciali ed open-source, come i componenti realizzati nel quadro del progetto Taglibs dal gruppo Jakarta / Apache [7].
Da quanto detto risulta quindi chiaro come l'evoluzione dell'architettura J2EE da un lato e l'affermarsi del modello open-source dall'altro abbiano provveduto a delimitare con una certa precisione quelli che sono gli attuali campi di utilizzo delle Custom Tag Library, ed al contempo a fornire soluzioni 'chiavi in mano' che sempre di più liberano gli sviluppatori dalla necessità di ricorrere in prima persona a questa tecnologia.
All'iniziale questione del ruolo delle CTL siamo quindi in grado di fornire una risposta chiara e sensata: l'implementazione di nuove Custom Tag Library è oggi da considerarsi come uno strumento di utilizzo limitato e destinato ad una precisa frangia di sviluppatori, vale a dire ai tool developer, cui spetta il compito di avvalersi di tale tecnologia per realizzare componenti che incapsulino funzionalità generiche e riusabili o comportamenti estremamente verticalizzati e problem-oriented. Viceversa le Custom Tag Library risultano completamente trasparenti agli sviluppatori di applicazioni, il cui ruolo consiste nel produrre sistemi software avvalendosi di ed assemblando - nella massima misura possibile - strumenti e preesistenti. Le Tag Library rappresentano dunque per gli application developer una tipologia di componenti 'black box' che essi valutano ed utilizzano in maniera funzionale, indipendentemente dalla loro natura standard (JSTL, Java Server Faces e così via) o custom.
Analizzati quindi il ruolo e le finalità delle Custom Tag Library in ambito J2EE, passiamo all'analisi delle caratteristiche di questa tecnologia.

 

Struttura di una Custom Tag Library
Una Custom Tag Library è costituita da tre componenti principali:

  • Un insieme di classi Java dedicate all'implementazione delle funzionalità dei tag, denominate tag handler;
  • un tag library descriptor, vale a dire un documento XML contenente le specifiche funzionali della CTL;
  • un sistema opzionale di validazione - rappresentato da una o più classi Java - il cui compito consiste nel verificare la correttezza sintattica delle pagine JSP che utilizzeranno la CTL.

Oltre ai punti sopra menzionati varrà anche la pena di rivolgere la nostra attenzione ai temi del deployment, della gestione degli attributi, degli errori, degli eventi, del tag nesting e dell'integrazione in contesto JSP / JSTL delle Custom Tag Library.

 

I tag handler
Per tag handler si intende una classe Java cui spetta il compito di fornire l'implementazione dei comportamenti associati ad uno specifico tag al JSP engine corrente e di comunicare con esso nel rispetto di una sequenza precisa di callback. Si noti che alle classi tag handler - diversamente da quanto accade per i servlet - non viene richiesto di essere rientranti: infatti, anche se il motore JSP ha il diritto di riutilizzare oggetti tag handler già istanziati, in ogni caso esso non può servirsi del medesimo tag handler per processare due tag contemporaneamente.
Java Server Pages 1.2 identifica tre categorie di tag handler - cui corrispondono altrettante interfacce definite nel package javax.servlet.jsp.tagext - differenziate in base alla complessità ed alla natura delle operazioni che svolgono sul contenuto delle pagine JSP: i tag handler base, i quali implementano l'interfaccia javax.servlet.jsp.tagext.Tag, i tag handler iterativi - associati all'interfaccia javax.servlet.jsp.tagext.IterationTag - ed i tag handler di contenuto, vincolati all'interfaccia javax.servlet.jsp.tagext.BodyTag.

 

I tag handler base
I tag handler base sono caratterizzati da un'interazione ridotta con la pagina JSP al cui interno agiscono; in particolare essi non effettuano alcuna manipolazione del loro body content, vale a dire del codice HTML / JSP contenuto all'interno dei rispettivi tag di apertura e chiusura. Tali tag handler, come già visto, implementano l'interfaccia javax.servlet.jsp.tagext e sono caratterizzati da un meccanismo di callback estremamente semplice e basato sui metodi doStartTag() e doEndTag(), che andiamo ad analizzare:

int doStartTag() throws javax.servlet.jsp.JspException

doStartTag() viene invocato ad ogni utilizzo all'interno di una pagina JSP del tag rappresentato dal tag handler e comunica con il motore JSP tramite il suo valore di ritorno:

javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE: il JSP engine procede alla normale valutazione del corpo del tag e di ogni eventuale subtag;
javax.servlet.jsp.tagext.Tag.SKIP_BODY: il corpo del tag viene interamente ignorato dal motore JSP, che salta direttamente al relativo tag di chiusura.

È anche possibile per il metodo doStartTag() generare un'eccezione figlia di javax.servlet.jsp.JspException, al fine di segnalare una condizione di errore grave: in questo caso il JSP engine interromperà il processo di valutazione dell'intera View JSP, usualmente ritornando al browser una pagina d'errore.

int doEndTag() throws javax.servlet.jsp.JspException

doEndTag() viene invocato in corrispondenza del tag di chiusura sull'oggetto tag handler corrispondente al tag corrente. Anche in questo caso la comunicazione con il motore JSP è affidata ad un semplice valore di ritorno:

javax.servlet.jsp.tagext.Tag.EVAL_PAGE: il JSP engine procede normalmente al processing del resto della pagina;
javax.servlet.jsp.tagext.Tag.SKIP_PAGE: la valutazione della pagina corrente viene interrotta.

Il ciclo di vita di un tag handler consiste dunque in coppie di invocazioni dei suddetti metodi doStartTag() e doEndTag(), seguiti da una chiamata finale al metodo release(), in risposta al quale il tag handler è tenuto a rilasciare immediatamente tutte le risorse in proprio possesso ed a predisporsi alla finalizzazione.
Si noti che all'atto dell'invocazione di doStartTag() il tag handler può fare affidamento sulla corretta inizializzazione del proprio contesto, consistente nell'esecuzione preventiva da parte del JSP engine dei seguenti passi:

  • La proprietà pageContext del tag handler viene impostata, tramite l'apposito metodo setPageContext() dell'interfaccia javax.servlet.jsp.tagext.Tag, all'oggetto contenente le informazioni di contesto relative alla pagina JSP corrente;
  • in caso di tag innestati la proprietà parent del tag handler punta al relativo tag handler padre;
  • qualora al tag siano associati degli attributi essi vengono impostati correttamente nell'istanza di tag handler.


Figura 1
- Ciclo di vita di un tag handler base

Dal momento che buona parte dei metodi definiti nell'interfaccia Tag viene di norma codificata in maniera identica in ogni tag handler, il package javax.servlet.jsp.tagext mette a disposizione la convenience class TagSupport, la quale fornisce un'implementazione standard di ogni metodo d'interfaccia. Il definire i propri tag handler base a partire dalla classe TagSupport permette agli sviluppatori di evitare la codifica diretta degli accessori per le proprietà parent e pageContext, nonché tipicamente l'implementazione del metodo release(), consentendo loro di concentrarsi sui soli metodi doStartTag() e doEndTag().

 

I tag handler iterativi
L'interfaccia javax.servlet.jsp.tagext.IterationTag rappresenta una delle novità della versione 1.2 del framework Java Server Pages e consente un'ìmplementazione estremamente semplificata di tag iterativi, vale a dire di quei tag che hanno bisogno di valutare ripetutamente il proprio body content sul modello del tag <c:forEach> di JSTL.
Tale interfaccia estende javax.servlet.jsp.tagext.Tag, aggiungendovi un unico metodo:

int doAfterBody() throws javax.servlet.jsp.JspException

doAfterBody() viene invocato al termine di ogni valutazione del contenuto del tag - dopo il doStartTag() iniziale e prima del doEndTag() finale - e comunica con il motore JSP tramite il suo valore di ritorno:

javax.servlet.jsp.tagext.IterationTag.EVAL_BODY_AGAIN: il motore JSP effettuerà un'altra iterazione sul tag, valutandone nuovamente il body e gli eventuali subtag;
javax.servlet.jsp.tagext.Tag.SKIP_BODY: la valutazione di questo tag è da ritenersi conclusa ed il JSP engine può procedere al processing del resto della pagina.


Figura 2
- Ciclo di vita di un tag handler iterativo

Anche nel caso dei tag handler iterativi è possibile per lo sviluppatore risparmiare parte del lavoro di codifica definendo la propria classe come child class di javax.servlet.jsp.tagext.TagSupport; sarà quindi sufficiente, nella gran parte dei casi, limitarsi all'implementazione dei metodi doStartTag(), doAfterBody() e doEndTag().

 

I tag handler di contenuto
Un'interazione più complessa caratterizza i tag handler di contenuto, rappresentati da classi che implementano l'interfaccia javax.servlet.jsp.tagext.BodyTag.
Tale interfaccia - basata su javax.servlet.jsp.tagext.Tag nella versione 1.1 di Java Server Pages - estende invece in JSP 1.2 javax.servlet.jsp.tagext.IterationTag e fornisce un insieme di metodi addizionali rivolti alla gestione del body content di un tag.

Il metodo doStartTag() supporta nel caso di tag handler di contenuto un insieme esteso di valori di ritorno:

  • javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE: il JSP engine procede alla normale valutazione del corpo del tag e di ogni eventuale subtag;
  • javax.servlet.jsp.tagext.Tag.SKIP_BODY: il corpo del tag viene interamente ignorato dal motore JSP, che salta direttamente al relativo tag di chiusura;
  • javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_BUFFERED: laddove le funzionalità dei due precedenti codici di ritorno corrispondono perfettamente a quanto visto nel caso dei tag handler base, il valore EVAL_BODY_BUFFERED permette la gestione in modalità bufferizzata del risultato della valutazione del body content del tag.

Si noti che a partire dalla versione 1.2 del framework Java Server Pages l'utilizzo del valore di ritorno javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_TAG, impiegato in JSP 1.1 - in funzione del contesto - con la medesima semantica di EVAL_BODY_AGAIN o di EVAL_BODY_BUFFERED è ufficialmente sconsigliato.

In caso di ritorno del valore EVAL_BODY_BUFFERED il motore JSP provvede ad invocare immediatamente sul tag handler due metodi aggiuntivi definiti nell'interfaccia javax.servlet.jsp.tagext.BodyTag:

void setBodyContent(BodyContent bodyContent)

A setBodyContent() spetta semplicemente la responsabilità di impostare la proprietà bodyContent del tag handler al buffer utilizzato per memorizzare il contenuto del tag. Tale buffer viene rappresentato da un'istanza della classe javax.servlet.jsp.tagext.BodyContent, child class di javax.servlet.jsp.JspWriter.

int doInitBody() throws javax.servlet.jsp.JspException

Questo metodo viene invocato per dare al tag handler la possibilità di effettuare specifiche attività di inizializzazione in seguito all'impostazione del body content e di interrompere, in caso di condizione di errore critico, il processing dell'intera pagina corrente.

Nel ciclo di vita di un tag handler di contenuti seguono quindi una o più chiamate al metodo doAfterBody() - la cui semantica è identica a quella già analizzata per l'interfaccia IterationTag - ed una chiamata finale al metodo doEndTag(), con la quale si conclude il processing del tag iniziato con l'invocazione del metodo doStartTag(). L'ultimo atto nella vita di un tag handler di contenuti è rappresentato dalla chiamata da parte del JSP engine del suo metodo release(), a fronte del quale il tag handler è tenuto a rilasciare tutte le risorse a propria disposizione, predisponendosi per la garbage collection.


Figura 3
- Ciclo di vita di un tag handler di contenuto in modalità bufferizzata

Qualora il tag handler abbia richiesto in sede di doStartTag() un processing buffer-based dei contenuti del tag tramite il valore di ritorno EVAL_BODY_BUFFERED è sua diretta responsabilità provvedere all'output del body content tramite il Writer associato, operazione tipicamente svolta nel metodo doEndTag().
Grazie a questa gestione dell'output in modalità lazy risulta possibile per il tag handler - in qualsiasi momento del suo ciclo di vita - applicare nuove trasformazioni al body content o addirittura sopprimerlo interamente semplicemente evitando di inviarlo al Writer.
Anche nel caso dei tag handler di contenuti è possibile per gli sviluppatori utilizzare le implementazioni standard per diversi metodi d'interfaccia definendo la propria classe a partire dalla convenience class javax.servlet.jsp.tagext.BodyTagSupport.

 

Conclusione
Nel presente articolo abbiamo introdotto il tema delle Custom Tag Library, analizzandone in particolare il ruolo e le finalità in ambienti J2EE ed iniziando la trattazione delle caratteristiche tecniche di questa API nel contesto della versione 1.2 del framework Java Server Pages.
La presentazione di questa tecnologia proseguirà nel prossimo articolo con la trattazione dei suoi rimanenti principali aspetti strutturali.

 

Bibliografia
[1] Lavinio Cerquetti: "JSF: Java Server Faces - I parte", Mokabyte N. 71 - Febbraio 2003
[2] Lavinio Cerquetti: "JSF: Java Server Faces - II parte", Mokabyte N. 72 - Marzo 2003
[3] Lavinio Cerquetti: "JSP 1.2: JSTL - I parte", Mokabyte N. 67 - Ottobre 2002
[4] Lavinio Cerquetti: "JSP 1.2: JSTL - II parte", Mokabyte N. 68 - Novembre 2002
[5] Lavinio Cerquetti: "JSP 1.2: JSTL - IV parte", Mokabyte N. 70 - Gennaio 2003
[6] Lavinio Cerquetti: "JSP 1.2: JSTL - III parte", Mokabyte N. 69 - Dicembre 2002
[7] Jakarta Taglibs: http://jakarta.apache.org/taglibs

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