En Autentia trabajamos constantemente con los frameworks mas avanzados para el
diseño de servicios web en
nuestros desarrollos. En esta línea, dedicamos especial interés a la
investigación y utilización de las extensiones de estos frameworks que faciliten y aporten mayor potencia al
diseño de una aplicación web.
Hoy os queremos mostrar una introducción al módulo Spring Web Flow
del framework Spring de cara al desarrollo de
aplicaciones web.
1. ¿Qué es Spring
Web Flow?
Spring Web Flow es un módulo del framework
Spring
dirigido a definir y gestionar los flujos de páginas dentro de una aplicación web.
El flujo de páginas de una
aplicación web consiste en la secuencia de páginas
por las que pasa dicha aplicación en función de la conversación que mantenga
con el usuario. Dependiendo de las opciones que escoja el usuario, resultados
de las operaciones de proceso…etc, la aplicación
seguirá una ruta de páginas especifica u otra.
Por ejemplo, el flujo de
páginas de una aplicación web sencilla podría ser el
siguiente:
La aplicación se dedica a
registrar los datos de los usuarios que la acceden. Desde la pagina inicial el
usuario selecciona la opción REGISTRAR para proceder a su registro. Se le
muestra una página donde debe introducir sus datos personales para completar el
registro. Una vez introducidos, la aplicación muestra una página confirmando el
registro si no se produjo ningún error, o bien ofrece al usuario la posibilidad
de volver a introducir sus datos correctamente si se produjo algún error.
Como puede observarse, la
forma más intuitiva de representar el flujo de páginas de una aplicación es con
un diagrama de estados. Spring Web Flow
utiliza estructuras declarativas basadas en máquinas de estados, como se
mostrará más adelante.
Partiendo de este concepto,
los aspectos fundamentales que potencia Spring Web Flow son:
- Encapsulación de la lógica de los flujos de páginas como un
módulo autónomo que puede reutilizarse en diferentes situaciones. Hay
aplicaciones web específicas que suelen utilizar
la misma lógica de flujo de páginas, como por ejemplo, aplicaciones tipo
“carrito de la compra”, correo web, etc.
- Proporcionar un motor capaz de capturar los
flujos de páginas de una aplicación, integrándolo con algunos frameworks de uso habitual como Spring, Spring MVC o JSF.
2. Instalación y requisitos
El ejemplo
sencillo que se estudiará más adelante en este documento, se desarrolló y
ejecutó en un entorno Windows XP que disponía
de la distribución Java j2sdk-1.4.2.
Esta distribución puede obtenerse gratuitamente desde la web
de SUN en el enlace:
http://java.sun.com/products/archive/j2se/1.4.2/index.html
Es necesario
obtener distribuciones del framework Spring y de Spring Web Flow.
Para realizar el ejemplo que se comentará más tarde, se utilizaron las ditribuciones spring–framework-1.2.6.zip y spring–webflow-pr5.zip, que pueden obtenerse gratuitamente desde los
enlaces:
http://sourceforge.net/project/showfiles.php?group_id=73357
http://sourceforge.net/project/showfiles.php?group_id=73357&package_id=148517
Una vez
descomprimidos ambos archivos, será necesario que los siguientes archivos sean
visibles en el classpath:
- spring–framework-1.2.6/dist/spring.jar
- spring-webflow-pr5/dist/spring-binding-1.0-dev-20050727232106.jar
- spring–webflow-pr5/dist/spring–webflow-pr5.jar
Se utilizó la
herramienta Ant
para compilar y empaquetar el ejemplo realizado. La distribución utilizada es apache-ant-1.6.5-bin.zip, que puede obtenerse gratuitamente desde el
siguiente enlace:
http://ant.apache.org/bindownload.cgi
Por último, para
deplegar el ejemplo se utilizó el contenedor de
aplicaciones web de Tomcat.
Se utilizó la distribución jakarta–tomcat-5.0.30.zip, que puede
obtenerse gratuitamente desde el enlace:
http://tomcat.apache.org/download-55.cgi
3. Un ejemplo sencillo
3.1. Idea
Para ilustrar el funcionamiento
y la forma de trabajar con Spring Web Flow se realizará una aplicación web
cuya funcionalidad será la siguiente:
- Obtener nombre y apellidos del usuario a través
de un formulario. - Comprobar que se introdujeron
dichos datos en el formulario. Si no se introdujo alguno de ellos,
informará al usuario de la situación de error y le ofrecerá la posibilidad
de volver a introducirlos. - Si la captura de los datos
anteriores se realiza correctamente, la aplicación muestra una página que
envía un saludo al usuario.
El flujo de
páginas de la aplicación puede resumirse en el siguiente diagrama de estados:
3.2. Implementación del flujo de
páginas
Para implementar el flujo de
páginas con Spring Web Flow
creamos un fichero xml
dónde se incluirá la declaración del flujo de páginas de la aplicación en forma
de máquina de estados.
En el ejemplo que se esta
estudiando se necesitarán tres tipos de estados distintos:
- View–State:
Son estados en los que se activa algún recurso con tareas de presentación
en la aplicación. Por ejemplo, en aplicaciones web
corresponde habitualmente con la activación de Java Server Pages (jsp´s).
- Action–State: Son
estados en los que la aplicación realiza alguna labor de la lógica de
negocio. Por ejemplo, acciones típicas sobre bases de datos, proceso de
datos, etc. La transición de este tipo de estados a otros suele estar
condicionada por el resultado de la operación específica que se realice en
el mismo. Un poco más adelante se comentará como gestiona esto Spring Web Flow.
- Start–State:
Es un estado en el que se indica que comienza el flujo de la aplicación.
Al igual que los View–State,
suele llevar asociada la activación de un recurso de presentación (jsp, etc).
- End–State:
Son estados en los que el flujo de la aplicación finaliza y ya no avanza
hacia ningún otro. Al igual que los View–State, suele llevar asociada la
activación de un recurso de presentación (jsp, etc).
La primera etiqueta de este
documento identificará dentro de la aplicación al flujo de páginas que
contiene, y tiene la siguiente estructura:
<webflow id=»helloFlow»
start-state=»rellenarDatos«>
El campo id contiene el identificador del
conjunto de flujos declarados en el fichero y al que se hará referencia en la
definición del constructor xml del flujo de páginas,
como veremos más adelante.
El campo start–state indicará el nombre del estado en
el que comenzará el flujo.
A continuación se introducen
como etiquetas hijas de la etiqueta webflow, los distintos estados que componen el flujo de la
aplicación, indicado al principio de este apartado en un diagrama.
Se indica en primer lugar el
estado inicial rellenarDatos, referenciado anteriormente en la
etiqueta webflow, que tendrá la siguiente estructura:
<view-state id=»rellenarDatos»
view=»datosPersonales«>
<entry>
<action bean=»HelloFormAction» method=»setupForm«/>
</entry>
<transition on=»saludar» to=»procesarSaludo«>
<action bean=»HelloFormAction» method=»bindAndValidate«/>
</transition>
</view–state>
Como indica la etiqueta del
estado, se trata de un View–State. Procederá
a activar el elemento de presentación datosPersonales.jsp que contendrá el formulario para
introducir el nombre y los apellidos del usuario. Esto se indica en el atributo
view que,
como puede observarse, no indica la extensión .jsp del elemento de presentación.
Esto no es necesario; más adelante se verá cómo se le indica a la
aplicación que considere los elementos
de presentación que se activan como archivos .jsp .
La etiqueta action indica el
recurso que implementa dicha acción. El atributo bean contiene el identificador de
dicho recurso dentro de la aplicación.
La etiqueta transition indica
la transición que se realizará a otro estado, indicado por el atributo to, tras el suceso de un evento
determinado, indicado por el atributo on. En este caso, el evento que activa la transición es el
evento saludar, que como se verá más
adelante, corresponde con la pulsación del botón del formulario inicial. El
estado destino de esta transición es procesarSaludo. Esta transición sólo se realizará si la
validación, indicada por el método bindAndValidate, concluye satisfactoriamente. Más adelante
se indicará cómo se hizo y que criterios se utilizaron para la validación de
este estado inicial.
El siguiente estado que figura es procesarSaludo, al cuál ya se
hizo referencia en el estado anterior. Su estructura es la siguiente:
<action–state id=»procesarSaludo«>
<action bean=»HelloAction«/>
<transition
on=»success» to=»mostrarSaludo«/>
</action–state>
Como puede verse este estado es de tipo Action–State. Se
especifica para procesar la acción el recurso HelloAction, y al no indicar el
método al que hacer referencia toma por defecto el indicado en el atributo id, es decir, procesarSaludo.
Si se da el evento success
se producirá una transición al estado mostrarSaludo.
El último estado que figura en el fichero es el
estado final que tiene el siguiente aspecto:
<end-state id=»mostrarSaludo»
view=»saludo«/>
La etiqueta end–state indica que se trata de un estado final. El
atributo view
indica el recurso de presentación que se activará en dicho estado. Recordar que
este recurso será saludo.jsp,
aunque no se indique la extensión. Como se dijo anteriormente, en breve se
comentará como indicar a la aplicación que tome como extensión predefinida la .jsp cuando se
activa un recurso de presentación. El campo id, como en casos anteriores,
contiene el identificador del estado para que pueda ser referenciado en la
declaración de transiciones, etc.
De esta forma, el fichero xml que contiene la definición del flujo de la aplicación queda como
se muestra a continuación:
<?xml
version=»1.0″ encoding=»UTF-8″?>
<!DOCTYPE webflow PUBLIC «-//SPRING//DTD WEBFLOW//EN»
«http://www.springframework.org/dtd/spring-webflow.dtd»>
<webflow id=»helloFlow»
start-state=»rellenarDatos«>
<view-state id=»rellenarDatos» view=»datosPersonales«>
<entry>
<action
bean=»HelloFormAction» method=»setupForm«/>
</entry>
<transition
on=»saludar» to=»procesarSaludo«>
<action
bean=»HelloFormAction» method=»bindAndValidate«/>
</transition>
</view-state>
<action-state id=»procesarSaludo«>
<action bean=»HelloAction«/>
<transition on=»success»
to=»mostrarSaludo«/>
</action-state>
<end-state id=»mostrarSaludo» view=»saludo«/>
</webflow>
Se ha mostrado como declarar
el flujo de páginas de la aplicación a través de un fichero xml. Esta declaración puede realizarse
también a través de una clase Java. Una versión con Java del diseño de este
flujo de páginas podría ser el siguiente:
public class HelloFlowBuilder extends AbstractFlowBuilder
{
protected
String flowId() {
return
«helloFlow«;
}
public void buildStates() throws FlowBuilderException
{
//Estado inicial, con formulario para rellenar
ViewState rellenarDatos
= addViewState(“rellenarDatos”, «datosPersonales«,
on(«saludar», “procesarSaludo”,
beforeExecute(method(«bindAndValidate«, action(«HelloFormAction«)))));
rellenarDatos.setEntryAction(method(«setupForm«,
action(«HelloFormAction«)));
//Estado
intermedio, proceso de datos
addActionState(“procesarSaludo”,
action(HelloAction.class, AutowireMode.CONSTRUCTOR),
new Transition[] {
on(on(success(),
“mostrarSaludo”) });
//Estado final, saludo al usuario
addEndState(success(), «saludo»);
} // buildStates()
} //HelloFlowBuilder
3.3. Integración del flujo de
páginas
Una vez implementado el flujo
de páginas, se mostrará a continuación como integrarlo con la aplicación web diseñada como ejemplo.
Para desplegar el flujo
definido (helloFlow)
así como los recursos que utiliza, se utiliza el contexto de aplicación de Spring. En este caso, se añadió en el fichero xml correspondiente las siguientes
etiquetas:
<bean
id=»helloFlow» class=»org.springframework.webflow.config.XmlFlowFactoryBean«>
<property name=»location»
value=»classpath:org/autentia/webflow/ejemplo/web/hello-flow.xml«/>
</bean>
En ésta etiqueta se indica la clase constructora para
el flujo que se utilizará, así como la localización del archivo xml con la
definición del flujo de páginas que utilizará dicho constructor.
En este caso, las especificaciones se refieren a la
construcción de un flujo a partir de la información contenida en un fichero xml. Como se vio antes, se puede crear una clase
Java para definir el flujo de páginas, en cuyo caso la información a
especificar en el fichero xml con el contexto de la aplicación sería algo
como lo siguiente:
<bean
id=»helloFlow« class=»org.springframework.webflow.config.FlowFactoryBean«>
<property name=»flowBuilder«>
<bean class=»org.autentia.webflow.ejemplo.web.HelloFlowBuilder» autowire=»byType«/>
</property>
</bean>
En este caso, la clase HelloFlowBuilder debe contener el
código Java especificado en el apartado anterior con la definición del flujo de
páginas de la aplicación.
Para definir los recursos que empleará el flujo de la
aplicación (validación, proceso, etc), se añaden las
siguientes líneas en el fichero xml que contiene el contexto de aplicación:
<bean id=»HelloFormAction»
class=»org.springframework.webflow.action.FormAction«>
<property name=»formObjectName»
value=»HelloInfo«/>
<property name=»formObjectClass»
value=»org.autentia.webflow.ejemplo.domain.HelloInfo«/>
<property name=»formObjectScopeAsString»
value=»flow»/>
<property name=»validator«>
<bean class=»org.autentia.webflow.ejemplo.web.HelloValidator«/>
</property>
</bean>
<bean id=»HelloAction»
class=»org.autentia.webflow.ejemplo.web.HelloAction«>
</bean>
La etiqueta bean identificada como HelloFormAction contiene las
especificaciones necesarias para inicializar y validar el formulario inicial.
Para ello, se define la bean HelloInfo, donde se almacenarán
los datos del usuario (nombre y apellidos) desde el formulario, así como la
clase HelloValidator para validar dicho formulario.
El código de la bean sería
algo como lo siguiente:
import java.io.Serializable;
public class HelloInfo implements Serializable
{
private
String firstName = «»;
private
String lastName = «»;
public
String getFirstName() {
return
firstName;
}
public
void setFirstName(String firstName)
{
this.firstName
= firstName;
}
public
String getLastName() {
return
lastName;
}
public
void setLastName(String lastName)
{
this.lastName = lastName;
}
}
El código para la validación sería algo como lo que
sigue:
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class HelloValidator implements Validator
{
public boolean
supports(Class clazz) {
return
clazz.equals(HelloInfo.class);
}
public void validate(Object
obj, Errors errors) {
HelloInfo
query = (HelloInfo)obj;
if((query.getFirstName()
== null || query.getFirstName().length() == 0)
|| (query.getLastName() == null || query.getLastName().length()
== 0)) {
errors.reject(«noCriteria«,
«Por favor, introduzca los datos para el saludo!»);
}
}//if
}//validate
Es una validación sencilla, como puede observarse.
Una vez se vinculan los campos del
formulario (nombre y apellidos) con la bean y se
selecciona el botón que inicie la acción del saludo, este código de validación
comprueba que ninguno de los campos sea vacío.
La etiqueta bean identificada como HelloAction contiene la
especificación del recurso encargado de la lógica de negocio en los Action–State. La
clase HelloAction debería contener un esqueleto como el
siguiente:
import org.springframework.webflow.Event;
import org.springframework.webflow.RequestContext;
import org.springframework.webflow.action.AbstractAction;
public class HelloAction extends AbstractAction
{
protected
Event doExecute(RequestContext
context) throws Exception {
//logica
de negocio que se precise
return
success();
}
}
En este caso no se precisa de ningúna
lógica de negocio especifica y la clase doExecute encargada
del proceso, simplemente devuelve success() para producir la transición desde el estado que contiene la
página con el formulario inicial hasta la página que saluda al usuario.
Por último, para definir el controlador del flujo de
páginas y los recursos de presentación que utilizará añadimos al contexto de
aplicación lo siguiente:
<beans>
<bean name=»/hello.htm» class=»org.springframework.webflow.mvc.FlowController«/>
<bean id=»viewResolver»
class=»org.springframework.web.servlet.view.InternalResourceViewResolver»>
<property name=»prefix» value=»/WEB-INF/jsp/»/>
<property name=»suffix» value=».jsp«/>
</bean>
</beans>
La etiqueta bean que define el controlador contiene la especificación
del recurso al que se hará referencia para poner en marcha y gestionar el flujo
de páginas de la aplicación. En este ejemplo concreto, la página inicial
contiene un botón que inicia el ejemplo (Iniciar
ejemplo) cuya definición para activar el flujo de páginas debe contener la
siguiente referencia al controlador:
<a href=»hello.htm?_flowId=helloFlow«>
. . . </a>
De ésta forma, al aplicación
busca en su contexto el flujo definido como helloFlow, cuya implementación se
explicó anteriormente.
La etiqueta bean que define los recursos de presentación (viewResolver), se
observa que contiene información sobre el directorio donde se encuentran y
sobre su extensión. De esta forma, cuando se solicite la activación de un
recurso de presentación desde un View–State, como se vio anteriormente, sólo es necesario
indicar el nombre del recurso, porque su localización y extensión ya viene
especificada en las etiquetas property (preffix, suffix) que actúan como
prefijo y sufijo de los recursos de presentación que se referencien.
3.4. Ejecución
Una vez compilado, empaquetado y desplegado el proyecto
en un contenedor de aplicaciones web (Tomcat, en este caso), la ejecución del ejemplo produce los
siguientes resultados. Se mostrará la correspondencia entre el flujo de páginas
de la aplicación y el diagrama de estados del mismo que se diseñó al principio.
1.
Instrucciones de la aplicación
2.
Relleno del formulario (error)
Se vuelve a la misma página y la validación produce
un mensaje de error.
3.
Relleno del formulario (ok)
La aplicación muestra entonces una página saludando
al usuario.
4. Fuentes
Página de Keith Donald sobre Spring Web Flow:
http://opensource2.atlassian.com/confluence/spring/display/WEBFLOW/Home
Página del Framework Spring:
http://www.springframework.org/
Apache Ant:
Apache Tomcat:
Sinceramente, me gustaría que alguien pusiera un poco de orden con los comentarios, porque vaya jeta tienen algunos, que se creen que aquí el trabajo se lo hacemos a ellos.
A mi esas actitudes me cabrean y mucho. Por favor, administradores, poner un poquito de orden con los comentarios, porque dan ganas a los que tratamos de colaborar con vosotros y no seguir haciéndolo y seguir \\\»chupando del bote\\\»… de verdad que que jeta tienen los amigos….
Sobre el tutorial, está bien, un poco denso, y como iniciación está bien. Está muy currado y muy buenos los gráficos, no te conocía como autor y seguiré viendo cosillas tuyas.
Un saludo,
Ah, i see. Well th’ats not too tricky at all!»