Skip to content

Spring Web Flow 2-parte-1

February 23, 2010

Le web application basate sul protocollo HTTP sono per loro natura stateless.

In alcune situazioni particolari, richieste da determinati tipi di problemi, si rende necessario avere “memoria” tra una interazione client-server e l’altra per poter realizzare una “macchina a stati”. Per fare questo, dobbiamo appoggiarci alla HttpSession, rendendo l’applicazione Stateful, ma dobbiamo pagare il prezzo di avere un applicazione meno leggera e scalabile (a meno di usare altri strumenti come Terracotta).

Per questo tipo di problemi possiamo usare Spring Web Flow, costruito su Spring MVC che fornisce:

  • Un dsl (domain-specific-language) per definire moduli con controller riusabili definiti flows
  • Un controller engine per gestire conversational state
  • Supporto per utilizzare Ajax per costruire rich user interfaces
  • Support per utilizzare le JavaServerFaces con Spring

SpringWebFlow si prefigge lo scopo di creare flussi riutilizzabili attraverso un flow definition language fatto da XML e Unified EL (JSP e JSF) e OGNL (Object-Graph Navigation Language), principalemente tramite configurazione XML, ma anche tramite classi Java. Con questi flussi possiamo costruire macchine “a stati”.

In questo articolo vedremo una trattazione non esaustiva della configurazione di webflow per la costruzione di flussi.

Flows

Vediamo le informazioni che abbiamo a disposizione nei flussi dichiarati nell’ xml.

  • currentEvent : Evento corrente, l’ ultimo processato dal flusso attivo
  • currentUser : Il Principal ovvero l’utente autenticato
  • externalContext : Il contesto “esterno” al flusso ovvero le API Servlet (Session, Request, ContextPath, etc) in cui il flusso vive. L’external Context è a tutti gli  effetti un Facade che fornisce al flusso l’accesso al mondo “esterno”.
  • flowExecutionContext : Fornisce l’accesso al Contesto (interno) di esecuzione del flusso.
  • flowExecutionUrl : Url relativo al contesto del flusso in esecuzione
  • flowRequestContext : Fornisce l’accesso alla istanza corrente della RequestContext
  • messageContext : Fornisce la possibilita’ di creare e recuperare messaggi dal flow execution (messaggi success o error)
  • resourceBundle : Fornisce l’acccsso al file di properties con i messaggio internazionalizzati
  • requestParameters : Fornisce l’accesso ai parameters presenti nella Request

Scopes

Vediamo ora le diverse visibilità fornite

  • flow : Questo scope inizia con l’avvio del flusso e viene distrutto al termine del flusso, per essere utilizzati nella definizione xml gli oggetti devono implementare java.io.Serializable
  • view : Questo scope visibilità inizia con l’avvio di uno view-state del flusso e viene distrutto al termine del view-state, per essere utilizzati nella definizione xml di questo scope gli oggetti devono implementare java.io.Serializable
  • request : Questo scope inizia quando il flusso viene creato e la request viene salvata in questo scope. Viene distrutta quando il flusso restituisce l’elaborazione a seguito della richiesta.
  • flash : Questo scope viene creato all’ inizio del flusso e viene svuotato dopo ogni view rendering, per essere utilizzati nella definizione xml di questo scope gli oggetti devono implementare java.io.Serializable
  • conversation : Inizia con l’avvio del flusso e viene distrutto al termine. Ha visibilità anche nei sottoflussi, e questa è la diferenza che lo caraterizza dal flow scope

Flow States

Un flusso può trovarsi in vari stati, predefiniti attraverso la configurazione xml (o da codice java)

  • start-state : Può essere definito esplicitamente o implicitamente uno stato di avvio. Se non definiamo esplicitamente, il primo state definito nel flusso viene considerato start-state. Altrimenti esplicitamente lo definiamo in questo modo:
    <flow start-state="myState"/>
  • action-state : Un action state permette l’esecuzione di logica di business. Si possono utilizzare due approcci, utilizzando il tag action-state oppure con il tag evaluate dentro un view-state. Il tag evaluate ha tre campi
  1. expression: permette la valutazione di un metodo su un bean accessibile dal flusso
  2. result: il risultato dell’ evaluate può essere assegnato ad una variabile con uno scope a piacimento
  3. resultType : il tipo del risultato
<evaluate expression="myService.findMyObjById(id)" result="flowScope.myObj"/>

L’ evaluate tag a sua volta può essere utilizzato in vari eventi del flusso:

– All’ avvio del flusso

<on-start><evaluate expression="issueService.findById(id, true)" result="flowScope.issue"/></on-start>

– All’ ingresso in uno stato

<view-state …><on-entry><evaluate…></on-entry></view-state>

– Prima del rendering

<view-state …><on-render><evaluate …></on-render></view-state>

– Al momento della esecuzione di una transizione:

<transition on="store" to="issueStore"><evaluate expression="persistenceContext.persist(issue)"/></transition>

– All’uscita da uno stato:

<view-state …><on-exit><evaluate…></on-exit></view-state>

– Alla fine di un flusso

<on-end><evaluate expression="itemInformationService.enrich(issue)"/></on-end>
  • view-state : Nel view state vengono mostrati dei template

id : identificativo dello stato

parent : (Opzionale) Se lo stato eredita da un altro

view : (Opzionale) Nome del view file, se omesso viene usato l’id come nome logico, sono possibili ridirezioni a file o indirizzi esterni

redirect : (Opzionale) Viene effettuata una ridirezione prima del rendering

popup : (Opzionale) Viene mostrato il rendering in un popup

model : (Opzionale) Indica il modello che deve essere bindato sulla view

  • decision-state : Un decision state è come un action state ma con la possibilità di utilizzare un if-then-else
<decision-state id="moreAnswersNeeded"><if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish"/></decision-state>
  • subflow-state : Permetto di richiamare un flusso come sottoflusso
<subflow-state id="addGuest" subflow="createGuest"><transition on="guestCreated" to="reviewBooking"><evaluate expression="booking.guests.add(currentEvent.attributes.guest)"/></transition><transition on="creationCancelled" to="reviewBooking"/></subflow-state>
  • end-state : Rappresenta la fine del flusso e può restituire un output oppure mostrare una view
<end-state id="objStore" commit="true"><output name="name" value="myObj.name"/></end-state>
<end-state id="issueStore" commit="true" view="externalRedirect:contextRelative:/myflow/obj/all"/>

Transition

Una transizione avviene quando a seguito di un evento si passa da uno stato ad un altro.Una transizione avviene in un view-state o in un action-state.

I parametri utilizzabili nella configurazione della transizione sono:

bind : Disponibile nel view-state, se è a true viene effettuato il binding e la validazione prima della transizione, se non viene fatto il binding non viene fatta neppure la validazione. Il valore di default è true

history : Disponibile nel view-state, opzionale, decide come deve essere il comportamento quando viene usato il tasto back, ci sono tre opzioni:

  1. preserve (default) : Dopo la transizione è possibile ritornare allo stato corrente
  2. discard : Dopo la transizione non è possibile ritornare allo stato corrente
  3. invalidate : Dopo la transizione non è possibile ritornare a nessun view-state precedente

on : Disponibile in action e view-state, è il nome del risultato che scatena la transizione, è una campo solo testo

on-exception : L’eccezione (fully qualified name) che scatena l’esecuzione della transizione

to : Il nome dello stato che viene raggiunto

Validation

Nel view state abbiamo visto che è possibile usare il tag model che causa il binding e la validazione di un nostro oggetto di dominio. Per effettuare la validazione abbiamo a disposizione due soluzioni, implementare un metodo che si chiami validate${state}(MessageContext context) dentro il nostro model object oppure creare una classe di validazione annotata come @Component e con il nome del metodo sempre come  validate${state} e con parametri in ingresso il nostro Object model e il MessageContext

Global transition

Nella configurazione è possibile definire delle transizioni globali, visibili in qualunque punto della configurazione

<global-transitions><transition on="login" to="login"><transition on="logout" to="logout"></global-transitions>

On-end

Alla fine di un flusso è possibile compiere delle azioni

ExceptionHandler

Possiamo definire le transizioni da effettuare in caso di eccezioni, specificando il fully qualified name

<transition on-exception="java.lang.NumberFormatException" to="add" />

Input/Output

All’ avvio di un flusso possiamo avere dei valori di input, ad esempio se il flow viene fatto partire da un url come il seguente

<a href="/myapp/myflow/issue/add?id=${issue.id}"/>

possiamo scrivere nella configurazione specificando il tipo in cui vogliamo sia convertito

<input name="id" type="long"/>

oppure recuperandolo da un determinato scope

<input name="id" value="flowScope.issue.id"/>

In output invece

<output name="id" />

In output invece <output name=”id” />

Variabili

Possiamo definire delle variabili a cui ci possiamo riferire in qualsiasi parte del flusso

<var name=”user” class=”com.myapp.User”/>

Flow Execution Listener

Possiamo predisporre dei listener sul flusso, solitamente sono quelli per la sicurezza e per la persistenza

<flow-execution-listeners><listener ref="securityFlowExecutionListener"/>
<listener ref="jpaFlowExecutionListener"/></flow-execution-listeners>

<bean id="jpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />
<constructor-arg ref="transactionManager" />
</bean>

<bean id="securityFlowExecutionListener"
class="org.springframework.webflow.security.SecurityFlowExecutionListener"/>


Conclusioni

Abbiamo visto come tramite la configurazaione xml possiamo dichiarare i nostri flussi, nell’articolo  successivo vedremo come realizzare delle azioni Create Read Update e Delete con una  applicazione di esempio, utilizzando la configurazione vista finora.

Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: