Introduzione
In un precedente articolo abbiamo visto come Spring MVC permetta di gestire la coordinazione tra gli strati di controllo, di vista e il modello dati nell'ambito del pattern Model View Controller. Non offre nessun supporto però per gli aspetti più propriamente legati ai flussi operativi dell'applicazione. La gestione delle transizioni tra i vari stati del sistema nel suo ciclo di vita è demandata all'utilizzo degli strumenti standard del framework, e l'insieme di regole che le determinano è nascosta nei meandri della configurazione e della codifica di questi strumenti. Spring Web Flow sopperisce a questa mancanza mettendo a disposizione un'infrastruttura mirata alla gestione centralizzata e uniforme di questi aspetti. Permette di isolare i flussi applicativi in unità logiche soggette a determinate regole, di serializzare e far persistere su più richieste le istanze di esecuzione del flusso, che possono essere ripristinate con una chiamata specifica, e in determinati casi permette il riutilizzo del flusso stesso in differenti situazioni.
Concetti di base
Spring Web Flow permette di isolare un particolare flusso applicativo e di codificarlo attraverso una definizione di flusso. La definizione è normalmente rappresentata da un file XML, ma esiste anche la possibilità di utilizzare un'API Java apposita, che in questo articolo tuttavia non verrà trattata (per informazioni a questo riguardo vedere la guida di riferimento su http://static.springframework.org/spring-webflow/docs/current/reference/index.html).
Attraverso il file XML il framework è in grado di creare un'istanza della org.springframework.webflow.definition.FlowDefinition. I concetti di base nella definizione del flusso sono gli stati, nelle loro varianti, e le transizioni.
Gli stati rappresentano degli step nell'esecuzione del flusso l'accesso ai quali può causare l'esecuzione di codice applicativo e quindi modificare l'insieme di dati del sistema, e alla fine restituire un evento che viene interpretato dalle eventuali transizioni configurate all'interno di essi per passare a un altro stato.
Uno stato può contenere delle "entryActions" cioè delle action che vengono eseguite nella fase iniziale della vita dello stato stesso, e delle "exit-actions" che vengono eseguite nel momento nella fase finale, prima dell'uscita. Anche le transizioni possono contenere delle action, che permettono di eseguire del codice dopo il "matching" dell'evento ma prima che venga eseguita la transizione al nuovo stato.
Un flusso è costituito al minimo da uno StartState che rappresenta il punto iniziale del flusso stesso e opzionalmente da uno o più EndState che rappresentano il punto finale. Lo StartState in realtà si appoggia tramite un riferimento a una delle tipologie di stati disponibili, mentre l'EndState rappresenta esso stesso una tipologia.
Le tipologie di stati principali sono le seguenti:
- ViewState
- ActionState
- SubflowState
- EndState
View State
Il ViewState rappresenta uno stato che si occupa di segnalare al framework di controllo utilizzato, per esempio Spring MVC, di mandare a video una schermata di interazione utente e di attendere un input dell'utente stesso per effettuare una transizione a un altro stato. Il ViewState può contenere delle "actions" (render-actions), che vengono eseguite prima che venga restituita la "view".
Di seguito un esempio di definizione di un flusso (ai minimi termini):
<flow ...>
<start-state idref="start" />
<view-state id="start" view="startView">
<render-actions>
<action bean="startAction"/>
</render-actions>
<transition on="goOn" to="finish" />
</view-state>
<end-state id="finish" />
</flow>
Come si vede lo StartState è qui rappresentato da un ViewState legato a una "view" chiamata "startState". Uno strato di adattamento tra Spring Web Flow e il framework di controllo utilizzato permette di associare tale nome a una qualunque risorsa atta a renderizzare una schermata di interazione utente, per esempio una pagina JSP. Il view-state contiene poi una "render-action" la cui implementazione è rappresentata dal componente "startAction", e una transizione che viene eseguita nel momento in cui l'utente esegue una richiesta con l'evento denominato "goOn", e che causa in questo caso il passaggio all'end-state identificato da "finish".
Action State
L'ActionState, a differenza del ViewState, è una tipologia di stato che permette di eseguire del codice applicativo nel momento in cui vi si accede, ma non implica nessuna interazione con l'utente:
<action-state id="executeSearch"> <action bean="searchAction"/> <transition on="success" to="displayResults"/> </action-state>
L'action può essere definita come bean-action per invocare un metodo di un bean Spring:
<bean-action bean="searchService" method="executeSearch">
<method-arguments>
<argument expression="${flowScope.criteria}"/>
</method-arguments>
<method-result name="results"/>
</bean-action>
SubFlow State
Il SubflowState permette di invocare un altro flusso come sotto-flusso del flusso corrente:
<subflow-state id="cercaContatto" flow="agenda">
<attribute-mapper>
<input-mapper>
<mapping source="flowScope.idContatto" target="id"/>
</input-mapper>
</attribute-mapper>
<transition on="finish" to="dettaglioContatto"/>
</subflow-state>
È possibile passare dati al sotto-flusso tramite input-mapper:
<input-mapper> <input-attribute name="shipping"/> </input-mapper>
End State
L'EndState rappresenta lo stato finale del flusso. Può specificare una view o un redirect:
<end-state id="finish" view="flowRedirect:searchFlow"/>
È possibile restituire dati dal flusso tramite output-mapper:
<end-state id="finish">
<output-mapper>
<output-attribute name="datiContatto"/>
</output-mapper>
</end-state>
Esempio completo di subflow con input e output:
<subflow-state id="cercaContatto" flow="agenda">
<attribute-mapper>
<input-mapper>
<mapping source="flowScope.idContatto" target="id"/>
</input-mapper>
<output-mapper>
<output-attribute name="datiContatto"/>
</output-mapper>
</attribute-mapper>
<transition on="finish" to="dettaglioContatto"/>
</subflow-state>
Interazione con le view
Per interagire con il flusso dalle pagine JSP si utilizzano parametri specifici:
<a href="flowController.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=submit">Submit</a>
Per avviare un nuovo flusso:
<a href="phonebook.htm?_flowId=search-flow">Phonebook</a>
Conclusioni
Spring Web Flow offre un approccio strutturato e centralizzato per la gestione dei flussi applicativi, permettendo di separare la logica di navigazione dal codice applicativo. L'integrazione trasparente con Spring MVC e altri framework lo rende uno strumento potente per lo sviluppo di applicazioni web complesse.
