Introduzione
Il ciclo di vita di una pagina Java Server Faces è simile a quello di una pagina JSP: Il client effettua una richiesta HTTP per la pagina, e il server risponde con la pagina tradotta in HTML. Tuttavia, a causa delle caratteristiche extra presenti in tale tecnologia, il ciclo di vita in JSF offre alcuni servizi aggiuntivi per l’elaborazione della pagina. Per chi intende sviluppare utilizzando tale tecnologia è quindi importante conoscerlo e sapere, ad esempio, quando avviene la validazione dell’input, la conversione o la gestione degli eventi.
Un pagina JSF viene rappresentata attraverso l’albero delle componenti che sono presenti nell’interfaccia utente, comunemente chiamata vista (view). Quando un client effettua una richiesta sulla pagina, JSF deve ricostruire la vista considerando lo stato precedentemente assunto dai vari componenti. A tale scopo è avviato un processo i cui step costituiscono appunto il ciclo di vita della pagina. Non tutti gli step del processo sono necessariamente eseguiti.
Processo Standard
Il processo standard si avvia a seguito di un evento su un componente JSF che sottomette una request all’applicazione: Faces Request. Al termine del processo l’applicazione generata una Faces Response, ovvero una nuova vista che sarà memorizzata nel FacesContext
.
Le request possono essere di due tipi: request iniziale (initial request) o postback. Nel primo caso l’utente esegue la request sulla pagina per la prima volta, viceversa nel secondo caso l’utente sottomette una form contenuta in una pagina precedentemente caricata nel browser a seguito di un altro postback o di una request iniziale. Quando JSF gestisce una request iniziale i soli step del lifecycle che sono eseguiti sono restore view e render response, in quanto non ci sono input utente o azioni da eseguire. Viceversa nel caso di un postback tutti gli step sono eseguiti. Di seguito sono descritte le varie fasi.
Restore View
Tale fase è la prima del ciclo di JSF ed è avviata nel momento in cui l’utente esegue un click su un link o un pulsante, generando una request. Durante questa fase JSF costruisce l’albero dei componenti della pagina, collega gli event handler ed i validatori ai componenti e salva la vista generata nel FacesContext
. Tale contesto contiene tutte le informazioni necessarie per processare la richiesta ed è accessibile ai componenti, agli event handler, ai validatori ed ai convertitori collegati alla vista. Si consideri ad esempio il seguente codice che implementa un validatore JSF:
1 2 3 4 5 6 7 |
@FacesValidator("urlValidator") public class UrlValidator implements Validator { @Override public void validate(FacesContext facesContext, UIComponent component, String value) throws ValidatorException { ... } } |
Le la richiesta è una initial request, JSF genera una vista vuota durante questa fase e procede verso la step render response. Diversamente se la richiesta è un postback, la vista è già presente nel contesto, JSF la recupera e la popola con le informazioni disponibili sul client e sul server.
Apply request Value
Dopo aver recuperato la vista dal contesto, per ogni componente appartenente all’albero della vista, JSF estrae dalla request i nuovi valori da essi assunti, attraverso l’invocazione del metodo decode sul componente. Il valore estratto viene memorizzato localmente sul componente. Sostanzialmente questo è quello che accade:
1 2 3 4 5 6 7 |
// input di tipo UIInput e request di tipo HttpServletRequest if (input.isRendered()) { String value = request.getParameter(input.getClientId()); if (value != null) { input.setSubmittedValue(value); } } |
Se un componente ha l’attributo immedite
valorizzata con true
, JSF nanticipa a questa fase JSF i processi di validazione, conversione e la gestione degli eventi. In caso di eccezione nella conversione un messaggio di errore viene generato ed accodato nel FacesContext,
per essere mostrato durante la fase di render response, insieme a tutti gli eventuali messaggi generati durante la fase di process validation (che quindi è comunque eseguita).
A questo punto, se durante il decode
o la gestione degli eventi viene invocato il metodo renderResponse
sul FacesContext
corrente, il processo salta direttamente alla fase di render response.
Se invece è eseguito un redirect verso una pagina fuori dal contesto delle risorse gestite da JSF o se la pagina non contiene alcun componente JSF è invocato il metodo FacesContext.responseComplete
.
Al termine di tale fase i componenti memorizzano i loro nuovi valori ed i messaggi ed eventi sono stati accodati.
Process Validation
Durante questa fase JSF processa tutti i validatori registrati sui componenti dell’albero della vista, utilizzando il metodo validate
di tali componenti. In tale fase è anche completata la conversione dei valori di input per tutti quei componenti che non hanno l’attributo immediate
valorizzato a true
. Se la validazione o la conversione fallisce un messaggio di errore viene generato ed accodato nel FacesContext,
ed il ciclo di vita avanza direttamente alla fase di render response dove la pagina è nuovamente mostrata all’utente con i messaggi di errore generati.
Se durante il validate
o la gestione degli eventi viene invocato il metodo renderResponse
sul FacesContext
corrente, il processo salta direttamente alla fase di render response.
Se invece è eseguito un redirect verso una pagina fuori dal contesto delle risorse gestite da JSF o se la pagina non contiene alcun componente JSF è invocato il metodo FacesContext.responseComplete
.
Update Model Value
Una volta stabilito che i valori provenienti dalla request è valido, JSF esamina tutti i componenti ed il valore memorizzato localmente nella prima fase viene trasferito sulle proprietà dei bean ad essi connessi per mezzo delle espressioni EL definite nell’attributo value
. Questo avviene tipicamente per i componenti UIInput
, ad esempio se abbiamo:
1 |
<h:inputText value="#{bean.property}" /> |
in tale fase è eseguito il codice:
1 |
bean.setProperty(input.getValue()); |
Se il valore non può essere convertito nel tipo specificato dalla proprietà del bean, il ciclo procede alla fase di render response ed un messaggio di errore è mostrato all’utente.
Se durante l’esecuzione del metodo updateModels
o la gestione degli eventi viene invocato il metodo renderResponse
sul FacesContext
corrente, il processo salta direttamente alla fase di render response.
Se invece è eseguito un redirect verso una pagina fuori dal contesto delle risorse gestite da JSF o se la pagina non contiene alcun componente JSF è invocato il metodo FacesContext.responseComplete
.
Invoke Application
Una volta che tutti i valori della request sono stati settati nei vari bean, gli eventi accodata in fase di apply request vengono processati. Tipicamente si tratta dell’evento di sottomissione della form.
A questo punto se è eseguito un redirect verso una pagina fuori dal contesto delle risorse gestite da JSF o se la pagina non contiene alcun componente JSF è invocato il metodo FacesContext.responseComplete
.
Render Response
Durante questa fase JSF delega al container/application server la renderizzazione della pagina JSP. Se la request è una initial request, i componenti sono inseriti nell’albero dei componenti mentre il JSP container esegue la pagina.
Se la request è un postback ed un errore si è generato durante le fasi di apply request, process validation o update model value, viene mostra la pagina originale con tutti i messaggi di errore accodati.
Al termine della renderizzazione, lo stato della response è memorizzato nel contesto per essere disponibile in una request successiva ed essere recuperata in una nuova fase di restore view.
Debugging
Il ciclo di vita JSF può essere tracciato utilizzando un FasesListener
a scopo di debug ma anche per eseguire codice in specifici momenti del processamento. Un semplice codice per il debugging è il seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package it.inspiredsoft; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; public class LifeCycleListener implements PhaseListener { public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } public void beforePhase(PhaseEvent event) { System.out.println("START PHASE " + event.getPhaseId()); } public void afterPhase(PhaseEvent event) { System.out.println("END PHASE " + event.getPhaseId()); } } |
faces-config.xml
:
1 2 3 |
<lifecycle> <phase-listener>it.inspiredsoft.LifeCycleListener</phase-listener> </lifecycle> |