Manual básico de Spring WebFlow

3
43566

Manual Básico de Spring WebFlow

Spring WebFlow es un Controlador
(http://es.wikipedia.org/wiki/Modelo_Vista_Controlador)
que orienta a procesos los flujos de navegación de una
aplicación definiéndolos mediante estados y
transiciones.

A medida que aplicamos Spring WebFlow a
nuestros proyectos, nos acostumbramos a sus virtudes y un día
nos damos cuenta de que no podríamos volver a trabajar como
antes. Más o menos como nos pasó cuando descubrimos
Struts o cualquier MVC. Y cuando más avanzamos, más
queremos y Spring es casi inagotable.

El objetivo de este tutorial es
facilitar el primer contacto con esta tecnología y analizar su
aplicabilidad, recomendar algunos links y comentaros nuestras
conclusiones.

Preparación del proyecto

Comenzaremos creando un proyecto y para este tutorial utilizaremos
Eclipse:


Seleccionamos
proyecto web:


Seleccionamos
un nombre y un servidor:


En
este paso no es necesario completar nada:


Y
completamos los nombres de las carpetas antes de dar a Finalizar:


A
continuación descargamos de
http://www.springframework.org/download
las distribuciones que se indican en rojo:

Copiamos en la carpeta /pruebaWebFlowSimple/web/WEB-INF/lib los
JAR los jar que se encuentran en los zip que descargamos y los
añadimos al proyecto:


Seleccionando
mediante “Add Jars” los que hemos descargado:

Configuración del contexto

Comenzaremos editando /pruebaWebFlowSimple/web/WEB-INF/web.xml
para dejarlo como se muestra a continuación:

<?xml
version=«1.0»
encoding=«ISO-8859-1»?>

<web-app
xmlns=«http://java.sun.com/xml/ns/j2ee»

xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance»

xsi:schemaLocation=«http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd»

version=«2.4»>

<context-param>

<param-name>webAppRootKey</param-name>

<param-value>swf-pruebaWebFlow.root</param-value>

</context-param>

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

<servlet>

<servlet-name>JspRedirector</servlet-name>

<jsp-file>/WEB-INF/test/jspRedirector.jsp</jsp-file>

</servlet>

<servlet>

<servlet-name>pruebaWebFlow</servlet-name>

<servlet-class>

org.springframework.web.servlet.DispatcherServlet

</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/pruebaWebFlow-servlet-config.xml

</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>JspRedirector</servlet-name>

<url-pattern>/JspRedirector</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>pruebaWebFlow</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<jsp-config>

<taglib>

<taglib-uri>http://www.adesis.com/cm/oi/2</taglib-uri>

<taglib-location>/WEB-INF/tld/adesis-cm-io-2.tld</taglib-location>

</taglib>

</jsp-config>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

También crearemos un fichero
/pruebaWebFlowSimple/web/WEB-INF/pruebaWebFlowSimple-servlet-config.xml
como se muestra a continuación:

<?xml
version=«1.0»
encoding=«UTF-8»?>

<beans
xmlns=«http://www.springframework.org/schema/beans»

xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance»

xmlns:flow=«http://www.springframework.org/schema/webflow-config»

xsi:schemaLocation=«

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/webflow-config

http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd»>

<bean
name=«/asistente.flow»

class=«org.springframework.webflow.executor.mvc.FlowController»>

<property
name=«flowExecutor»
ref=«pruebaFlowExecutor»
/>

<property
name=«defaultFlowId»
value=«asistente-flow»
/>

<property
name=«cacheSeconds»
value=«5»
/>

</bean>

<bean
id=«viewResolver»

class=«org.springframework.web.servlet.view.InternalResourceViewResolver»>

<property
name=«prefix»
value=«/WEB-INF/jsp/»
/>

<property
name=«suffix»
value=«.jsp»
/>

</bean>

<flow:registry
id=«flowRegistry»>

<flow:location
path=«/WEB-INF/flows/**-flow.xml»
/>

</flow:registry>

<flow:executor
id=«pruebaFlowExecutor»
registry-ref=«flowRegistry»>

<flow:execution-attributes>

<flow:alwaysRedirectOnPause
value=«false»
/>

</flow:execution-attributes>

</flow:executor>

</beans>

Definición del flujo

Creamos un fichero
/pruebaWebFlowSimple/web/WEB-INF/flows/asistente-flow.xml como se
muestra a continuación:

<?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-1.0.xsd»>

<start-actions>

<action
bean=«formAction»
method=«setupForm»
/>

</start-actions>

<start-state
idref=«mostrarPaso1»
/>

<view-state
id=«mostrarPaso1»
view=«paso1»>

<transition
on=«siguiente»
to=«avanzaAPaso2»>

<action
bean=«formAction»
method=«bind»
/>

</transition>

</view-state>

<action-state
id=«avanzaAPaso2»>

<action
bean=«formAction»
method=«validarPaso1YPrepararPaso2»
/>

<transition
on=«success»
to=«mostrarPaso2»
/>

<transition
on=«error»
to=«mostrarPaso1»
/>

</action-state>

<view-state
id=«mostrarPaso2»
view=«paso2»>

<transition
on=«siguiente»
to=«avanzaAPaso3»>

<action
bean=«formAction»
method=«bind»
/>

</transition>

<transition
on=«anterior»
to=«mostrarPaso1»>

<action
bean=«formAction»
method=«bind»
/>

</transition>

</view-state>

<action-state
id=«avanzaAPaso3»>

<action
bean=«formAction»
method=«validarPaso2YPrepararPaso3»
/>

<transition
on=«success»
to=«mostrarPaso3»
/>

<transition
on=«error»
to=«mostrarPaso2»
/>

</action-state>

<end-state
id=«mostrarPaso3»
view=«paso3»
/>

<import
resource=«asistente-beans.xml»
/>

</flow>

Vemos cómo el flujo queda
determinado por estados desde donde se realizan transiciones a otros
estados. Los estados pueden ser de visualización o de
ejecución de operaciones, y utilizan beans definidos en
asistente-beans.xml:

<beans
xmlns=«http://www.springframework.org/schema/beans»

xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance»

xsi:schemaLocation=«http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd»>

<bean
id=«formAction»

class=«es.autentia.tutoriales.manualBasicoSpringWebFlow.controller.action.AsistenteAction»>

<property
name=«formObjectScope»
value=«FLOW»
/>

<property
name=«formObjectName»
value=«form»
/>

<property
name=«formObjectClass»

value=«es.autentia.tutoriales.manualBasicoSpringWebFlow.controller.form.AsistenteForm»
/>

</bean>

</beans>

Definición de las clases

En el flujo se define el bean donde se
implementarán las acciones y el form que es un JavaBean donde
se almacenan o recuperan los datos que se le muestran al usuario y/o
que éste completa. Cuando en asistente-flow.xml se invoca al
método bind en una transición, se está indicando
a Spring que realice la copia de los valores de formulario enviados
desde el navegador del usuario al formulario del flujo.

package
es.autentia.tutoriales.manualBasicoSpringWebFlow.controller.form;

import
java.io.Serializable;

public
class
AsistenteForm
implements
Serializable {

private
static
final
long
serialVersionUID
= 3849222383843518624L;

private
Integer
numero1
= 1;

private
Integer
numero2
= 2;

private
String
mensaje;

public
String getMensaje() {

return
mensaje;

}

public
void
setMensaje(String mensaje) {

this.mensaje
= mensaje;

}

public
Integer getNumero1() {

return
numero1;

}

public
void
setNumero1(Integer numero1) {

this.numero1
= numero1;

}

public
Integer getNumero2() {

return
numero2;

}

public
void
setNumero2(Integer numero2) {

this.numero2
= numero2;

}

}

Crearemos también un Action como
el siguiente:

package
es.autentia.tutoriales.manualBasicoSpringWebFlow.controller.action;

import
org.springframework.webflow.execution.RequestContext;

import
org.springframework.webflow.action.FormAction;

import
org.springframework.webflow.execution.Event;

import
es.autentia.tutoriales.manualBasicoSpringWebFlow.controller.form.AsistenteForm;

public
class
AsistenteAction
extends
FormAction {

public
AsistenteAction() {

setFormObjectClass(AsistenteForm.class);

}

@Override

public
Event setupForm(RequestContext context)
throws
Exception {

AsistenteForm
form = (AsistenteForm) getFormObject(context);

form.setNumero1(1);

form.setNumero2(2);

form.setMensaje(«Introduzca
los números a sumar»
);

return
super.setupForm(context);

}

public
Event validarPaso1YPrepararPaso2(RequestContext context)

throws
Exception {

AsistenteForm
form = (AsistenteForm) getFormObject(context);

Event
resultado =
null;

if
(form.getNumero1() < 0 || form.getNumero2() < 0) {

form.setMensaje(«Los
números a sumar deben ser positivos»
);

resultado
= error();

}
else
{

form.setMensaje(«El
resultado de la suma es «

+
(form.getNumero1() + form.getNumero2()));

resultado
= success();

}

return
resultado;

}

public
Event validarPaso2YPrepararPaso3(RequestContext context)

throws
Exception {

AsistenteForm
form = (AsistenteForm) getFormObject(context);

form.setMensaje(«Fin
de la operación»
);

return
success();

}

}

En el mismo vemos que es muy simple
acceder a los datos que se le muestran o que introduce el usuario con
la ayuda del form indicado en la configuración.

Definición de las páginas

Las páginas son muy simples. En
/pruebaWebFlowSimple/web/WEB-INF/jsp/paso1.jsp vemos:

<html
xmlns=«http://www.w3.org/1999/xhtml»
xml:lang=«en»
lang=«en»>

<head>

<title>Asistente
sumador
</title>

</head>

<body>

<h1>Asistente
sumador: Paso 1
</h1>

<form>

<input
type=«hidden»
name=«_flowId»
value=«asistente-flow»
/>

<input
type=«hidden»
name=«_eventId»
value=«siguiente»
/>

<input
type=«hidden»
name=«_flowExecutionKey»
value=«${flowExecutionKey}«
/>

<p>${form.mensaje}</p>

<p>Primer
número:
<input
type=«text»
name=«numero1»
value=«${form.numero1}«/></p>

<p>Segundo
número:
<input
type=«text»
name=«numero2»
value=«${form.numero2}«/></p>

<input
type=«submit»
value=«siguiente»
/>

</form>

</body>

</html>

Donde en los campos del formulario se
indican el identificador de flujo, de evento y la clave de ejecución
que utiliza WebFlow para mantener el estado de un usuario. En
/pruebaWebFlowSimple/web/WEB-INF/jsp/paso2.jsp ponemos:

<html
xmlns=«http://www.w3.org/1999/xhtml»
xml:lang=«en»
lang=«en»>

<head>

<title>Asistente
sumador
</title>

</head>

<body>

<h1>Asistente
sumador: Paso 2
</h1>

<form>

<input
type=«hidden»
name=«_flowId»
value=«asistente-flow»
/>

<input
type=«hidden»
name=«_eventId»
value=«siguiente»
/>

<input
type=«hidden»
name=«_flowExecutionKey»
value=«${flowExecutionKey}«
/>

<p>${form.mensaje}</p>

<input
type=«submit»
value=«siguiente»
/>

<input
type=«submit»
value=«anterior»
onclick=«document.forms[0]._eventId.value=’anterior’;return
true;»
/>

</form>

</body>

</html>

Y en
/pruebaWebFlowSimple/web/WEB-INF/jsp/paso3.jsp:

<html
xmlns=«http://www.w3.org/1999/xhtml»
xml:lang=«en»
lang=«en»>

<head>

<title>Asistente
sumador
</title>

</head>

<body>

<h1>Asistente
sumador: Paso 3
</h1>

<form>

<p>${form.mensaje}</p>

</form>

</body>

</html>

Ejecutando la aplicación

Lanzamos el servidor con la URL
http://localhost:8080/pruebaWebFlowSimple/asistente.flow
y veremos en funcionamiento el ejemplo del asistente sumador.

Una mejora que podemos implementar es utilizar el envío del
formulario mediante Post, para conservar limpia la url del navegador
del usuario.

Algunos links interesantes

  • Sitio oficial de Spring,
    http://www.springframework.org/webflow

  • Documentación oficial de Referencia ,
    http://static.springframework.org/spring-webflow/docs/1.0.x/reference/index.html

  • Introducción de Ervacom,
    http://www.ervacon.com/products/swf/intro/index.html

Conclusiones

Tras utilizarlo en proyectos de envergadura concluimos que:

  • El Spring WebFlow es un
    Controlador que orienta a procesos el flujo de navegación,
    que no se debe confundir con un BPM (Ver
    http://en.wikipedia.org/wiki/Business_process_management)
    ya que el proceso de negocio puede diferir con la navegación
    de su interfaz de usuario, los BPM son independientes de las
    tecnologías de interfaz de usuario y muchos etcéteras..

  • Lo vemos especialmente útil
    en aplicaciones con flujos de mediana a gran complejidad cuyo
    mantenimiento sea relativamente periódico.

  • Permite generar documentación
    actualizada del flujo de navegación mediante una simple XSD.

  • Puede convivir con otros MVC como
    el Spring MVC o Struts.

  • Securiza la ejecución de
    flujos, evitnado re-call y ataques deny-of-service o
    cross-site-scripting.

Desde Autentia contamos con los
conocimientos y experiencia para ayudarle a sacar la máxima
ventaja de las tecnologías más innovadoras y mejorar la
calidad de sus desarrollos software.

No dude en contactarse con nosotros
mediante www.autentia.com .

3 Comentarios

  1. Bien, como introducción, no está mal.

    Desde mi punto de vista, el nivel que ofreces es demasiado alto. Aún a riesgo de meter la pata, voy a mojarme en la siguiente afirmación: \\\»El objetivo de un tutorial no es exhibicionarse, sino compartir información\\\». Cualquier conducta que se salga de este cometido es un error en la enseñanza.

    Saludos,

Dejar respuesta

Please enter your comment!
Please enter your name here