RSS

Ejemplo básico de Spring Web Flow

05 Jul

Visit my new website http://java4developers.com

En este post vamos a ver como hacer un ejemplo sencillo de Spring Web Flow e integrarlo con los ejemplos que estamos haciendo en los últimos post (Ejemplo básico JPA con EntityManager , Spring y Maven ó Generación del metamodelo de base de datos con Maven y uso en Criteria JPA 2.0). Spring Web Flow nos permite construir nuestras aplicación Web de una manera rápida basandonos en flujos. Esto es, las transaciones entre unas páginas y otras en función de las acciones que suceden, como por ejemplo pulsar un determinado botón.


La estructura de nuestro proyecto será la siguiente:
estructuraProyecto-hop2croft-wordpress-com

Podemos observar como la única variación que encontramos respecto a proyecto anteriores es la existencia de la carpeta webapp de la que colgará la parte web de nuestra aplicación. Los servicios que llamaremos en la capa de vista de Spring Web Flow son los ya implementados, como por ejemplo getCars() que devolverá una lista de coches de nuestra base de datos mySql.

Vamos a explicar la estructura de ficheros que vamos a usar en Spring WebFlow. Voy a mostrar un esquema donde las flechas indicarán que un fichero (del que sale la flecha) va a utilizar otro fichero (al que llega la flecha).

estructuraWebFlow-hop2croft-wordpress-com

El fichero principal en toda aplicación web es el fichero web.xml. En este fichero vamos a definir dos cosas.
Por un lado tenemos el contexto de nuestro aplicación. Dicho contexto se va a encontrar en el fichero carMvc-service.xml. En este fichero encontraremos además un bean que nos permitirá acceder al servicio de listado de coches. El listener encargado en la carga de nuestro contexto vendrá definido en el elemento Listener. La clase de este listener será org.springframework.web.context.ContextLoaderListener.
En segundo lugar necesitamos definir el servlet que se va a encargar de manejar las peticiones a la aplicación web. Vendrá definido en el elemento servlet. En nuestro caso se llamará carMvc (vamos a configurarlo en el fichero carMvc-servlet.xml) y la clase del servlet será org.springframework.web.servlet.DispatcherServlet. También tenemos que indicarle a nuestro servlet que peticiones, es decir, las llamadas a que urls de nuestra aplicación quiere que maneje. Esto se define en el servlet-mapping. En nuestra aplicación el servlet carMvc manejará las peticiones que lleguen a cualquier url que se encuentre bajo la dirección flows. El contenido del fichero web.xml es el siguiente.

<web-app version="2.4" 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">

	<display-name>aplicación de ejemplo</display-name>

	<!-- indicamos que nuestros beans de contexto serán los que se indiquen
		en el fichero carMvc-service.xml -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/carMvc-service.xml</param-value>
	</context-param>

	<!-- será la clase que se encargará de cargar nuestro contexto indicado en el context-param -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- nuestro servlet se llamará carMvc. Tendremos que crear un fichero carMvc-servlet
		donde tendremos que incluir el fichero donde definamos nuestros flows (carMvc-webflow.xml)-->
	<servlet>
		<servlet-name>carMvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- nuestro servlet carMvc será el encargado de gestionar nuestros flows -->
	<servlet-mapping>
		<servlet-name>carMvc</servlet-name>
		<url-pattern>/flows/*</url-pattern>
	</servlet-mapping>
</web-app>

Nuestro index.jsp tendrá el siguiente aspecto.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body onLoad="location.href='flows/welcome/bienvenido.jsp'">
</body>
</html>

El fichero carMvc-service.xml (donde definiremos el contexto de la aplicación) tiene esta pinta.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<bean name="carService" class="com.hopcroft.examples.service.CarService" />

		<!-- The rest of the config is covered below -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost/test"
		p:username="root" p:password="" p:initialSize="5" p:maxActive="10">
	</bean>

	<bean
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
		id="entityManagerFactory">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<context:component-scan base-package="com.hopcroft.examples.dao">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Repository" />
	</context:component-scan>

	<bean class="org.springframework.orm.jpa.JpaTransactionManager"
		id="transactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

</beans>

El fichero carMvc-servlet.xml simplemente importa el fichero carMvc-webflow.xml que es donde se encuentra la gestión de flows.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- se declaran los flows en otro fichero -->
	<import resource="carMvc-webflow.xml" />

</beans>

En el fichero carMvc-webflow.xml podemos ver dos partes diferenciadas.
Por un lado vamos a añadir un web-flow:executor que se encargará de gestionar los flujos de nuestra aplicación web a través de un bean de la clase org.springframework.webflow.mvc.servlet.FlowController. Es decir nuestro flowController no va a ser otra cosa si no un servlet.
En segundo lugar creamos un webflow:flow-registry que nos indicará el fichero (bienvenido.xml) encargado de definir los flujos de nuestra aplicación. El contenido de carMvc-webflow.xml es el siguiente.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">

	<bean name="/*" class="org.springframework.webflow.mvc.servlet.FlowController">
		<property name="flowExecutor" ref="flowExecutor" />
	</bean>

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

	<webflow:flow-registry id="flowRegistry">
		<webflow:flow-location path="/WEB-INF/flows/welcome/bienvenido.xml"></webflow:flow-location>
	</webflow:flow-registry>
</beans>

Sólo nos queda ver el fichero bienvenido.xml y las dos jsps que tiene nuestro proyecto. Como ya hemos dicho bienvenido.xml será el fichero que defina el flujo de nuestra aplicación. Algo importante a reseñar es que igual que en Maven en este fichero se da el patrón Convención sobre configuración. ¿Qué quiere esto decir?. Pues que si decimos que en nuestra aplicación va a haber una transición del estado A al estado B, a su vez estamos diciendo que tendremos dos jsps A.jsp y B.jsp, siempre y cuando los estados de los que estamos hablando son estados de vista de Spring Web Flow. Va a ver el contenido del fichero bienvenido.xml.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">

	<view-state id="bienvenido">
		<transition on="next" to="list"></transition>
	</view-state>
	<view-state id="list">
		<on-render>
			<evaluate expression="carService.getCars()" result="requestScope.cars"></evaluate>
		</on-render>
	</view-state>
</flow>

Vemos que hay dos estados de vista de Spring Web Flow.
Estado de vista bienvenido. Será el estado inicial de nuestra aplicación. Su aspecto visual vendrá determinado por la jsp del mismo nombre, bienvenido.jsp. El contenido de bienvenido.jsp es:

<html>
<head>Bienvenido</head>
<body>
<a href="${flowExecutionUrl}&_eventId=next">Next</a>
</body>
</html>

Mirando el fichero bienvenido.xml y bienvenido.jsp podemos deducir fácilmente el flujo de nuestra aplicación. Bienvenido.jsp tendrá un enlace con un evento next. Cuando se seleccione este enlace nuestro flujo nos indica que haremos una transición al estado list.
List.jsp. Es una jsp en la que pintaremos la lista de nuestros coches guardados en base de datos. Miramos el fichero bienvenido.xml y veremos que antes de pintar la jsp ejecutaremos una llamada al servicio carService.getCars(). La lista de coches que devuelva este servicio se almacenará en una variable requestScope llamada cars. Esta variable cars será la que recorra nuestra jsp para obtener los coches.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<html>
<head>
<title>Car list</title>

</head>
<body>
	<table>
		<tr>
			<th>Company</th>
			<th>Model</th>
			<th>Price</th>
		</tr>
		<h2>Car list</h2>
		<c:forEach items="${cars}" var="car">
			<tr>
				<td><c:out value="${car.company}" /></td>
				<td><c:out value="${car.model}" /></td>
				<td><c:out value="${car.price}" /></td>
			</tr>
		</c:forEach>
	</table>

</body>
</html>

Por último pego mi pom.xml para la gestión de dependencias. Añado el plugin de jetty para incluir en este contenedor web nuestra aplicación directamente desde Maven.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hopcroft.examples.spring</groupId>
	<artifactId>simpleWebFlowApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<build>
		<plugins>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>8.0.0.M3</version>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>javax.security</groupId>
			<artifactId>jacc</artifactId>
			<version>1.0</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.10</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>3.5.6-Final</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.ow2.easybeans</groupId>
			<artifactId>easybeans-uberjar-hibernate</artifactId>
			<version>1.2.1</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-jpamodelgen</artifactId>
			<version>1.0.0.Final</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.webflow</groupId>
			<artifactId>spring-webflow</artifactId>
			<version>2.3.0.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.1.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.webflow</groupId>
			<artifactId>spring-binding</artifactId>
			<version>2.3.0.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.webflow</groupId>
			<artifactId>spring-js</artifactId>
			<version>2.3.0.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
	</dependencies>
</project>

En el navegador nuestra aplicación se verá así. Podemos ver los estados generados en la url del navegador

pantallaInicioWebFlow-hop2croft-wordpress-com

pantallaListadoWebFlow-hop2croft-wordpress-com

Nuestra aplicación es bastante sencilla y sólo usa estados de Spring Web Flow de tipo vista. Pero Spring Web Flow tiene estados de acción, de decisión, de subflow y de finalización para definir mejor los flujos de nuestra aplicación.

Basamos nuestra aplicación en jsps pero podemos incluir en Spring Web Flow los componentes JSF de los distintos frameworks como IceFaces, RichFaces, … que existen. Spring tiene su propio proyecto para JSF llamados Spring Faces. Igualmente podemos utilizar el toolkit de Dojo mediante el proyecto Spring Javascript. A ver si me animo y me hago un post al respecto.

 
6 comentarios

Publicado por en 5 julio, 2011 en Spring Web Flow

 

Etiquetas: , , , , ,

6 Respuestas a “Ejemplo básico de Spring Web Flow

  1. Dante

    19 julio, 2011 at 11:22 pm

    Falta el index.jsp macho, es bastante importante…;-)

     
    • hop2croft

      22 julio, 2011 at 9:22 am

      Hola Dante,

      ya he añadido el index.jsp en la entrada.

      Saludos,

       
  2. Lennarth Anaya

    6 junio, 2013 at 5:43 pm

    Muchas gracias.

    El uso del siguiente tag, en el archivo bienvenido.xml, es lo único que me faltó en tu post para terminar de comprender la forma de utilizarlo en mi proyecto:

     
    • hop2croft

      15 agosto, 2013 at 8:55 pm

      Hola Lennarth,

      gracias por leer el blog. El contenido de bienvenido.xml es el siguiente:

      Un saludo y gracias de nuevo

       
  3. Hostile05g

    24 febrero, 2014 at 12:06 pm

    Reblogueó esto en Diario de un mercenario informático hostily comentado:
    Sencillo y completo ejemplo de spring web flow

     

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: