Dopo la serie introduttiva su JavaServer Faces, iniziamo una serie di articoli di approfondimento per esaminare più da vicino i vari aspetti di JSF in pratica. In questo primo articolo affrontiamo l‘importante argomento degli eventi.
Introduzione
La serie introduttiva dovrebbe aver messo in evidenza quali siano gli elementi su cui è fondata l‘architettura di JavaServer Faces. JSF è un framework orientato allo sviluppo di applicazioni web Java EE che indirizza principalmente la realizzazione del layer di interfaccia utente. Dal punto di vista dell‘interazione dell‘utente con l‘interfaccia dell‘applicazione, vengono mutuati nel mondo web concetti analoghi a quelli ben noti nell‘ambito dello sviluppo di applicazioni stand-alone, tipo Swing.
In JSF la gestione delle azioni che l‘utente compie sull‘interfaccia è gestita mediante un meccanismo a eventi. Le azioni eseguite dagli utenti sono rappresentate da Event Objects che mantengono l‘informazione sul tipo di evento generato e sul componente che ha dato origine all‘evento.
In JSF gli eventi rientrano in tre categorie, una delle quali in realtà non legata alle azioni utente sull‘interfaccia ma piuttosto al ciclo di vita di JSF:
Action Events
Eventi scatenati dai componenti che generano comandi quali command button e hyperlink.
Value Change Events
Eventi scatenati dai componenti di tipo input ai quali mediante l‘interfaccia si assegnano dei valori, quali text-box, radio button, checkbox etc.
Phase Events
Eventi associati non ad azioni dell‘utente ma al compimento delle varie fasi del ciclo di vita di gestione di una richiesta JSF
Tutti gli eventi generati da componenti dell‘interfaccia utente sono rappresentati da oggetti i cui tipi estendono la classe base
javax.faces.event.FacesEvent
che a sua volta estende la classe
java.util.EventObject.
Nella classe base sono molto importanti i metodi
public UIComponent getComponent()
che restituisce un riferimento al componente che ha generato l‘evento, e i metodi
public PhaseId getPhaseId()
public void setPhaseId(PhaseId phaseId)
che consentono di avere informazioni e di impostare il valore della fase in cui l‘evento va in esecuzione.
Per i Phase Events la classe base è invece
javax.faces.event.PhaseEvent
che non ha il metodo getComponent() poiché gli eventi di questa categoria non hanno origine da un componente di interfaccia, ma ha invece metodi che consentono di acquisire un riferimento alla fase di esecuzione e al FacesContext. In [5] si possono trovare tutti i dettagli sulle classi e le interfacce in questione.
Gli eventi vengono notificati ai rispettivi listener al termine della fase alla quale quell‘evento è associato. A seguito dell‘elaborazione dell‘evento è possibile che l‘intero ciclo di vita venga saltato e si proceda alla fase di RenderResponse.
A partire dalla fase Apply Request Values e per tutte le fasi seguenti del ciclo di vita fino alla InvokeApplication gli eventi possono essere creati e accodati per l‘elaborazione al termine della fase opportuna.
Gli Action Events
Gli action events sono generati dai componenti di tipo command, quindi command button ed hyperlink, ovvero quei componenti che estendono la classe javax.faces.component.UICommand come appunto HtmlCommandButton e Html CommandLink.
Questi componenti implementano l‘interfaccia javax.faces.component.ActionSource che come suggerito dal suo nome ci dice che essi possono essere “sorgenti” di eventi di tipo azione. Quando un utente clicca su un bottone o su un link della nostra pagina web, la sua azione dà origine in JSF a un oggetto di tipo javax.faces.event.ActionEvent.
Abbiamo quindi alcuni componenti che generano eventi e sappiamo quale sia il tipo dell‘evento generato. Quello che ci resta da vedere è come sia possibile gestire l‘evento. A questo pensano le classi Listener; il concetto di listener dovrebbe già essere chiaro non solo a chi conosce lo sviluppo Swing ma anche agli sviluppatori di applicazioni web Java EE che utilizzano listener quali ad esempio i ContextListener che stanno in ascolto sugli eventi di creazione e distruzione del contesto di una applicazione web.
In questo caso le classi che possono intercettare e gestire gli action events devono implementare l‘interfaccia
javax.faces.event.ActionListener
che definisce il metodo
public void proccessAction(ActionEvent evento)
la cui implementazione definisce la logica applicativa associata all‘evento fornito in input come parametro.
Ovviamente un listener deve registrarsi per poter intercettare gli eventi generati da un determinato componente, e quindi deve essere possibile associare il listener con il componente del quale si vogliono gestire gli eventi.
È possibile farlo direttamente nel codice della pagina JSP utilizzando il tag actionListener della libreria Core di JSF:
Il metodo processAction della classe listener, nel quale va inserita la logica di gestione dell‘evento, non ha legami con il navigation handler di JSF come si può notare anche dal tipo di ritorno che è void. Per cui gli action listener non influenzano la navigazione delle pagine ma possono essere usati per effettuare operazioni sull‘interfaccia utente quale il cambiamento di una proprietà o cose simili. Un action listener può essere registrato su di un componente anche utilizzando l‘attributo actionListener del command component come nell‘esempio seguente:
In questo caso utilizziamo il metodo
public void ascolta(ActionEvent evento)
inserito nel backing-bean della pagina come listener dell‘action event e non una classe dedicata, ma il meccanismo è lo stesso. La differenza è che è possibile definire un solo action listener method mentre invece si possono associare più action listener class ad un componente.
A questo punto ci si può chiedere cosa sia il metodo associato all‘attributo action del nostro command button che come sappiamo va in esecuzione anche esso quando il bottone viene cliccato e per di più influenza il flusso di navigazione interagendo con il navigation handler di JSF che ne interpreta il valore di ritorno.
Il framework registra un listener di default su tutti i componenti di tipo command dell‘applicazione che va in esecuzione nella fase Invoke Application e che esegue il metodo indicato nell‘attributo action del command stesso, per questo detto action method.
Ecco spiegato perché affinchà© un action method vada in esecuzione al click su un command component non sia necessario registrare alcun listener, ma indicare solo il binding opportuno nell‘attributo action.
I Value Change Events
I Value Change Events vengono generati al cambiamento di un valore di un componente di tipo input. Dal punto di vista concettuale il meccanismo di generazione e gestione di questi eventi è del tutto analogo ai precedenti , a meno dei nomi delle varie classi ed interfacce interessate.
In questo caso l‘evento è rappresentato da una istanza della classe javax.faces.ValueChangeEvent. I componenti che generano questa tipologia di eventi sono quelli che implementano l‘interfaccia
javax.faces.component.ValueHolder
quindi componenti ai quali è associato un valore inserito nell‘interfaccia utente.
I listener per questa tipologia di eventi implementano l‘interfaccia
javax.faces.event.ValueChangeListener
che definisce il metodo
void processValueChange(ValueChangeEvent event)
throws AbortProcessingException
nel quale le classi di implementazione devono inserire il codice di gestione dell‘evento.
Analogamente a prima è possibile inserire un value-change method nel backing-bean della pagina per gestire l‘evento oppure è possibile definire una classe listener e registrarla sul componente. Le due modalità sono indicate di seguito prendendo come esempio un input text:
Come per gli action event anche in questo caso è possibile registrare un solo value-change listener method ma più di una value-change listener class.
In genere è bene usare le classi listener sia per eventi action che value change quando il codice di gestione dell‘evento è riutilizzabile per più di un componente in modo da ottimizzare il codice e consentirne il riuso, altrimenti le due modalità sono equivalenti.
I PhaseEvent
A differenza dei precedenti , i phase event non vengono generati a seguito di azioni dell‘utente sull‘interfaccia dell‘applicazione, ma a fronte dell‘esecuzione delle varie fasi del ciclo di vita di gestione delle richieste di JSF [6].
Mentre con i precedenti eventi si ha a che fare sempre nello sviluppo di una applicazione JSF, con questi ultimi avviene più di rado anche se è bene conoscerli e sapere come utilizzarli.
I phase event sono rappresentati da istanze della classe javax.faces.event.PhaseEvent. Sono gestiti da classi listener che implementano l‘interfaccia javax.faces.event.PhaseEventListener.
Le istanze di queste classi listener vengono registrate per ascoltare gli eventi generati dalla classe che rappresenta il ciclo di vita di JSF, ovvero javax.faces.lifecycle.Lifecycle che mette a disposizione i metodi addPhaseListener(PhaseListener listener) e removePhaseListener(PhaseListener listener) per registrare e rimuovere un listener, getPhaseListeners() per acquisire un array contenente i riferimenti agli oggetti di tipo PhaseListener registrati.
Un utilizzo dei PhaseListener può essere ad esempio quello di inizializzare un backing-bean con dei valori di default prima della phase di RenderResponse , per supplire alla carenza di JSF di non avere un metodo che consenta di effettuare questa operazione prima che la vista venga visualizzata. In [3] c‘è un ottimo esempio di come eseguire questa inizializzazione.
Applicazione di esempio
Allo scopo di fornire anche dei semplici esempi pratici associati alla discussione teorica, alleghiamo delle applicazioni di esempio (scaricabili dal menu in alto a sinistra). All‘applicazione di esempio presentata nella serie precedente, sarà aggiunto a ogni articolo un elemento riguardante l‘argomento trattato. In questo caso abbiamo aggiunto una pagina JSP (output.jsp) nella quale la selezione di uno dei possibili valori in una list-box modifica il valore di una label della pagina.
Per fare ciò è stato sviluppato un value change listener che sta in ascolto sul cambiamento di valore selezionato nella lista e modifica di conseguenza il valore della label.
Il codice dell‘esempio è molto semplice. Vale la pena di sottolineare solo le seguenti particolarità :
Primo, solo i componenti di tipo command provocano l‘invio del form di una pagina. Affinchà© ciò avvenga anche quando modifichiamo il valore della nostra list-box è necessario associare questa operazione all‘evento onchange del componente.
Secondo, la gestione dell‘evento è fatta da una classe listener e non da un value change listener method del backing bean. Lo svantaggio è che in questo caso non abbiamo ha disposizione un riferimento agli altri componenti della pagina come sarebbe invece in un backing-bean avendo definito gli opportuni binding. Per acquisire un riferimento al componente HtmlOutputtext del quale vogliamo modificare il valore al cambiare del valore della list box dobbiamo allora acquisire il tree di componenti dal FacesContext ed acquisire il riferimento al componente desiderato con il metodo findComponent().
UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
HtmlOutputText outputText = (HtmlOutputText)root.findComponent("form1:outputText1");
L‘applicazione di esempio associate all‘articolo è stata testata su Apache Tomcat 5.5.25
Conclusioni
In questo articolo abbiamo iniziato ad approfondire un aspetto fondamentale di JSF, la gestione degli eventi. Il modello event-driven di JSF rende lo sviluppo web molto più simile allo sviluppo di applicazioni stand-alone e fornisce un astrazione di più alto livello rispetto ai framework tradizionali tipo Struts. Nei prossimi articoli faremo evolvere la nostra applicazione di esempio affrontando un altro tema fondamentale dello sviluppo web: la conversione e la validazione dei dati di interfaccia.
Ruferimenti
[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 Publications 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