Posts tagged: spring web flow

SWF: Model Data Validation

spring-webflowDopo aver visto come SWF realizza il mapping tra i dati di un modello e i campi di un form, vediamo ora come creare il meccanismo di validazione che consente di verificare il valore di ciascun campo inserito all’interno di un form. In questo articolo abbiamo visto come viene realizzata il binding dei dati e teniamolo come punto di partenza per iniziare a vedere il meccanismo della validazione.
Riprendiamo il codice sorgente del file di configurazione del flow e la pagina HTML all’interno della quale è presente il form:

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Bene. Come già ampiamente discusso, il flow definito dal file XML di sopra è molto semplice. Abbiamo una sola transizione da uno stato all’altro, ovvero si naviga da una pagina ad un altra pagina. Supponiamo ora, di voler validare i dati del form della pagina di partenza, e spostarsi nella pagina di arrivo, solo se non esistono errori di validazione secondo il nostro personale schema di validazione. Il nostro schema, prevede per semplicità che almeno uno dei tre campi presenti nel form sia indicato, e che se indicato il numero del volo questi dovrà essere un valore numero e eventuali caratteri alfabetici verranno rigettati.

Iniziamo con la modifica del file XML di definizione del flow:

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" >
          <action method="bindAndValidate" bean="flightAction" />
      </transition>
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

Il metodo bindAndValidate in sostanza richiama la nostra classe validator, la quale effettua i controlli che andremo a definire. Da notare che il riferimento nel tag action, punta ad un metodo all’interno della nostra classe Action che in realtà non esiste, ma che viene ereditato dalla superclasse FormAction. Definendo all’interno del file XML di definizione del bean il riferimento alla nostra classe Validatore, autormaticamente verrà richiamata all’invocazione del metodo bindAndValidate. Il codice della classe che si occupa di effettuare la validazione è il seguente: 

package it.devme.flight;
 
public class FlightValidator implements Validator {
 
	public boolean supports(Class clazz) {
		return clazz.equals(Flight.class);
	}
 
	public void validate(Object target, Errors errors) {
                Flight flight = (Flight) target;
                if (flight.getNumber()==0 && flight.getTime()==null && flight.getGate.equals("")) {
                    errors.rejectValue("gate", "Attenzione, &egrave; necessario specificare almeno 1 tra: numero del volo, ora di partenza, Gate");
                }
 
                if (errors.getErrorCount()==0) {
                    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "gate", "Il campo gate non pu&ograve; essere vuoto.");
                }
	}
 
}

Vediamo cosa è stato fatto. Intanto osserviamo che FlightValidator implementa la classe Validator, quindi definisce alcuni metodi essenziali che sono supports e validate. Il primo assicura il supporto al Bean indicato al suo interno. Il secondo implmenta la logica di validazione. Nel nostro caso vogliamo che almeno uno dei tre campi venga inserito, e che il campo Gate sia diverso da vuoto. Il primo controllo verifica la prima delle nostre condizioni; il secondo if, verifica prima che non ci siano stati errori in precedenza, e in caso positivo effettua il controllo di validazione sul campo Gate.

Vediamo ora come modifichiamo il file HTML per presentare il messaggio di errore:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
         <td><form:errors path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Per semplicità il nostro messaggio di errore comparirà sempre nello stesso posto, ovvero sotto il campo Gate. Così nel primo caso di errore, darà contezza del fatto che almeno uno dei campi di precedenti deve essere indicato, mentre nel secondo caso, sarà indicativo del campo stesso. Come vedete, l’aggiunta del tag <form:errors/> fa in modo di collegare eventuali codici di errori con il campo presentato in pagina.

Alla prossima. Stay tuned!

SWF: Model Data Binding

spring-webflowNell’ultimo articolo su Spring Web Flow ho condensato la maggior parte dei concetti chiave sull’argomento. Mi piacerebbe ora tornare indietro su alcune cose cercando di porre maggiore attenzione. In questo articolo vedremo un pò più da vicino come avviene il binding dei dati in SWF, da un form HTML verso un bean.
In SWF esiste un controller, dello stesso genere di quelli utilizzati in Spring MVC (es. AbstractFormController), che permette di effettuare il binding dei parametri corrispondenti ai campi di un form HTML, quindi presente nella request HTTP verso un command object rappresentato da una semplicissima classe Java Bean. Ad una certa view è associato un certo model object sul quale verranno memorizzati i valori dei parametri del form e affinché avvenga in binding è sufficiente dichiarare l’attributo model dell’elemento <view-state>. L’esempio che segue mostra come deve essere definito il tutto nel contesto SWF affinché avvenga correttamente il binding.

Si comincia col definire la classe del nostro Java Bean, suppioniamo di voler memorizzare le informazioni riguardanti i voli in partenza da un certo aeroporto.

package it.devme.flight;
 
import java.io.Serializable;
import java.sql.Time;
 
public class Flight implements Serializable {
 
        private static final long serialVersionUID = 1L;
 
        private String number;
        private Time time;
	private String gate;
	private boolean isDelayed;
 
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public Time getTime() {
		return time;
	}
	public void setTime(Time time) {
		this.time = time;
	}
	public String getGate() {
		return gate;
	}
	public void setGate(String gate) {
		this.gate = gate;
	}
	public boolean isDelayed() {
		return isDelayed;
	}
	public void setDelayed(boolean isDelayed) {
		this.isDelayed = isDelayed;
	}
 
}

Fin qui niente di più semplice. Procediamo ora con il nostro flow. Il flow consiste di 2 view. La prima pagina raccoglie i dati che verranno memorizzati all’interno dei 4 attributi della classe Flight. Avanzando con il bottone next, la seconda pagina semplicemente mostrerà i dati raccolti.

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

All’inizio del flow creiamo la variabile di flow flight la quale è un istanza della classe Flight. Fissando questa variabile come attributo model nella start view, diciamo a SWF di utilizzare questo oggetto per effettuare il binding dei dati. Implementando una semplicissima pagina HTML con un form chiudiamo il primo cerchio.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Cliccando sul Next ci spostiamo verso la seconda view definita all’interno del flow attraverso la transition confirm. All’interno di questa transition i valori dei campi del form vengono inviati in POST e automaticamente ne viene fatto il bind verso l’oggetto flight presente nel flow scope. Successivamente, durante il caricamento della view confirm, vengono eseguite le espressioni EL rispetto al valore dei campi del bean, consentendoci quindi di visualizzarne il valore.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>Flight Information Details</title>
  </head>
 
<body>
 
  <h1>Flight Information Details</h1>
  <p>Dettagli del volo:</p>
  <form:form modelAttribute="flight" >
 
    <table>
      <tr>
        <td>Numero volo: </td>
        <td>${flight.number}</td>
      </tr>
 
      <tr>
       <td>Orario: </td>
       <td>${flight.time}</td>
      </tr>
 
      <tr>
       <td>Gate: </td>
       <td>${flight.gate}</td>
      </tr>
 
      <tr>
       <td>Ritardo: </td>
       <td>${flight.isDelayed}</td>
      </tr>
 
   </table>
 </form:form>
</body>
</html>

Come vedete è molto semplice. Una piccola nota: se per qualunque ragione non si volesse proseguire durante una transition, al binding dei parametri della request è sufficiente aggiungere l’attributo bind all’interno del tag transition indicando come valore false.

    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
      <transition on="cancel" to="end" bind="false" />
    </view-state>

Nel prossimo articolo parleremo della validazione dei campi di un form. Alla prossima
Stay tuned!

Spring Web Flow tutto d’un fiato ! [Part 2/2]

spring-webflowRieccoci qui per continuare il discorso iniziato nel post precedente su SFW, il framework per la creazione di applicazioni web. Dopo aver definito gli elementi facenti parte della definizione di un flusso (flow) vediamo come è possibile definirlo, attraverso la sua specifica XML. Come detto in precedenza, il flow ha inizio grazie al flow executor un componente interno di SWF. L’executor da inizio al flow a partire dallo stato configurato come attributo nel tag root della definizione del flow:

<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
      http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
      start-start="loginState">
	<-- Add your definition here -->
</flow>

loginState rappresenta lo stato iniziale da cui avrà inizio l’esecuzione del flow. Come già detto gli stati sono gli elementi di base della definizione di un flow, in cui è possibile transire da uno stato ad un’altro effettuando delle operazioni, dei controlli o visualizzare dell’output. Tra i possibili stati che possono essere utilizzati vi è il View State, il quale viene utilizzato per visualizzare o chiedere informazioni agli utenti. Una view può essere una qualunque pagina JSP, ma può anche essere una qualunque view supportata da Spring MVC. Di seguito viene riassunto l’elenco di possibili elementi figli del view state:

  • <attribute>, dichiara un attributo che descrive lo stato.
  • <binder>, usato per configurare un custom form binding.
  • <exception-handler>, riferisce un bean che implementa un FlowExecutionExceptionHandler che gestisce tutte le eccezioni sollevate nello stato corrente.
  • <on-entry>, indica quali azioni devono essere eseguiti quando si entra in questo stato.
  • <on-exit>,  indica quali azioni devono essere eseguiti quando si esce da questo stato.
  • <on-render>. indica quali azioni devono essere eseguiti quando si visualizza questo stato.
  • <secured>, viene utilizzato assieme a Spring Security per restringere l’accesso a questo stato.
  • <transition>, definisce un percorso da questo stato ad un’altro basato sul verificarsi di un evento o un’eccezione.
  • <var>, dichiara una variabile.

Ciò detto, il modo più semplice per dichiarare un View State è il seguente:

<view-state id="login"/>

Il tag di sopra definisce una view logica, ovvero non associata a nessuna pagina fisica, da cui non sarà possibile transire in nessun’altro stato. Per associare la view, il nome logico definito dall’attributo id ad una pagina jsp è necessario aggiungere l’attributo view,

<view-state id="login" view="loginForm"/>

in questo modo la view è associata alla pagina loginForm.jsp, la quale verrà visualizzata al caricamento della view. Nota: sarà possibile evitare di dover specificare l’estensione dei file associati alle view, configurando opportunamente Spring MVC View Resolver.

Ma vediamo un’esempio un pò più completo:

<view-state id="login" view="loginForm">
  <transition on="accountEntered" to="homeUser" />
  <transition to="endState" on="cancel" />
</view-state>

l’esempio di sopra definisce due elementi figli del tag view, elementi transition. Il primo dei due indica una transizione dallo stato corrente allo stato homeUser, al verificarsi incondizionato dell’evento accountEntered. Il secondo definisce una transizione dallo stato corrente allo stato endState al verificarsi dell’evento cancel. Come definiamo questo tipo di eventi ? Molto semplicemente….lo scatenarsi degli eventi avviene direttamente dalle view fisiche associate. Consideriamo la view loginForm.jsp associata alla view logica login come segue:

<form:form>
   <!-- HTML form definition here -->
   <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
   <input type="submit" name="_eventId_accountEntered" value="Entra" />
   <input type="submit" name="_eventId_cancel" value="Annulla" />
</form:form>

la pagina web contiene tra gli altri elementi che ospiteranno i dati di accesso dell’utente, il pulsante di submit della pagina e un campo hidden. Quest’ultimo è richiesto per riconoscere l’id del flow in cui ci si trova ed è sempre necessario. I pulsanti di submit, oltre al loro classico funzionamento servono anche a scatenare gli eventi che abbiamo definito nel file di flow. Si noti il nome del pulsante submit Entra che invia i dati del form al server e serve per effettuare l’autenticazione dell’utente. In particolare il nome del pulsante ha un prefisso _eventId_ il quale indica a SWF che alla pressione del bottone dovrà scatenare un evento il cui nome è ciò che segue il prefisso, nel nostro caso accountEntered. In questo modo è possibile catturare l’evento nel file di definizione del flow. Stesso discorso vale per il bottone cancel.
E’ possibile anche scatenare un’evento attraverso un link, ad esempio:

<a href="${flowExecutionUrl}&_eventId=accountEntered">Entra</a>

Altro modo consiste nel creare un acampo hidden all’interno del form con name _eventId.

Dopo aver analizzato il ViewState che permette all’utente di essere coinvolto all’interno del flow, passiamo a vedere l’ActionState il quale definisce alcuni elementi che permettono di effettuare delle elaborazioni all’interno del flow. L’elenco dei nodi figli dell’action state è il seguente:

  • <attribute>, dichiara un attributo che descrive lo stato
  • <evaluate>, valuta un’espressione assegnando opzionalmente il risultato.
  • <exception-handler>, referenzia un bean che implementa FlowExecutionExceptionHandler il quale gestisce le eventuali eccezioni per lo stato corrente.
  • <on-entry>, definisce un’azione la quale viene eseguita all’ingresso dello stato.
  • <on-exit>, definisce un’azione la quale viene eseguita all’uscita dello stato.
  • <render>, richiede che la prossima view renderizzi un frammento di contenuto.
  • <secured>, restringe l’accesso allo stato corrente richiedendo l’inserimento degli attributi utente.
  • <set>, imposta una variabile all’interno dello scope del flow.
  • <transition>, definisce una transizione verso uno stato al verificarsi di un evento. Lo stato di destinazione può coincidere con lo stato corrente.

Quindi vediamo un’esempio d’uso dell’ActionState:

<action-state id="homePage">
	<evaluate expression="devME.authenticateUser(username, password)" />
	<transition to="home" on="userOK" />
	<transition to="loginForm" on="userKO" />
</action-state>

Il codice di sopra definisce il seguente comportamento. Dopo che l’utente ha inserito i propri dati di accesso all’interno del form e clicca sul pulsante Entra viene scatenato l’evento accountEntered, il quale prevede una transizione nello stato homeUser, un’ActionState. All’interno viene valutata un’espressione la quale esegue un metodo di autenticazione dell’utente sulla base dei dati forniti. Lo stesso metodo scatena 2 eventi diversi: userOK se l’utente è stato autenticato, dal quale si transisce verso la home dell’utente. L’evento userKO se l’utente non è stato autenticato e quindi si ritorna alla view di login. Come vedete è molto semplice e intuitivo.
Stay tuned.

Spring Web Flow tutto d’un fiato ! [Part 1/2]

spring-webflowSpring Web Flow è un framework basato su Spring MVC che permette lo sviluppo di applicazioni flow-based. In questo articolo vedremo come aggiungere Spring Web Flow ad applicazioni Spring-based e definire alcuni flussi tra le applicazioni e i loro utenti.
Iniziamo col dire che tutti i flussi (da ora in avanti li chiameremo con loro vero nome, flow) sono costituiti da 3 elementi base, ovvero:

  • States
  • Transitions
  • Flow data

States: indica ciò che accade all’interno di un flow. Spring Web Flow definisce 5 tipi di States differenti: View, Active, End, Subflow, Decision. Vedremo più avanti come questi tipi di stato partecipano alla creazione di un flow.
Transitions: sono di fatto il mezzo di collegamento tra i vari States. Una View state, può contenere al suo interno un certo numero di transizioni ciascuna delle quali collega altri stati.
Flow data: all’interno di un flow vengono collezionati alcuni dati che dipendono dagli stati in cui il flow si trova o si trovava. La visibilità dei dati di ciascun flow dipende dallo scope con cui questo viene definito. Per la precisione esistono 5 tipi di scope differenti in SWF che sono:
 

  • Flow scope : viene creato all’inizio del flow e distrutto quando il flow termina. I dati all’interno del Flow scope sono accessibili e disponibili per tutta la durata del flow
  • Conversation scope : viene creato all’inizio di un top-level flow e distrutto quanto il flow termina. Il conversation scope è simile al flow scope, eccetto per l’accesso ai dati. Infatti mentre il flow scope è accessibile solo dal flow che l’ha creato, il conversation flow può essere acceduto sia dal top-level flow che da tutti i suoi subflow.
  • Request scope : viene creato all’inizio di una richiesta HTTP e distrutto al termine di tale richiesta. I dati all’interno del request scope sono disponibili a tutti gli stati del flow per l’intera durata della richiesta.
  • Flash scope : viene creato quando il flow ha inizio, ripulito quando si entra all’interno di una view e distrutto alla fine del flow. I dati all’interno del flash scope sono disponibili a tutti gli states per l’intera durata del flow.
  • View scope : viene creato quando il flow entra all’interno di una view e distrutto quando la view viene renderizzata. E’ il flow che ha più breve durata, per questo motivo i dati all’interno di questo scope sono disponibili solo alla view che li ha creati.

Ciò detto vediamo come fare ad installare ed utilizzare SFW in pochi semplice passi. Naturalmente per poter iniziare a lavorare con SWF è necessario scaricare i jar che lo contengono, questa pagina costituisce il punto di partenza da cui potete trovare info dettagliate sul framework e dal link download potete scaricare le librerie. Le librerie necessario da aggiungere al classpath della propria applicazione sono :

  • org.springframework.WebFlow-2.0.8.RELEASE.jar
  • org.springframework.binding-2.0.8.RELEASE.jar
  • org.springframework.faces-2.0.8.RELEASE.jar
  • org.springframework.js-2.0.8.RELEASE.jar

in realtà WebFlow e Binding sono le librerie principali, mentre faces e js mettono a disposizione il supporto a JSF e Javascript/Ajax. In aggiunta a queste librerie è necessario aggiungere il supporto al linguaggio che aiuta a definire i Flow.
SWF usa OGNL.

SWF come detto si basa su Spring MVC, quindi così come per tutte le applicazioni spring-based, le richieste passano attraverso il DispatcherServlet. Per configurare SWF basta aggiungere le seguenti righe di codice al file servlet.xml :

<servlet>
    <servlet-name>DevMeApp</servlet-name>   
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

Bene, a questo punto non rimane che indicare quale richiesta debba essere "catturata" da spring e quindi girata a SWF. Per fare questo, ci basterà configurare il nostro DispatcherServlet con un handle per tutte le richieste il cui URL fa match con la stringa "devme"…tradotto:

<servlet-mapping>
  <servlet-name>DevMeApp</servlet-name>
  <url-pattern>/devme/*</url-pattern>
</servlet-mapping>

Ora che abbiamo configurato Spring passiamo alla configurazione di SWF. La configurazione avviene tramite compilazione di file XML di Spring aggiungendo a tali file il namespace di SWF, in modo tale da poter utilizzare i tag messi a disposizione dal framework. Quindi partendo da un file di spring aggiungiamo i seguenti namespace:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
</beans>

quindi aggiungiamo la nostra configurazione all’interno del tag beans.
La prima cosa da fare è aggiungere un riferimento al flow executor, ovvero all’elemento che gestisce l’esecuzione del flow. Aggiungiamo quindi il tag:

<webflow:flow-executor id="flowExecutor"/>

Il secondo step è quello di indicare a SWF dove trovare il file xml della definizione del flow. Questo viene fatto attraverso il tag flow registry che permette di indicare la posizione su file system in cui risiedono i file XML dei vari flow. Per esperienza, è molto comodo creare i file di flusso separati all’interno di ogni singolo file.
Aggiungiamo quindi il flow registry:

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows-defs">
	<webflow:flow-location id="devme" path="/WEB-INF/flows/devme-flow.xml" />
</webflow:flow-registry>

l’attributo id del tag flow location permette di assegnare un id al flow di modo che possa essere riferito all’occorrenza. In assenza dell’attributi id, l’id del flow corrisponderà al nome del file del flow.

SWF fornisce un handler adapter (spring based) che permette che fa da bridge tra il DispatcherServlet e il flow executor, gestendo la richiesta e manipolando il flow. E’ necessario configurare l’handler all’interno del file di configurazione affinché possa essere invocato dal framework. Aggiungiamo quindi il seguente tag bean:

<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerAdapter">
  <property name="flowExecutor" ref="flowExecutor" />
</bean>
<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerMapping">
  <property name="flowRegistry" ref="flowRegistry" />
</bean>

in questo modo il DispatcherServlet è in grado di stabilire a chi girare la richesta consultando uno o più handler. Nel codice di sopra abbiamo aggiunto un riferimento al FlowHandlerMapping il quale mantiene un riferimento al FlowRegistry in modo tale da girare la richiesta al flow corretto. Un’applicazione basata su SWF tipicamente contiene più di un flow, per questo il collegamento tra FlowHandlerMapping e flow registry consente di selezionare il flow corretto sulla base della richiesta ricevuta.

Dopo aver configurato la nostra applicazione Spring fornendo il supporto a SWF vediamo come definire un file di flow. Un file di flow è un file XML diverso da quello visti finora, nel senso che ha un nodo radice diverso il quale appartiene ad un namespace differente.
 

<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
      http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
	<-- Add your definition here -->
</flow>

Vediamo quali sono gli elementi che contribuiscono alla definizione di un flow. Di seguito viene mostrato un elenco di tali elementi con per ciascuno una breve descrizione.

  • <action-state>, definisce una o più azioni, al termine delle quali si può transire verso uno stato successivo all’interno del flow.
  • <attribute>, consente di definire un attributo da memorizzare all’interno del flow. Viene utilizzato assieme al tag <value>.
  • <bean-import>, importa un elemento bean definito dall’utente.
  • <decision-state>, permette di valutare un’espressione sulla base della quale decidere verso quale stato transire.
  • <end-state>, indica lo stato finale del flow. La transizione su questo stato comporta il termine del flow.
  • <exception-handler>, indica un bean che può gestire eccezioni per questo flow.
  • <global-transition>, definisce una o più transizioni che sono disponibili da tutti gli stati.
  • <input>, definisce un input per questo flow.
  • <on-end>, evento che definisce l’azione che viene richiamata quando il flow termina.
  • <on-start>, evento che definisce l’azione che viene richiamata quando il flow inizia.
  • <output>, definisce un output per questo flow.
  • <persistence-context>, crea e alloca un contesto quando il flow ha inizio. Viene usato assieme al transaction manager.
  • <secured>, viene usato per restringere l’accesso ad un determinato stato.
  • <subflow-state>, invoca un nuovo flow come sotto-flow di quello corrente.
  • <var>, definisce una variabile con flow scope.
  • <view-state>, indica uno stato che si presenta all’utente tipicamente caricando un certo output e richiedendo l’interazione da parte dell’utente.

Per evitare di dlungarmi più di quanto abbia già fatto rimando la continuazione di questo articolo al post successivo. Continuo a scrivere quandi non passerà molto dalla pubblicazione seguente…prometto :P  !
Stay tuned.

SimpleCaptcha….not too simple.

devme - captchaL’ultima volta ci siamo lasciati con la promessa di rincontrarci per parlare del captcha, l’ormai famoso sistema che rende più sicure le nostre procedure web guidate. Per dovere di cronaca, sinteticamente, segue una breve descrizione. Il captcha è un sistema che genera un immagine casuale, contenente caratteri alfanumerici, non interpretabili da alcuna procedura automatica. Per completare uno step di una qualunque procedura web che contiene un capthca, è necessario inserire ciò che l’immagine raffigura in un campo di testo associato, in modo che server-side si possa effettuare il controllo. Se il contenuto dell’immagine differisce da ciò che è stato inserito nel campo di testo, allora la procedura fallisce; la procedura ha successo in caso contrario. Come contestualizziamo il tutto, in Spring Web Flow? Intanto scegliamo la libreria che implementa il captcha. Le papabili sono molte, la più citata in rete è JCapthca, che avevo anche utilizzato, se non fosse per i miei colleghi che non l’hanno voluta perché non gradivano le immagini generate dal punto di vista grafico. Quindi cercando un altro pò per la rete mi sono imbattuto in simplecatptcha. E’ una libreria molto light, semplice da utilizzare al tempo stesso efficace. Genera immagini carine, con alcuni effetti notevoli, quali ad esempio la distorsione che ho utilizzato. Let’s go. Al solito, si consideri una procedura di registrazione web composta, per semplicità da 3 step, inserimento dei dati personali, riepilogo e conferma. Scarichiamo e mettiamo nel classpath della nostra applicazione la libreria. Successivamente dobbiamo configurare il file web.xml in modo da "installare" il captcha, configurando l’apposita servlet che si occuperà di creare l’immagine casuale.

1
2
3
4
5
6
7
8
9
<servlet>
    <servlet-name>Captcha</servlet-name>
    <servlet-class>nl.captcha.servlet.CaptchaServlet</servlet-class>
     .....some setup of capthca....
</servlet>
<servlet-mapping>
    <servlet-name>Captcha>/servlet-name>
    <url-pattern>/Captcha.jpg>/url-pattern>
</servlet-mapping>

Sul sito di simplecaptcha troverete alcuni setup iniziali che si possono dare alle immagini generate, ad esempio, impostare un background, utilizzare la distorsione, forzare ciò che viene generato, etc, etc. Noterete che i setup sono della forma di tag del tipo init-param che vengono inseriti del tag servlet, lì dove vedete " …..some setup of capthca….". Ok, quindi ora possiamo usare la libreria, e non solo, se ci riferiamo all’interno delle nostre pagine all’indirizzo /Captcha.jpg invocheremo la libreria che si occuperà di generare l’immagine desiderata. Quindi metteremo un bel tag img nel primo step della nostra procudura:

1
2
3
4
5
6
7
8
9
10
<spring:bind path="LCCaptcha">
    <div class="field <c:if test="${status.error}">wrong</c:if>">
        <label for="LCCaptcha">Inserire la scritta che compare nell'immagine</label>
        <img src="<c:url value="Captcha.jpg" />" title="Captcha" alt="Captcha">
        <input type="text" id="LCCaptcha" name="LCCaptcha" value="">
        <c:if test="${status.error}">
            <div class="error">${status.errorMessage}</div>
        </c:if>
    </div>
</spring:bind>

Dando per scontato di conoscere Spring Web Flow e quindi Spring, notiamo che l’elemento img si trova dentro un elemento spring:bind che si preoccupa al submit della pagina di fare bind del contenuto del campo di input (in questo caso) con la corrispondente varibile del bean associato. La descrizione precisa del funzionamento di SWF esula dal contenuto del post. Ciò detto, vediamo che l’url dell’immagine corrisponde all’invocazione dell’indirizzo, configurato all’interno del web.xml che invoca la libreria del captcha. Risultato vedremo l’immagine generata, che cambierà ad ogni ricarica della pagina. Contestualmente alla generazione dell’immagine, la libreria simplecaptcha genererà il valore di ciò che viene visualizzato nell’immagine, rendendolo disponibile come attributo di sessione, dalla chiave SIMPLE_CAPCHA_SESSION_KEY. Supponiamo quindi di inserire i dati presenti nello step1 e fare submit della pagina, spostandoci quindi nel riepilogo ovvero allo step2 della procedura. Ricordiamo che utilizzando SWF per lo sviluppo di applicazioni web, è necessario definire un flusso di operazioni che specificano come avverrà la navigazione delle pagine. Ad esempio, nel flusso di navigazione verrà definito che dallo step1 si andrà allo step2, previa validazione dei dati. Di seguito mostriamo lo scorcio di codice xml del flusso di navigazione che definisce quanto detto sopra:

1
2
3
4
5
6
7
8
9
10
11
12
13
.....
.....
<view-state id="start" view="step1">
    <render-actions>
        <action method="setupForm" bean="DevMeAction" />
    </render-actions>
    <transition on="regUserStep1" to="step2">
        <action method="bindAndValidate" bean="DevMeAction" />
    </transition>
    <transition on="regUserCancel" to="userCancel" />
</view-state>
.....
....

Quindi da quanto scritto sopra, si ha che al momento del submit dei dati, andremo allo step2 se la procedura di validazione andrà a buon fine. In caso contrario, rimarremo nella stessa view, visualizzando opportunamente i messagi di errori dei campi non validi. Qui ho usato un piccolo trick, se così possiamo chiamarlo, non sapendo se è la soluzione migliore nell’ambito SWF. Subito prima di chiamare il metodo che effettua la validazione del form, SWF richiama un suo metodo interno doBind(…) che si preoccupa di prendere i dati nel form e assegnarli alle corrispondenti variabili definite all’interno del bean associato al form…il tutto avviene in automatico. Facendo override del metodo, l’idea è quella di recuperare il valore dalla sessione e assegnarlo, nel mio caso ad una variabile del bean, ma nulla vieta di creare un attributo del flusso e assegnarlo.

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void doBind(RequestContext context, DataBinder binder) throws Exception {
    super.doBind(context, binder);
    /* Get user data from flow */
    DevMeBean devme = (DevMeBean) context.getFlowScope().get("devme");
    /* Retrive session value only in step 2*/
    String captcha_id = context.getExternalContext().getSessionMap().
                        getRequiredString("SIMPLE_CAPCHA_SESSION_KEY");
    if (captcha_id!=null) {
        user.setLCCaptcha_id(captcha_id);
    }
}

Per il mio caso risulta più comodo averlo nel bean, così poi il metodo di validazione, cha ha visibiltà del bean, può accedere comodamente sia al valore del campo di input e sia al valore dell’attributo di sessione, in modo da poter effettuare il controllo.

1
2
3
4
5
6
7
8
public void validate(Object target, Errors errors) {
    DevMeBean devme = (DevMeBean ) target;
    String captcha_id = devme.getLCCaptcha_id();
    String captcha = devme.getLCCaptcha();
    if (!captcha_id.equals(captcha)) {
        errors.rejectValue("LCCaptcha", "error.captcha.invalid");
    }
}

E il gioco è fatto. Se tutto va bene, e quindi niente errori, allora si va allo step2 della procedura….altrimenti si rimane allo step1, e si visualizza l’errore. A questo punto vi chiedere, bhe se tutto è così semplice allora perchè nel titolo hai messo "…not too simple" ? Ora vi dico. L’applicazione a cui sto lavorando dovrà girare su un sistema Linux Debian senza x-window, ovvero senza ambiente grafico. Tipicamente le applicazioni Java che hanno a che fare con la manipolazione delle immagini si appoggiano sul sottosistema grafico del sistema operativo sul quale girano. Se nessun sottosistema è installato, allora bisogna informare la JVM e lanciarla in esecuzione con una specifica opzione:

1
-Djava.awt.headless=true

, è possibile reperire questo dettaglio direttamente dal sito di simplecaptcha. Naturalmente ho fatto quanto detto, ma ovviamente la libreria non né voleva sapere di funzionare, e quindi l’immagine non veniva visualizzata. Gira che ti rigira, ho trovato che simplecaptcha, che premetto è alla versione 0.3, ha un piccolo bug. Per risolverlo è necessario scaricare i sorgenti, e rifare il deploy dell’applicazione, con 1000 sbattimenti annessi al recupero di alcune librerie che non sono, diciamo così standard. Il bug lo potete trovare direttamente sul forum qui, ma vi assicuro che il jar bug-free no, quindi lo rendo disponibile per dovere informatico. See you later.

 

You need to log in to vote

The blog owner requires users to be logged in to be able to vote for this post.

Alternatively, if you do not have an account yet you can create one here.

Powered by Vote It Up

WordPress Themes