RichFaces KeepAlive: come risolvere il dilemma di Amleto

Essere o non essere? Request o Session? Questo non è più un problema… Grazie a RichFaces abbiamo a disposizione la soluzione al dilemma shakespeariano.

Come sappiamo, la comunicazione tramite web è basata sul protocollo HTTP che di natura è senza stato, per cui ogni richiesta (request) è indipendente dalla precedente. I server web però implementano un meccanismo che permette di riconoscere se il client che chiama è sempre lo stesso, creando una sessione univoca (session) che può essere sfruttata per mantenere memoria della comunicazione.
Ad ogni richiesta il browser invia l’identificativo della sessione al server (memorizzato in un cookie) in modo da essere riconosciuto. Se l’intervallo tra due richieste non supera una certa soglia (solitamente 30 minuti), il client viene riconosciuto e può accedere a quelle informazioni legate alla propria sessione utente, altrimenti la sessione viene invalidata e tutti i dati in essa memorizzati vengono persi.

Il JSF, definendo gli scope, cioè il tempo di vita dei backing bean di una applicazione web, si basa proprio su questa classica infrastruttura: i backing bean in request vengono creati e distrutti ad ogni richiesta del client, mentre quelli in session rimangono attivi fino al timeout della sessione. Quest’ultimo comportamento può essere molto oneroso dal punto di vista di occupazione della memoria e della serializzazione sella sessione web in ambienti cluster, come già accennato in un altro post.

Con l’ausilio del framework RichFaces di JBoss abbiamo a disposizione un nuovo scope di tipo conversazionale, grazie all’annotazione @KeepAlive applicabile su un backing bean in request:

   @KeepAlive
   public class MyBackingBean implements Serializable {
   
        //My Code...
   }

Uno scope conversazionale applicato ad un backing bean configurato originariamente per stare in request ci permette di mantenere in vita il backing bean tra più richieste all’interno della stessa pagina e verrà eliminato solo quando la lasceremo. Per lo meno questo è quello che dice giustamente la documentazione, ma c’è da porre particolare attenzione. Ovviamente non è che RichFaces fa magie: semplicemente tiene in sessione un oggetto a cui aggancia i nostri backing bean, che decide poi di eliminare al cambio della vista. E’ proprio il “come” viene determinato il cambio della vista che può creare un po’ di confusione.

Per come è strutturata la navigazione del framework, i backing bean in KeepAlive vengono distrutti solo quando la navigation-rule del faces config, che ci permette di navigare tra le pagine tramite le action, è stata dichiarata <redirect/>, altrimenti il backing bean rimane agganciato alla sessione fino al timeout. Utilizzare il <redirect/> comporta però una doppia chiamata al server: ad ogni action, il client chiama il server con una chiamata POST, il quale, se prevede una navigazione, ritorna l’indirizzo verso cui dirottare il client (nel parametro ‘Location’ dell’header della response), che a sua volta effettuerà il fetch della nuova pagina con una nuova chiamata GET al server. Quest’ultimo tipo di chiamata fa comprendere al server che la conversazione sulla vista precedente è finita e invalida lo scope KeepAlive. Lo stesso risultato si ha se effettuo una chiamata ad una pagina tramite il tag <h:outputLink/>: si deduce quindi che il cambio della vista è percepito ad ogni chiamata di tipo GET verso una pagina. Ricordiamo che tutte le chiamate al server che richiedono una form in JSF effettuano una chiamata POST: l’unico tag che effettua una chiamata GET è <h:outputLink/> che però non chiama nessuna action e quindi non usa le regole di navigazione.

Piuttosto che cambiare le regole di navigazione ed introdurre il redirect, può essere conveniente attivare lo scope KeepAlive solo per le conversazioni Ajax:

   @KeepAlive(ajaxOnly = true)
   public class MyBackingBean implements Serializable {
   
        //My Code...
   }

In questo modo potremo fare affidamento sullo stato del backing bean durante conversazioni ajax ed avere al contempo la garanzia che questo venga distrutto non appena venga effettuata qualalsiasi chiamata non-Ajax al server, sia essa di GET o POST.

Conclusioni

L’annotazione KeepAlive di RichFaces è molto utile per chi lavora con le versioni del JSF inferiori alla 2.0. Nella nuova versione infatti è stato introdotto nativamente questo scope grazie all’annotazione @ViewScoped che ha lo stesso comportamento di KeepAlive: il backing bean viene mantenuto vivo finché rimaniamo nella stessa vista e viene invalidato solo a seguito di una chiamata GET ad una pagina.
Pensando alla semantica dei metodi forniti dal protocollo HTTP questo comportamento ha anche senso. Il metodo GET infatti serve per recuperare (appunto get) una risorsa dal server: chiamando una pagina con il metodo GET sto dicendo quindi al server che voglio accedere ad una risorsa che in questo caso è una pagina (che sia una nuova o sempre la stessa). Il metodo POST invece serve per inviare informazioni al server e non a caso è il metodo usato dalle action JSF: è questo il motivo per cui, nel caso in cui non si usi redirect nelle regole di navigazione, si verifica quel fastidioso sfasamento tra l’URL sulla barra degli indirizzi e il contenuto della pagina.

Andrea Como

Sono un software engineer focalizzato nella progettazione e sviluppo di applicazioni web in Java. Presso OmniaGroup ricopro il ruolo di Tech Leader sulle tecnologie legate alla piattaforma Java EE 5 (come WebSphere 7.0, EJB3, JPA 1 (EclipseLink), JSF 1.2 (Mojarra) e RichFaces 3) e Java EE 6 con JBoss AS 7, in particolare di CDI, JAX-RS, nonché di EJB 3.1, JPA2, JSF2 e RichFaces 4. Al momento mi occupo di ECM, in particolar modo sulla customizzazione di Alfresco 4 e sulla sua installazione con tecnologie da devops come Vagrant e Chef. In passato ho lavorato con la piattaforma alternativa alla enterprise per lo sviluppo web: Java SE 6, Tomcat 6, Hibernate 3 e Spring 2.5. Nei ritagli di tempo sviluppo siti web in PHP e ASP. Per maggiori informazioni consulta il mio curriculum pubblico. Follow me on Twitter - LinkedIn profile - Google+