I framework presenti nel mondo Open Source si preoccupano di mettere a disposizione dello sviluppatore una serie di servizi standard. Tra questi non figura però normalmente alcun prodotto specificamente finalizzato alla gestione dei flussi applicativi. Spring Web Flow sopperisce a questa mancanza e permette di codificare e configurare i flussi in modo centralizzato e armonico rispetto agli altri aspetti del sistema.
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 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):
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:
La action “searchAction” rappresenta il comportamento applicativo eseguito dallo stato e la transizione in questo caso, in risposta all‘evento success restituito dalla action, rimanda a un view-state chiamato “displayResults”. Se utilizziamo Spring MVC tale componente sarà un bean configurato nel contesto di Spring e sarà disponibile a SWF attraverso la bean-factory di Spring stesso (vale anche per la render-action dell‘esempio precedente). L‘unica richiesta è che la classe implementi l‘interfaccia org.springframework.webflow.execution.Action. L‘interfaccia prevede un metodo execute che riceve in ingresso un oggetto RequestContext attraverso il quale la action stessa ha accesso ai dati di input necessari, e l‘implementazione si preoccuperà di rendere disponibili i dati di uscita nel contesto del flusso (flow-scope). È anche possibile configurare dei bean non aderenti a nessuna specifica interfaccia, in tal caso la configurazione sarebbe del tipo:
SubFlow State
Il subflow-state permette invece di eseguire all‘interno del flusso corrente un altro flusso come sottoflusso:
Il flusso agenda per utilizzare i dati di input deve definire un input-mapper corrispondente come di seguito riportato:
End State
L‘EndState rappresenta la fase conclusiva nel ciclo di vita del flusso e non può rimandare a stati precedenti, però può operare una redirezione verso una nuova esecuzione dello stesso o di un altro flusso:
Può anche fare una redirezione verso un URL esterno.
Inoltre permette di definire attraverso l‘elemento output-mapper i dati da restituire a un eventuale flusso chiamante, nel caso che venga eseguito come sottoflusso da quest‘ultimo:
Il flusso chiamante a sua volta può raccogliere i dati restituiti dal sottoflusso definento un elemento output-mapper nel subflow-state:
Esecuzione, repositories e contesti di visibilità
A partire da una definizione di flusso è possibile eseguire più istanze dello stesso. Una singola esecuzione implica una serie di interazioni con l‘utente, che attraversa più richieste. Possono esistere più richieste in parallelo, anche per lo stesso utente (più finestre del browser aperte). Il sistema è in grado di identificare un‘istanza di esecuzione e ogni singolo step all‘interno di essa. Inoltre è in grado di salvare su un apposito repository l‘istanza di esecuzione ogni volta che si accede a un view-state, il quale implica uno stato di pausa in attesa di un input dell‘utente. Quando l‘utente effettua una richiesta non fa altro che lanciare un evento che attraverso il matching di una transizione configurata provoca il ripristino dal repository dell‘esecuzione del flusso a partire dallo step in cui era stato precedentemente salvato. Per identificare precisamente l‘esecuzione di un flusso SWF utilizza un identificatore costituito da due parti: una che identifica l‘esecuzione del flusso nel suo complesso e l‘altra lo step corrente di esecuzione. Dal punto di vista dello sviluppatore tale identificatore è rappresentato dal nome “_flowExecutionKey”. Di seguito un esempio di chiamata con l‘utilizzo del flowExecutionKey:
Nel frammento mostrato sopra viene rimandato indietro nella richiesta il _flowExecutionKey corrente, recuperato dalla richiesta e viene scatenato un evento submit tramite il parametro _eventId. In questo modo viene ripristinata dal repository l‘esecuzione del flusso a partire dallo step corrente e l‘esecuzione prosegue in base all‘interpretazione dell‘evento submit da parte delle transizioni configurate. Per lanciare una nuova esecuzione di un flusso a partire dalla fase iniziale è invece necessario inviare al server una richiesta del tipo:
Phonebook
Dove _flowId è il parametro che identifica la definizione del flusso. Il sistema utilizza in questo caso il valore “search-flow” per identificare il flusso omonimo e lanciarne una nuova esecuzione. L‘esecuzione del flusso mantiene poi dei contesti con diverse caratteristiche di visibilità che permettono di accedere a una serie di informazioni sulla stato di esecuzione e di memorizzare informazioni e risorse specifiche, come per esempio parametri di input necessari all‘esecuzione di un sottoflusso. I contesti di visibilità esistenti sono i seguenti:
- Request
- Flash
- Flow
- Conversation
La visibilità “Request” ha un ciclo di vita che è quello di una singola richiesta. Quella “Flash” ha termine nel momento in cui viene lanciata una richiesta con un evento associato. La visibilità “Flow” ha un ciclo di vita che può essere temporaneamente interrotto da esecuzioni di sottoflussi. SWF mantiene uno stack di sessioni di flusso, e il flusso corrente è sempre quello in cima allo stack. In questo modo quando viene eseguito un sottoflusso, la sessione del flusso padre viene momentaneamente interrotta e si ha accesso solo alla sessione del sottoflusso correntemente in esecuzione, che si trova in cima allo stack. Quando il sottoflusso termina viene ripristinata la sessione del flusso chiamante. Questo implica che durante l‘esecuzione del sottoflusso non è possibile accedere a risorse e informazioni del chiamante attraverso il contesto di visibilità flow ma solo a quelle del sottoflusso stesso. L‘unico modo di accedere a informazioni e risorse condivise è utilizzare il contesto di visibilità “Conversation” che copre l‘intera esecuzione del flusso.
Conclusioni
Spring Web Flow fornisce delle funzionalità potenti e si interfaccia senza particolari sforzi con i più noti framework di controllo. Tuttavia potrebbe sarebbe auspicabile una maggiore cura per gli aspetti riguardanti il riutilizzo dei flussi. All‘atto pratico succede spesso di trovarsi di fronte a flussi applicativi che logicamente si differenziano solamente per pochi dettagli, ma SWF per ora non ha dei meccanismi di templating o di ereditarietà che sfruttino al meglio tali situazioni. Insomma il riutilizzo è possibile col minimo sforzo e in maniera trasparente solo in casi specifici.
Riferimenti
[1] “Spring Web Flow Reference”
http://static.springframework.org/spring-webflow/docs/current/reference/index.html
[2] “Spring Reference” 2.0
http://www.springframework.org/
[3] Craig Walls – Ryan Breidenbach, “Spring in Action”, Manning, 2005