Continuiamo la nostra esplorazione di JavaServer Faces affrontando due aspetti tipici che si incontrano sempre nello sviluppo di una applicazione web: la conversione e la validazione dei dati di interfaccia.
Introduzione
La conversione e la validazione dei dati di interfaccia sono due problemi tipici nello sviluppo di una applicazione web. I dati inseriti in un form di una pagina HTML vengono inviati al server sotto forma di stringhe. Ma è chiaro che non tutti i dati nel dominio di un determinato problema sono rappresentabili da stringhe; in qualsiasi applicazione si ha a che fare con numeri, date, percentuali e così via. Questo comporta che è necessario effettuare una trasformazione del dato dalla sua digitazione da parte dell‘utente nella pagina web al suo utilizzo in oggetti di business che rappresentano le entità del nostro sistema. È necessario quindi effettuare in genere una conversione del dato originario in un dato tipizzato che ha significato nel contesto di utilizzo. Allo stesso modo quando il valore di un oggetto deve essere visualizzato è necessario darne una rappresentazione sotto forma di stringa per poterlo presentare all‘utente nell‘interfaccia utente.
È altresì importante che tutti i dati che arrivano al layer applicativo dal layer di interfaccia siano validi. Ovvero siano sottoposti ad un controllo formale prima di essere utilizzati per valorizzare gli attributi di oggetti di business. I tipi di validazione sono quelli tipici di una applicazione e hanno lo scopo di verificare che il dato inserito sia conforme alle regole che abbiamo stabilito per il nostro contesto applicativo: un dato obbligatorio deve necessariamente essere digitato, una data deve necessariamente avere un certo formato stabilito e così via.
Ogni framework degno di questo nome deve mettere a disposizione dello sviluppatore strumenti che assolvono a questi due importanti compiti e che lo svincolino da scrivere codice ripetitivo e a scarso valore aggiunto.
JSF mette a disposizione convertitori e validatori che assolvono al problema descritto. Abbiamo già discusso in [6] in quale momento del ciclo di vita di una richiesta JSF avvengono queste operazioni. Vediamo ora come si utilizzano convertitori e validatori nelle applicazioni.
Convertitori
Ogni giorno ciascuno di noi visualizza pagine web e invia ai server web dati digitati nei form delle pagine stesse. Chi non ha mai riempito un form di registrazione a un qualche servizio? Affinchà© una applicazione possa mostrare i dati ad un utente questi devono essere necessariamente convertiti in stringhe e l‘utente li digiterà nell‘interfaccia utente come stringhe. Ma nel dominio applicativo questi dati saranno rappresentati da oggetti con un tipo opportuno alla loro rappresentazione: un oggetto cha rappresenta una data, un numero e così via.
I convertitori hanno proprio la funzione di creare una rappresentazione in forma di stringa di un oggetto e di creare un oggetto dalla rappresentazione in stringa del dato da questo rappresentato.
JSF fornisce una serie di converters standard che coprono l‘esigenza della conversione per tutti i tipi base del linguaggio Java quali Byte, Character, Integer, Short, Double, Float, Long BigDecimal, BigInteger, Boolean Character.
Questo significa che se associo a un campo di input del mio form un attributo di un managed-bean di tipo Integer, sarà il framework a effettuare l‘opportuna trasformazione della stringa digitata nel tipo desiderato. Analogamente il framework si occupa di fornire la rappresentazione sotto forma di stringa dell‘attributo quando questo debba essere visualizzato nell‘interfaccia utente. Il framework si occuperà anche di inviare all‘utente un opportuno messaggio di errore qualora la conversione non sia possibile in base a quanto digitato dall‘utente nell‘interfaccia,
Tutto ciò può sembrare quasi ovvio e banale ma chi ha sviluppato applicazioni web utilizzando Servlet e JSP senza l‘ausilio di alcun framework sa quanto sia tedioso e a rischio di errori questo compito.
Oltre ai converters standard, JSF dà allo sviluppatore la possibilità di costruire convertitori custom in base alle singole esigenze.
Tipicamente i convertitori devono essere registrati sui componenti: è possibile farlo a livello di codice Java ma più comunemente lo si fa nella pagina JSP in diversi modi.
Un modo è quello di utilizzare l‘attributo converter dei tag JSF che lo prevedono, tipicamente componenti di input o di visualizzazione dati. In questo modo è possibile associare un convertitore a un campo di input dichiarando nell‘attributo il suo identificatore. Ad esempio:
In questo caso si è dichiarato che l‘output dell‘attributo codiceAvvPostale del managed-bean utente deve essere convertito in fase di visualizzazione secondo le regole del converter registrato con l‘identificatore cap.
È possibile ottenere lo stesso risultato utilizzando il tag
È possibile inserire al posto dell‘identificatore del converter anche un espressione che realizza un value-binding con una istanza della classe del converter, ma ciò toglie flessibilità all‘applicazione.
È preferibile invece dichiarare nel file di configurazione i converter assegnando ad essi un identificatore e poi facendo riferimento a questo nell‘applicazione.
Di seguito è riportato un esempio di dichiarazione per identificatore di un converter nel file faces-config.xml:
cap it.mokabyte.converter.CapConverter
Un convertitore deve corrispondere ad una classe che implementa l‘interfaccia javax.faces.convert.Converter che definisce i metodi
public Object getAsObject(FacesContext facesContext,UIComponent uIComponent, String string)
throws ConverterException;
public String getAsString(FacesContext facesContext,UIComponent uIComponent, Object object)
throws ConverterException;
per le conversioni da stringa a oggetto e da oggetto a stringa rispettivamente.
Oltre che per identificatore, i converter possono essere dichiarati anche per classe, quindi associati a uno specifico tipo di oggetto Java.
La registrazione è necessaria solo per i convertitori custom mentre non è necessaria per i convertitori standard.
I convertitori standard di JSF sono essenzialmente due: il DateTime per le date e il Number per numeri, valute e percentuali. I tag corrispondenti sono
Oltre che a dare una rappresentazione in forma di stringa degli oggetti a cui fanno riferimento sono molto utili per impostare il formato del dato digitato nella pagina, Nell‘applicazione di esempio (che potete scaricare dal menu in alto a sinistra nello ZIP che contiene l‘applicazione sia per JBoss che per Tomcat) è stato aggiunto un esempio di utilizzo del convertitore per le date.
Nei riferimenti al termine dell‘articolo, è poi possibile trovare numerosi esempi di utilizzo dei converter standard.
È possibile infine definire un custom tag per un proprio converter e associarlo a un componente innestando il proprio tag all‘interno del tag del componente stesso. Questa tecnica può essere utile quando si vogliono passare dei parametri al converter:
Validatori
La validazione dei dati di input di una applicazione è un aspetto essenziale per la robustezza e l‘affidabilità della stessa. Consentire a un dato non corretto di transitare per i layer applicativi del nostro sistema è un grave errore. I validatori hanno il compito di impedire all‘utente l‘immissione di dati non validi secondo le regole stabilite dalla logica applicativa.
Come per i convertitori, JSF ci mette a disposizione tutti gli strumenti per effettuare la validazione dell‘input in modo semplice ed efficiente.
C‘è da osservare però che JSF al momento non fornisce uno strumento per la validazione client-side. La verifica dei dati viene effettuata server-side durante il ciclo di vita della richiesta. Questo quindi implica un round-trip al server ogniqualvolta che si effettua l‘invio di un form con dati non validi. È possibile, se lo si desidera, utilizzare il Validator Framework di Struts qualora si voglia effettuare anche una validazione client-side.
Ci sono diversi modi per associare una validazione a un componente di input.
Si può utilizzare l‘attributo validator dei tag JSF associati a componenti di input per definire un binding con un metodo di un managed-bean. Questa tecnica può essere usata quando è necessario effettuare validazioni in casi specifici secondo una logica non riutilizzabile in altri casi.
Ad esempio in un form di registrazione potrei effettuare una validazione del nome utente in base ad una logica particolare della singola applicazione:
value="#{utente.username}/>
Altrimenti è possibile registrare sui componenti validatori standard e/o custom analogamente a quanto già visto per i convertitori, ovvero innestando il custom tag del validatore all‘interno del tag del componente oppure utilizzando il tag
JSF fornisce alcuni validator standard, DoubleRange, Length, LongRange i cui corrispondenti tag JSF sono
Tutti i componenti di input hanno poi un attributo required che definisce l‘obbligatorietà del dato da digitare se posto al valore true. Non è proprio un validatore ma ha lo stesso significato, ed unito ai validatori standard può soddisfare molte delle esigenze comuni. Se gli strumenti standard forniti da JSF non sono sufficienti per le nostre esigenze è comunque possibile realizzare un validatore custom.
Un validatore custom deve essere registrato nel file di configurazione di JSF dichiarandone la classe come segu
codiceFiscale it.mokabyte.validator.CodiceFiscaleValidator
e associadogli un identificatore che potrà essere poi utilizzato per referenziare il validatore nell‘applicazione.
Un validatore deve corrispondere ad una classe che implementa l‘interfaccia javax.faces.validator.Validator che definisce il metodo
public void validate(FacesContext context, UIComponent component,Object value)
throws ValidatorException;
nel quale inserire la logica di validazione desiderata.
Messaggi di errore
Le operazioni di conversione e validazione dei dati possono dar luogo a errori che devono essere opportunamente segnalati agli utenti. La gestione degli errori e della loro visualizzazione nell‘interfaccia è gestita completamente da JSF mediante i componenti HtmlMessage e HtmlMessages.
HtmlMessage visualizza un singolo messaggio di errore associato a un singolo componente.
HtmlMessages invece consente di visualizzare tutta la lista di messaggi di errore che si possono essere verificati durante l‘elaborazione della richiesta JSF.
Se ad esempio ho un form con numerosi campi, potrei pensare di associare ad ogni singolo campo di input il proprio messaggio di errore da visualizzare accanto ad esso. In questo caso posso utilizzare il componente HtmlMessage e il suo corrispondente tag come segue:
È possibile anche visualizzare una lista di tutti i messaggi di errore che il framework ha accodato durante l‘elaborazione della singola richiesta in un‘unica lista utilizzando il componente HtmlMessages e il suo corrispondente tag:
In questo modo viene visualizzata una lista completa dei messaggi di errore, non solo quelli generati dai componenti di input ma anche quelli impostati da codice a fronte di determinate situazioni da segnalare all‘utente. L‘impostazione dell‘attributo globalOnly al valore true consente di eliminare i primi dalla lista.
Applicazione di esempio
Abbiamo aggiunto all‘applicazione di esempio (scaricabile come allegato dal menu in alto a sinistra) alcuni elementi legati alla discussione teorica appena conclusa.
Nel form di login sono stati inseriti alcuni controlli sui campi di input utente e password in modo che siano entrambi obbligatori e di lunghezza compresa tra 6 e 12. In caso di errore di validazione è stata aggiunta la visualizzazione del messaggio di errore. Ecco la sezione di codice per il campo di input utente:
id="utente" title="utente" >
Nella pagina seguente a quella di login è stato aggiunto un campo di input nel quale va inserita una data per dimostrare l‘utilizzo di un converter per il controllo del formato del dato digitato.
Il converter utilizza un pattern per determinare il formato della data inserita dall‘utente e da visualizzare all‘utente.
L‘applicazione di esempio associata all‘articolo è stata testata su Apache Tomcat 5.5.25 (jsfIItomcat.war) e Jboss 4.2.1 – GA (jsfIIjboss.war)
Conclusioni
Convertitori e validatori sono un valido aiuto che JSF dà allo sviluppatore per consentirgli di svolgere operazioni ripetitive come la validazione e la conversione dei dati di interfaccia con il minimo sforzo e secondo una impostazione standard.
Fino ad ora nei nostri esempi abbiamo sempre inserito label nelle pagine e nel codice come costanti di tipo stringa. Nel prossimo articolo faremo fare un altro piccolo passo avanti all‘applicazione di esempio introducendo un argomento molto importante: l‘internazionalizzazione.
Riferimenti
[1] David Geary – Cay Horstmann, “Core Java Server Faces” – Sun Microsystems Press, 2004
[2] Bill Dudney – Jonathan Lehr – Bill Willis – LeRoy Mattingly, “Mastering Java Server Faces”, Wiley Publishing Inc., 2004
[3] Kito D. Mann, “Java Server Faces In Action”, Manning Pubblications Co., 2005
[4] Autori Vari, “The Java EE 5 Tutorial”, Sun Microsystems, Febbraio 2007
[5] The API Javadocs for the JavaServer TM Faces Specification
http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html
[6] Alfredo Larotonda, “JSF: Il nuovo volto dello sviluppo web – II Parte”, MokaByte 118, Maggio 2007