sábado, 30 de noviembre de 2013

Spring MVC: Formularios + Validator

Si bien venimos lento pero seguro en el entendimiento de MVC de spring, me pareció que ya estamos en condiciones de hacer un poco de desarrollo un tanto mas real. Algo que de seguro vamos a tener que trabajar son los formularios.

Nuestro ejemplo es simplemente un formulario con una serie de controles los cuales en primera instancia deben ser inicializados y posterior al submite deben ser visualizados en otra pagina.

Formulario inicializado



Realizado el Submit del formulario




Validación sobre datos del formulario





Cuales son los objetos de estudio de esta guía:

  • Aprenderemos a manejar diferentes controles para construcción del formulario.
  • Aprenderemos como inicializar valores en controles.
  • Aprenderemos a realizar validaciones sobre los datos cargados del formulario.
  • Aprenderemos a trasladar información a otra vista.

Como en todo los ejemplo en este caso utilizaremos 2 vistas  con las cuales vamos a trabajar , primero la vista del formulario y segundo la vista que utilizaremos para mostrar los datos que enviamos desde el formulario.


Buenisimo !!! ahora como hacemos esto. jajajaja


Pero como trabajamos con MVC 

Inicia con un Request



Finaliza con Response

Cuando se envía una solicitud a la Spring Framework MVC la siguiente secuencia de los acontecimientos suceden.

El DispatcherServlet recibe por primera vez la solicitud.

La consulta a la DispatcherServlet Handler Mapping e invoca el controlador asociado a la solicitud.

El proceso regulador de la solicitud llamando a los métodos apropiados de servicio y devuelve un objeto ModeAndView a la DispatcherServlet. 

El objeto ModeAndView contiene los datos del modelo y el nombre de vista.

El DispatcherServlet envía el nombre de la vista a un ViewResolver para encontrar la visión real de invocar.

Ahora, el DispatcherServlet pasará el modelo de objetos a la vista para hacer que el resultado.

La vista con la ayuda de los datos del modelo hará que el resultado de vuelta al usuario.


Paso 0: Definir nuestro proyecto y archivo POOM.

Definamos nuestro proyecto 












El siguiente paso es definir nuestro archivo pom para la gestión de dependencias.

pom.xml: definiremos todas las librerías necesarias para trabajar con nuestro ejemplo.


<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>mvc_MultiActionControllerAnnotation</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>mvc_MultiActionControllerAnnotation Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
   <spring.version>3.0.5.RELEASE</spring.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency> 
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.1.2</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
 </dependency>            
 <dependency>
  <groupId>taglibs</groupId>
  <artifactId>standard</artifactId>
  <version>1.1.2</version>
 </dependency>
  </dependencies>
  <build>
    <finalName>mvc_MultiActionControllerAnnotation</finalName>
  </build>
</project>

En esta ocacion no utilizaremos ninguna libreria rara, solo spring y las taglib para trabajar con JSP un poquito.

Paso 1: Definir el dispacher

web.xml: Primero debemos modificar nuestro archivo web.xml de base para definir el dispachservlet de spring y la locacion del spring bean configuration file.


<web-app id="WebApp_ID" 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>Spring Web MVC Application</display-name>
  
  <servlet>
   <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

    <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
  </context-param>
  
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  
</web-app>

En este archivo lo que realizamos es mendiante definimos el dispatchservlet y el orden de prioridad para las solicitudes. La tarea de este recurso es procesar las llamadas realizadas a la aplicación y determinar que Controlador sera el encargado de atenderla para su resolución.

Luego mediante definimos el patron de filtro para la escucha de solicitudes si por ejemplo agregamos *.htm le estamos indicando que escuche todo las llamadas que hagan referencia a archivos htm.

Mediante definimos donde buscara el spring bean configuration file que utilizara nuestro proyecto. Ahora si esta información no es proporcionada en el archivo de analisis, spring por si solo buscara el archivo en WEB-INF  con el nombre resultante de una concatenacion simple.

lo que pusimos en servlet-name + "-servlet.xml", por consiguiente si tuviera una definición como  <servlet-name>mvc-dispatcher</servlet-name>  la busqueda del archivo se realizaria a mvc-dispatcher-servlet.xml.

 Por ultimo tendremos la definición de nuestro ContextLoaderListener es el encargado de inicializar todo el contexto del framework para el funcionamiento de la mecanica MVC.

Paso 2: Definir controlador,  modelo,  vista y Validator 

Antes de comenzar a trabajar con nuestro controlador es necesario afirmar algunos conceptos que creo necesarios.

@ModelAttribute es una notacion de Spring Mvc y por lo general es usada en 2 escenarios:

Primero puede ser usado para inyectar objetos del modelo de datos a la vista, esto hace que sea especialmente útil para asegurar que una JSP tiene todos los datos que necesita.

La segunda es utilizarlo para leer datos de un modelo de datos existentes y asignarlo al controlador por medio de los parametros de un metodo.

mmmmm de verdad macho no me queda claro!! Me lo cuentas mas fácil ? Si !!

Por ejemplo tenemos un combo en un formulario y en la inicializacion es necesario cargarlos con valores,  que hacemos ¿  A ver en primera instancia nuestra vista tiene que contener la variable sobre la que trabajaremos, por eso imaginemos un pequeño fragmento de codigo jsp para cargar un combo.

<form:select path="country">
<form:option value="NONE" label="--- Select ---"/>
<form:options items="${countryList}" />

</form:select>

Nota: Si observamos no es un codigo completado definimos el control select y posterior definimos una opcion, la primera por default para indicarle al usuario que debe seleccionar, posterior define una opcion que se encuentra asociado a una variable ${countryList}

Ahora necesito el metodo que se encargue de cargar esta variable que por supuesto tendra en su cabecera la notacion @ModelAttribute que debe hacer referencia a la variable antes mencionada ${countryList} para que spring enlace de manera automática esta definición a la necesidad de la vista, miremos un pequeño fragmento de codigo java que servira a los fines.

@ModelAttribute("countryList")
public Map<String,String> populateCountryList() {

//Data referencing for java skills list box
Map<String,String> country = new LinkedHashMap<String,String>();
country.put("US", "United Stated");
country.put("CHINA", "China");
country.put("SG", "Singapore");
country.put("MY", "Malaysia");

return country;

}

Nota: a nivel código no es mas que un método que gestiona una colección y la retorna.

Si si amigo acabamos de realizar la inicializacion de un control de un formulario de una manera facil y desacoplada, por lo que acabamos de abordar el primer escenario.

Ahora el segundo escenario y lo podríamos plantear en un ejemplo de los mas comunes, tengo un formulario que se cargo y se realiza un submit y por supuesto quiero operar con los datos del formulario desde mi controlador para luego realizar otras acciones. 

Primero vamos a tener que definir un método que por supuesto escuche las solicitudes del cliente con @RequestMapping  al cual deberemos indicar que en nuestro caso el metodo debe ser por POST.

@RequestMapping(method = RequestMethod.POST)
public String processSubmit(
@ModelAttribute("customer") Customer customer,
BindingResult result, SessionStatus status) {

...................

}

Nota: Ahora debemos observar que recibimos un modelo que existe por parametros del metodo  para poder trabajar por el desde el controlador, la particularidad es que lo realizamos con @ModelAttribute.

Por otro lado en la mayoría de las veces en un formulario vamos a necesitar una logica de validación para el usuario, para esto spring proporciona la interface Validator que nos va ayudar con este fin.

Hay algunas cosas que necesitamos hacer para implementar nuestra validacion:

  • Crear una clase de validador para algún modelo de dominio y implementar la interfaz Validator.
  • La clase Sobre cargar el metodo supports donde proporcionamos la clase en la que el validator se apoya.
  • La sobrecarga al método Validate que es quien valida la información e informa los errores por medio del objeto error.


Primero vamos a mirar nuestro formulario para entender algunos conceptos.


<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<style>
.error {
 color: #ff0000;
}
.errorblock{
 color: #000;
 background-color: #ffEEEE;
 border: 3px solid #ff0000;
 padding:8px;
 margin:16px;
}

</style>
</head>

<body>
<h2>Formulario de validacion con Spring</h2>

<form:form method="POST" commandName="customer">

<form:errors path="*" cssClass="errorblock" element="div"/>

<table>
<tr>
<td>Nombre de Usuario : </td>
<td><form:input path="userName" /></td>
<td><form:errors path="userName" cssClass="error" /></td>
</tr>
<tr>
<td>Direccion : </td>
<td><form:textarea path="address" /></td>
<td><form:errors path="address" cssClass="error" /></td>
</tr>
<tr>
<td>Password : </td>
<td><form:password path="password" /></td>
<td><form:errors path="password" cssClass="error" /></td>
</tr>
<tr>
<td>Confirmacion Password : </td>
<td><form:password path="confirmPassword" /></td>
<td><form:errors path="confirmPassword" cssClass="error" /></td>
</tr>
<tr>
<td>Suscribir newsletter? : </td>
<td><form:checkbox path="receiveNewsletter" /></td>
<td><form:errors path="receiveNewsletter" cssClass="error" /></td>
</tr>
<tr>
<td>Frameworks Favorito: </td>
<td>
 <form:checkboxes items="${webFrameworkList}" path="favFramework" /> 
</td>
<td><form:errors path="favFramework" cssClass="error" /></td>
</tr>
<tr>
<td>Sexo: </td>
<td>
<form:radiobutton path="sex" value="M"/>Male 
<form:radiobutton path="sex" value="F"/>Female 
</td>
<td><form:errors path="sex" cssClass="error" /></td>
</tr>
<tr>
<td>Seleccione number : </td>
<td>
 <form:radiobuttons path="favNumber" items="${numberList}"  /> 
</td>
<td><form:errors path="favNumber" cssClass="error" /></td>
</tr>

<tr>
<td>Country : </td>
<td>
<form:select path="country">
 <form:option value="NONE" label="--- Select ---"/>
 <form:options items="${countryList}" />
</form:select>
</td>
<td><form:errors path="country" cssClass="error" /></td>
</tr>
 
<tr>
<td>Java Skills : </td>
<td>
<form:select path="javaSkills" items="${javaSkillsList}" multiple="true" />
</td>
<td><form:errors path="javaSkills" cssClass="error" /></td>
</tr>


<form:hidden path="secretValue" />

<tr>
<td colspan="3"><input type="submit" /></td>
</tr>
</table>
</form:form>

</body>
</html>

Lo primero que debemos destacar es que en nuestro formulario vamos a encontrar:

form:form : hace referencia por medio de commandName al modelo que sera expuesto desde el controlador por medio de modelAttribute en nuestro caso se llama "customer",  un poco mas adelante veremos la definición del modelo.

Por otro lado vamos a encontrar controles de usuario y su correspondiente objeto de error que son los que utiliza spring junto a la clase validator para generar una respuesta de error.

Ejemplo 

<tr>
<td>UserName : </td>
<td><form:input path="userName" /></td>
<td><form:errors path="userName" cssClass="error" /></td>

</tr>

Nota: un tema donde coloquemos el form:error asociado al campos es donde en tiempos de ejecucion aparecer por ejemplo la leyenda de validacion por dar un ejemplo "El campo username es requerido".

Por otro parte vamos a encontrar codigo por ejemplo la generacion de N controles 

 checkbox

<form:checkboxes items="${webFrameworkList}" path="favFramework" />

Radiobutton

<form:radiobuttons path="favNumber" items="${numberList}"  />

Nota:  donde debemos prestar atención son en las variables ${webFrameworkList} , ${numberLista}  por que de seguro es una colección que cargaremos desde nuestra controladora por medio de metodos.

Por otro lado vamos a encontrar el codigo para la carga de opciones de un combobox que la unica diferencia en comparación con la generacion de N controles es sintaxis.

<form:select path="country">
<form:option value="NONE" label="--- Select ---"/>
<form:options items="${countryList}" />

</form:select>

Creo que la siguiente parte es definir nuestro modelo que sera el que utilizaremos para la comunicación de datos entre la vista y el controlador.


package com.company.customer.model;

public class Customer{
 
 //textbox
 String userName;
 
 //textarea
 String address;
 
 //password
 String password;
 String confirmPassword;
 
 //checkbox
 boolean receiveNewsletter;
 String [] favFramework;
 
 //radio button
 String favNumber;
 String sex;
 
 //dropdown box
 String country;
 String javaSkills;
 
 //hidden value
 String secretValue;
 
 public String getSecretValue() {
  return secretValue;
 }
 public void setSecretValue(String secretValue) {
  this.secretValue = secretValue;
 }
 public String getUserName() {
  return userName;
 }
 public void setUserName(String userName) {
  this.userName = userName;
 }
 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public String getConfirmPassword() {
  return confirmPassword;
 }
 public void setConfirmPassword(String confirmPassword) {
  this.confirmPassword = confirmPassword;
 }
 public boolean isReceiveNewsletter() {
  return receiveNewsletter;
 }
 public void setReceiveNewsletter(boolean receiveNewsletter) {
  this.receiveNewsletter = receiveNewsletter;
 }
 public String[] getFavFramework() {
  return favFramework;
 }
 public void setFavFramework(String[] favFramework) {
  this.favFramework = favFramework;
 }
 public String getFavNumber() {
  return favNumber;
 }
 public void setFavNumber(String favNumber) {
  this.favNumber = favNumber;
 }
 public String getSex() {
  return sex;
 }
 public void setSex(String sex) {
  this.sex = sex;
 }
 public String getCountry() {
  return country;
 }
 public void setCountry(String country) {
  this.country = country;
 }
 public String getJavaSkills() {
  return javaSkills;
 }
 public void setJavaSkills(String javaSkills) {
  this.javaSkills = javaSkills;
 }
}

Nota: A  nivel codigo nos vamos a dar cuenta que no es otra cosa que una clase con sus correspondientes getter y setter.

Ahora debemos entender el validattor que generamos para nuestra vista.


package com.company.customer.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.company.customer.model.Customer;

public class CustomerValidator implements Validator{

 public boolean supports(Class clazz) {
  //just validate the Customer instances
  return Customer.class.isAssignableFrom(clazz);

 }

 public void validate(Object target, Errors errors) {
  
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
    "required.userName", "El campo Nombre de Usuario es Necesario.");
  
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address",
    "required.address", "El campo direccion es Necesarios.");
  
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password",
    "required.password", "El campo clave es Necesarios.");
   
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "confirmPassword",
    "required.confirmPassword", "El campo clave es Necesarios.");
  
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "sex", 
    "required.sex", "El campo sexo es Necesarios.");
  
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "favNumber", 
    "required.favNumber", "El campo Numero es Necesarios.");
  
  ValidationUtils.rejectIfEmptyOrWhitespace(
    errors, "javaSkills", "required.javaSkills","El campo Skill es Necesarios.");
  
  Customer cust = (Customer)target;
  
  if(!(cust.getPassword().equals(cust.getConfirmPassword()))){
   errors.rejectValue("password", "notmatch.password");
  }
  
  if(cust.getFavFramework().length==0){
   errors.rejectValue("favFramework", "required.favFrameworks");
  }

  if("NONE".equals(cust.getCountry())){
   errors.rejectValue("country", "required.country");
  }
  
 }
 
}

Donde en el metodo supports asignamos la clase sobre la que se apoya en validattor.

Para el metodo Validate tiene 2 parametros, target hace referencia al objeto que sera validado y el parametro error hace referencia a los objetos definidos en la vista.

Para la validación de algo tan típico como determinar si el campo de entrada está vacío podemos utilizar la clase ValidationUtils de Spring, donde podemos determinar el nombre del campo afectado, que mensaje internacionalizado e incluso parametrizado queremos mostrar, y en caso de no encontrar la clave del mensaje, poder mostrar un texto por defecto, para nuestro ejemplo validados como campo requerido la mayoria de los campos.

Pero como podemos ver es posible realizar validaciones manuales accediendo al objeto por metodo del parametro target  e informando el error asociado a un campo por medio de rejectValue.

Por ejemplo validamos que el combo no tenga seleccionado NONE y si hay error retornamos el error asociado al campo.

if("NONE".equals(cust.getCountry())){
errors.rejectValue("country", "required.country");
}


Ahora es momento de trabajar con nuestro controlador donde lograremos integrar todos los hitos mencionados.

package com.company.customer.controller;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;

import com.company.customer.model.Customer;
import com.company.customer.validator.CustomerValidator;

@Controller
@RequestMapping("/customer.htm")
public class CustomerController{
 
 CustomerValidator customerValidator;
 
 @Autowired
 public CustomerController(CustomerValidator customerValidator){
  this.customerValidator = customerValidator;
 }
 
 @RequestMapping(method = RequestMethod.POST)
 public String processSubmit(
   @ModelAttribute("customer") Customer customer,
   BindingResult result, SessionStatus status) {
  
  customerValidator.validate(customer, result);
  
  if (result.hasErrors()) {
   //if validator failed
   return "CustomerForm";
  } else {
   status.setComplete();
   //form success
   return "CustomerSuccess";
  }
 }
 
 @RequestMapping(method = RequestMethod.GET)
 public String initForm(ModelMap model){
  
  Customer cust = new Customer();
  //Make "Spring MVC" as default checked value
  cust.setFavFramework(new String []{"Spring MVC"});
  
  //Make "Make" as default radio button selected value
  cust.setSex("M");
  
  //make "Hibernate" as the default java skills selection
  cust.setJavaSkills("Hibernate");
  
  //initilize a hidden value
  cust.setSecretValue("I'm hidden value");
  
  //command object
  model.addAttribute("customer", cust);
  
  //return form view
  return "CustomerForm";
 }
 
 
 @ModelAttribute("webFrameworkList")
 public List<String> populateWebFrameworkList() {
  
  //Data referencing for web framework checkboxes
  List<String> webFrameworkList = new ArrayList<String>();
  webFrameworkList.add("Spring MVC");
  webFrameworkList.add("Struts 1");
  webFrameworkList.add("Struts 2");
  webFrameworkList.add("JSF");
  webFrameworkList.add("Apache Wicket");
  
  return webFrameworkList;
 }
 
 @InitBinder
 public void initBinder(WebDataBinder binder) {
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
  
 }
 
 @ModelAttribute("numberList")
 public List<String> populateNumberList() {
  
  //Data referencing for number radiobuttons
  List<String> numberList = new ArrayList<String>();
  numberList.add("Number 1");
  numberList.add("Number 2");
  numberList.add("Number 3");
  numberList.add("Number 4");
  numberList.add("Number 5");
  
  return numberList;
 }
 
 @ModelAttribute("javaSkillsList")
 public Map<String,String> populateJavaSkillList() {
  
  //Data referencing for java skills list box
  Map<String,String> javaSkill = new LinkedHashMap<String,String>();
  javaSkill.put("Hibernate", "Hibernate");
  javaSkill.put("Spring", "Spring");
  javaSkill.put("Apache Wicket", "Apache Wicket");
  javaSkill.put("Struts", "Struts");
  
  return javaSkill;
 }

 @ModelAttribute("countryList")
 public Map<String,String> populateCountryList() {
  
  //Data referencing for java skills list box
  Map<String,String> country = new LinkedHashMap<String,String>();
  country.put("US", "United Stated");
  country.put("CHINA", "China");
  country.put("SG", "Singapore");
  country.put("MY", "Malaysia");
  
  return country;
 }
 
}

Ahora de seguro notamos que en primera instancia la clase controladora lleva un Annotation @controller y que por cada metodo mapeamos el metodo a una url con @RequestMapping.

Avisaaa!!  me quieres decir que con esto voy a poder escuchar en los métodos cuando realicen una solicitud URL como las que mapeamos ?

Ahora los siguientes metodos utilizaran @ModelAttribute haciendo referencia a las variables definidas del lado de la vista para cargar su contenido y decir verdad no presentan grandes problematicas en el codigo, simplemente son metodos que retornan colecciones que precargamos pero podriamos levantar datos de un modelo de dato o archivos fisicos y tendria la misma mecanica.

  • @ModelAttribute("webFrameworkList")
  • @ModelAttribute("numberList")
  • @ModelAttribute("javaSkillsList")
  • @ModelAttribute("countryList")


Por otro lado podemos notar que realizaremos al parecer una inyeccion por contructor para el validator del formulario para poder ser usado en el controlador.

Luego contamos con 2 metodos imporntantes 

@RequestMapping(method = RequestMethod.GET): metodo que se dispara cuando llamamos al formulario. 
@RequestMapping(method = RequestMethod.POST): metodo que se dispara cuando realizamos un post desde el formulario, si observamos es una logica muy simple dispara el validator si hay error retorna a la pagina de formulario para manifestar los errores que encontro, en caso contrario direcciona a una pagina que solo muestra los datos cargados.


Que jodido el tema hasta ahora siempre vimos que el retorno de los metodos era un un ModelAndView pero en este caso son string. Si observamos retornamos String pero no cualquiera sino el nombre de las vistas a la que retornaremos. O sea el ViewResolver redirecciona con string ? Yesss.

Cuando en anteriores entregas el retorno era un ModelAndView que como su nombre lo indica es una composicion del modelo logico de datos y vista o interfaz de usuario. En el contructor le mandamos el nombre de la vista JSP  y luego con el metodo addObjetc le agregamos un valor a key para pasar objetos. 

Ahora ?  con el @ModelAttribute por parametro del metodo invocado le transferimos a la siguiente vista, esto lo  podremos ver en el metodo que realiza el post luego de realizar la validacion nos direcciona a la pagina que mostrara los datos cargados.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<body>
<h2>Spring's form tags example</h2>

<table>
<tr>
<td>UserName :</td><td>${customer.userName}</td>
</tr>
<tr>
<td>Address :</td><td>${customer.address}</td>
</tr>
<tr>
<td>Password :</td><td>${customer.password}</td>
</tr>
<tr>
<td>Confirm Password :</td><td>${customer.confirmPassword}</td>
</tr>
<tr>
<td>Receive Newsletter :</td><td>${customer.receiveNewsletter}</td>
</tr>
<tr>
<td>Favourite Web Frameworks :</td>
<td>
<c:forEach items="${customer.favFramework}" var="current">
   [<c:out value="${current}" />]
</c:forEach>
</td>
</tr>
<tr>
<td>Sex :</td><td>${customer.sex}</td>
</tr>
<tr>
<td>Favourite Number :</td><td>${customer.favNumber}</td>
</tr>
<tr>
<td>Country :</td><td>${customer.country}</td>
</tr>
<tr>
<td>Java Skills :</td><td>${customer.javaSkills}</td>
</tr>
<tr>
<td>Hidden Value :</td><td>${customer.secretValue}</td>
</tr>

</table>

</body>
</html>

Nota: esto podríamos realizarlo con un objeto ModelAndView  transfiriendo el objeto del modelo a la vista y obtendríamos un resultado similar.


Paso 4: Definir nuestro Spring Bean Configuration File

src/main/webapp/WEB-INF/mvc-dispacher-servlet.xml: me voy a contradecir un poco en mis comentarios, aqui es donde comienza la magia, por que definiendo los aspectos necesarios la maquina spring comienza a funcionar.

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

 <context:component-scan base-package="com.company.customer.controller" />
 
 <bean class="com.company.customer.validator.CustomerValidator" />
 
  <!-- Register the Customer.properties -->
 <bean id="messageSource"
  class="org.springframework.context.support.ResourceBundleMessageSource">
  <property name="basename" value="Customer" />
 </bean>
  
 <bean id="viewResolver"
       class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
          <property name="prefix">
              <value>/WEB-INF/pages/</value>
           </property>
          <property name="suffix">
             <value>.jsp</value>
          </property>
    </bean>

</beans>

Pero no veo que definamos el  handler que permite que dispatchservlet pueda interpretar los controladores y pueda  mapear una URL. 

Exacto!!

Con component-scan logramos este objetivo, si con esto buscara las Annotation en el paquete que le indicamos y posibilitara que el mecanismo spring escuche las solicitudes.

Pero en esta ocacion contamos con un nuevo bean que hace referecia a la clase  ResourceBundleMessageSource que nos posibilita leer label de un archivo de recurso que en este caso se llama Customer, si miramos nuestro proyecto encontraremos un archivo de recurso con los label que utiliza nuestro proyecto.

required.userName = El campo Username es Necesario!
required.address = El campo Direccion es Necesario!
required.password = El campo Password es Necesario!
required.confirmPassword = El campo Password es Necesario!
required.favFrameworks = Debe seleccionar Framework!
required.sex = Debe seleccionar el Sexo!
required.favNumber = Debe Seleccionar un Numero!
notmatch.password = El campo Password y confirmar Password no son iguales!
required.country = Debe Seleccionar Country!
required.javaSkills = Debe Seleccionar Skill!

Como ultima opción y no menos importante es la definicion del ViewResolver que es utilizado por el controlador para realizar el redireccionamiento adecuado,  en nuestra definicion le decimos que dentreo del directorio /pages vamos a encontrar nuestras vistas y van a ser de extencion jsp.

Paso 5:  Entendiendo la ejecución

 Para facilitar la prueba definimos un index que haga referencia a URL que posibiliten entender el mapeo que realizamos en el contenedor bean.

Primero ejecutemos en tomcat.





Tendremos una index que nos proporciona el link a nuestro formulario. 


El link nos lleva al formulario de carga.


En primera instancia podremos ver como nuestro formulario se encuentra generado y sus controles tienen los valores que definimos desde la clase controladora. 

Si no ingresamos valores y realizamos el post podremos ver la utilización de los validattor.


Si pasamos la validación correspondiente podremos ver la vista resultado de los datos cargados.



El código del proyecto lo puedes descargar desde aquí.

codigofuente.zip



lunes, 25 de noviembre de 2013

Spring MVC: Entendiendo MultiActionController con Annotation

De una manera practica, es otro de las tantas formas de trabajar con Spring mediante la utilización de Annotation y aqui comienza a ponerse bueno por que es la forma que mas se utiliza por que es menos costosa su implementacion.

Pero voy agradecer a Jose Diaz de Peru quien con su material que se encuentra disponible en la red me brindo una introducción a este framework que si bien tiene mucho tiempo en el mundo java nunca lo había tocado en detalle y gracias a esto quise conocerlo en profundidad y comenzó mi estudio sobre los beneficios de spring.  Si bien trabaje con otros framework en java de verdad al estudiar Spring con profundidad te das cuenta la potencia del mismo para todo lo que un desarrollado puede llegar a necesitar en un proyecto.

Este Articulo forma parte de la guia creada en este sitio sobre Spring MVC.

Por definición:

"Como definir acciones en grupo en un solo controlador utilizando Annotation"

Desde ya hace algunas versión Spring permite el uso de un sistema de anotaciones que simplifica la forma de trabajar con el, logrando que el dispatchservlet pueda interpretar los mapeos sin necesidad de definirlo uno a uno en el spring bean configuration. 

Amigos este es el mundo real en un proyecto grande lo vamos a hacer de esta forma, por que de esta forma no generamos grandes archivos de configuracion en spring, por que es un código mas limpio y por que es mas simple de leer e interpretar por los programadores...etc.

El MultiActionController en Spring nos permite controlar varias peticiones realizadas al servidor en una misma clase controlador, podemos interpretar, como su propio nombre indica, que un MultiActionController no es mas que un conjunto de Spring Controllers en una misma clase.

Donde esta la magia ?

El tema pasa primero por agregar la dependencia necesaria que posibilitara el scaneo de los controladores en nuestros paquetes.

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>

</dependency>

Segundo por definir las anotaciones en nuestro controlador que podremos entenderlo de una manera gráfica. 

002
@Controller: Anotación que registra el controlador para Spring MVC, vamos utilizar esta Annotation en cada controlador que vamos a crear en nuestro proyecto.

@RequestMapping: Anotación que se encarga de relacionar un método con una petición http. Es decir cada uno de nuestros metodos del controlador deberá tener un mapeo con una URL de caso contrario no podria atender una solicitud de manera directa.

Nota: El uso de anotaciones no implica que únicamente tengamos un único controlador sino que nos permite agrupar un conjunto de urls que esten asociadas a nivel de negocio en un controlador especifico. La aplicación soportará n controladores.

003

Finalmente indicarle a spring en su archivo de configuracion que busque en un paquete las Annotation que definimos anteriormente.

<context:component-scan base-package="Mi paquete"/>

Nota: Con este linea le indicamos a spring que busque los controladores y mapeos en el paquete que yo defina.

Para nuestro ejemplo vamos a crear una controladora que responsa a las operaciones CRUD por ejemplo 
  • add
  • delete
  • update
  • list
Nota: nuestro modelo sera Customer  por lo que nuestro controlador se llamara  CustomerController.

Como en todo los ejemplo solo utilizaremos una vista dado que no es el objeto de estudio, y por supuesto lo único que buscamos registrar es nuestro paso por los métodos del controlador y su interacción con una vista, motivo por lo que la logica de cada uno de estos métodos sera básico simplemente pasando un mensaje a la vista.


Buenisimo !!! ahora como hacemos esto. jajajaja


Pero como trabajamos con MVC 

Inicia con un Request



Finaliza con Response

Cuando se envía una solicitud a la Spring Framework MVC la siguiente secuencia de los acontecimientos suceden.

El DispatcherServlet recibe por primera vez la solicitud.

La consulta a la DispatcherServlet Handler Mapping e invoca el controlador asociado a la solicitud.

El proceso regulador de la solicitud llamando a los métodos apropiados de servicio y devuelve un objeto ModeAndView a la DispatcherServlet. 

El objeto ModeAndView contiene los datos del modelo y el nombre de vista.

El DispatcherServlet envía el nombre de la vista a un ViewResolver para encontrar la visión real de invocar.

Ahora, el DispatcherServlet pasará el modelo de objetos a la vista para hacer que el resultado.

La vista con la ayuda de los datos del modelo hará que el resultado de vuelta al usuario.


Paso 0: Definir nuestro proyecto y archivo POOM.

Definamos nuestro proyecto 












El siguiente paso es definir nuestro archivo pom para la gestión de dependencias.

pom.xml: definiremos todas las librerías necesarias para trabajar con nuestro ejemplo.


<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>mvc_MultiActionControllerAnnotation</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>mvc_MultiActionControllerAnnotation Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
   <spring.version>3.0.5.RELEASE</spring.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency> 
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.1.2</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
 </dependency>            
 <dependency>
  <groupId>taglibs</groupId>
  <artifactId>standard</artifactId>
  <version>1.1.2</version>
 </dependency>
  </dependencies>
  <build>
    <finalName>mvc_MultiActionControllerAnnotation</finalName>
  </build>
</project>

En esta ocacion no utilizaremos ninguna libreria rara, solo spring y las taglib para trabajar con JSP un poquito.

Paso 1: Definir el dispacher

web.xml: Primero debemos modificar nuestro archivo web.xml de base para definir el dispachservlet de spring y la locacion del spring bean configuration file.


<web-app id="WebApp_ID" 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>Spring Web MVC Application</display-name>
  
  <servlet>
   <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

    <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
  </context-param>
  
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  
</web-app>

En este archivo lo que realizamos es mendiante definimos el dispatchservlet y el orden de prioridad para las solicitudes. La tarea de este recurso es procesar las llamadas realizadas a la aplicación y determinar que Controlador sera el encargado de atenderla para su resolución.

Luego mediante definimos el patron de filtro para la escucha de solicitudes si por ejemplo agregamos *.htm le estamos indicando que escuche todo las llamadas que hagan referencia a archivos htm.

Mediante definimos donde buscara el spring bean configuration file que utilizara nuestro proyecto. Ahora si esta información no es proporcionada en el archivo de analisis, spring por si solo buscara el archivo en WEB-INF  con el nombre resultante de una concatenacion simple.

lo que pusimos en servlet-name + "-servlet.xml", por consiguiente si tuviera una definición como  <servlet-name>mvc-dispatcher</servlet-name>  la busqueda del archivo se realizaria a mvc-dispatcher-servlet.xml.

 Por ultimo tendremos la definición de nuestro ContextLoaderListener es el encargado de inicializar todo el contexto del framework para el funcionamiento de la mecanica MVC.

Paso 2: Definir nuestro controlador

Para nuestro ejemplo vamos a definir 1 controlador muy simples con el cual vamos a trabajar para que pueda reconocer diferentes metodos desde spring bean configuration. 

Como lo unico que intentamos es demostrar nuestros paso por los metodos definidos en el controlador, vamos a crear los diferentes metodos crud en la clase y la logica simplemente sera pasar un mensaje  a la vista que indique que paso por el metodo.

Los métodos crud son estos y la definición del nombre te garantizo que lo vas a entender mucho mas adelante.
  • add
  • delete
  • update
  • list
Ahora estos metodo retorna un ModelAndView que como su nombre lo indica es una composicion del modelo logico de datos y vista o interfaz de usuario. En pocas palabras representa la vista y el modelo devueltos por un controlador al dispatchservlet, la vista toma la forma de una referencia a un objeto view o nombre de vista que por supuesto sera analizado y redireccionado por un ViewResolver. El modelo es un mapa, lo que permite el uso de múltiples objetos de datos con clave de nombre.

Nota: ModelAndView es un objeto que vale la pena mirarlo mas a fondo por que tiene muchos metodos y formas de crearlo de acuerdo a nuestra necesidad. 

En nuestro ejemplo en el contructor le mandamos el nombre de la vista JSP "PaginaRetorno" y luego con el metodo addObjetc le agregamos un valor a key de nombre msg. Esto no es magia de seguro nuestra vista PaginaRetorno debe contener la clave a la que hacemos referencia por que de caso contrario no va tirar en Tucumano  "un loco que haces esto a lo que haces referencia no existe en la vista"

Nota son controladores muy simple solo pasamos a la vista en cada metodo una leyenda que corresponde a la operacion a la que atendimos.

com.company.controlador.CustomerController.java: definimos nuestro controlador.

package com.company.controlador;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

@Controller
public class CustomerController {
 
 @RequestMapping("/customer/add.htm")
 public ModelAndView add(HttpServletRequest request,
  HttpServletResponse response) throws Exception {
  
  return new ModelAndView("PaginaRetorno", "msg","add() method");
   
 }
 @RequestMapping("/customer/delete.htm")
 public ModelAndView delete(HttpServletRequest request,
  HttpServletResponse response) throws Exception {
   
  return new ModelAndView("PaginaRetorno", "msg","delete() method");
    
 }
 @RequestMapping("/customer/update.htm")
 public ModelAndView update(HttpServletRequest request,
  HttpServletResponse response) throws Exception {
   
  return new ModelAndView("PaginaRetorno", "msg","update() method");
    
 }
 @RequestMapping("/customer/list.htm")
 public ModelAndView list(HttpServletRequest request,
  HttpServletResponse response) throws Exception {
    
  return new ModelAndView("PaginaRetorno", "msg","list() method");
     
 }
 
}

Ahora de seguro notamos que en primera instancia la clase controladora lleva un Annotation @controller y que por cada metodo mapeamos el metodo a una url con @RequestMapping.

Avisaaa!!  me quieres decir que con esto voy a poder escuchar en los métodos cuando realicen una solicitud URL como las que mapeamos ?

Sip cuando realicemos una llamada a estas URL vamos a escuchar por cada uno de estos metodos.

 Nota Personal: Si te apaga la maquina y cuando levanta te olvidas que tenes un apache ya escuchando por el puerto 8080  y el tomcat no responde, por favor no demores minutos en pensar que la vida te engaño, fijate si no hay algun proceso ocupando el puerto en lugar de perder el tiempo. que Salame nooo ? jajaja. sisi apache estaba en 8080 motivo por el que tomcat nunca subio al puerto.

Pero volviendo a la nuestro vamos a poder escuchar por las siguientes URL asociadas a cada metodo de la siguiente forma.

http://localhost:8080/mvc_MultiActionControllerAnnotation/customer/add.htm
http://localhost:8080/mvc_MultiActionControllerAnnotation/customer/delete.htm
http://localhost:8080/mvc_MultiActionControllerAnnotation/customer/update.htm
http://localhost:8080/mvc_MultiActionControllerAnnotation/customer/list.htm


Paso 3: Definir nuestro Vista 

src/main/webapp/WEB-INF/pages/PaginaRetorno.jsp: Es simplemente la vista que utilizamos para el ejemplo.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h1>Spring MVC Ejemplo</h1>

<h2>${msg}</h2>

</body>
</html>


Primero que nada  Vamos a usar la Librería Estandar JSP (JSP Standard Tag Library - JSTL) por lo que primero tenemos que comenzar incluyéndolas en nuestro pom.XML de caso contrario nuestro jsp se va romper por todos lados, las librerías usadas son:




  • JSTL



  • TAGLIBS


  • Ojo ya que estamos hablemos de una buena practica en construir archivos JSP como la utilizacion de las taglibs va ser de moneda corriente en todo los jsp con los que vamos a trabajar, una buena practica es definir un archivo include.jsp como cabecera que tendra lo que sea necesario en todos los jsp que vamos a crear.

    ejemplo de un include.jsp

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

    una pagina ejemplo jsp que utilice esta buena practica seria 


    <%@ include file="/WEB-INF/views/include.jsp" %>
    <html>
      <head><title>Hello :: Spring Application</title></head>
      <body>
        <h1>Hello - Spring Application</h1>
        <p>Greetings, it is now <c:out value="${now}"/></p>
      </body>
    </html>

    Pero de verdad nos fuimos por las ramas, de vuelta con nuestro ejemplo encontramos la clave ${msg} que es a la cual hicimos referencia desde nuestros controladores. Te das cuenta que esto no es magia!! para todo una linea de código.


    Paso 4: Definir nuestro Spring Bean Configuration File

    src/main/webapp/WEB-INF/mvc-dispacher-servlet.xml: me voy a contradecir un poco en mis comentarios, aqui es donde comienza la magia, por que definiendo los aspectos necesarios la maquina spring comienza a funcionar.


    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
     
     <context:component-scan base-package="com.company.controlador" />
     
     
     <bean id="viewResolver"
         class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
            <property name="prefix">
                <value>/WEB-INF/pages/</value>
            </property>
            <property name="suffix">
                <value>.jsp</value>
            </property>
        </bean>
    </beans>
    

    Pero no veo que definamos el  handler que permite que dispatchservlet pueda interpretar los controladores y pueda  mapear una URL. 

    Exacto!!

    Con component-scan logramos este objetivo, si con esto buscara las Annotation en el paquete que le indicamos y posibilitara que el mecanismo spring escuche las solicitudes.

    Como ultima opción y no menos importante es la definicion del ViewResolver que es utilizado por el controlador para realizar el redireccionamiento adecuado,  en nuestra definicion le decimos que dentreo del directorio /pages vamos a encontrar nuestras vistas y van a ser de extencion jsp.

    Paso 5:  Entendiendo la ejecución

     Para facilitar la prueba definimos un index que haga referencia a URL que posibiliten entender el mapeo que realizamos en el contenedor bean.

    Primero ejecutemos en tomcat.





    Tendremos una index que nos proporciona los link para visualizar los diferentes posibilidades que definimos en nuestro archivo de configuración. 



    Las siguientes imágenes en su URL podrán mostrarnos la ejecución de cada uno de los métodos que definimos en la clase controladora.








    El código del proyecto lo puedes descargar desde aquí.

    codigofuente.zip



    domingo, 24 de noviembre de 2013

    Spring MVC: Controlador MultiActionController

    De una manera practica, es otro de los tantos tipos de controladores que podemos definir con la utilización de spring.

    Este Articulo forma parte de la guia creada en este sitio sobre Spring MVC.

    Por definición:

    "Como definir acciones en grupo en un solo controlador"

    El MultiActionController en Spring nos permite controlar varias peticiones realizadas al servidor en una misma clase controlador, podemos interpretar, como su propio nombre indica, que un MultiActionController no es mas que un conjunto de Spring Controllers en una misma clase.

    Lo primero es crear nuestra clase que extendera de la clase MultiActionController, luego establecemos los métodos de la clase, igual que crearíamos el handleRequest en un Controller, pero implementando todos los que queramos dándoles el nombre que nos interese.

    Para nuestro ejemplo vamos a crear una controladora que responsa a las operaciones CRUD por ejemplo 

    • AddCustomer
    • deleteCustomer
    • updateCustomer
    • listCustomer

    Como en todo los ejemplo solo utilizaremos una vista dado que no es el objeto de estudio, y por supuesto lo único que buscamos registrar es nuestro paso por los métodos del controlador y su interacción con una vista, motivo por lo que la logica de cada uno de estos métodos sera básico simplemente pasando un mensaje a la vista.


    Buenisimo !!! ahora como hacemos esto. jajajaja


    Pero como trabajamos con MVC 

    Inicia con un Request



    Finaliza con Response

    Cuando se envía una solicitud a la Spring Framework MVC la siguiente secuencia de los acontecimientos suceden.

    El DispatcherServlet recibe por primera vez la solicitud.

    La consulta a la DispatcherServlet Handler Mapping e invoca el controlador asociado a la solicitud.

    El proceso regulador de la solicitud llamando a los métodos apropiados de servicio y devuelve un objeto ModeAndView a la DispatcherServlet. 

    El objeto ModeAndView contiene los datos del modelo y el nombre de vista.

    El DispatcherServlet envía el nombre de la vista a un ViewResolver para encontrar la visión real de invocar.

    Ahora, el DispatcherServlet pasará el modelo de objetos a la vista para hacer que el resultado.

    La vista con la ayuda de los datos del modelo hará que el resultado de vuelta al usuario.


    Paso 0: Definir nuestro proyecto y archivo POOM.

    Definamos nuestro proyecto 












    El siguiente paso es definir nuestro archivo pom para la gestión de dependencias.

    pom.xml: definiremos todas las librerías necesarias para trabajar con nuestro ejemplo.


    <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/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.company</groupId>
      <artifactId>mvc_MultiActionController</artifactId>
      <packaging>war</packaging>
      <version>0.0.1-SNAPSHOT</version>
      <name>mvc_MultiActionController Maven Webapp</name>
      <url>http://maven.apache.org</url>
      <properties>
       <spring.version>3.0.5.RELEASE</spring.version>
      </properties>
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
        <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-core</artifactId>
       <version>${spring.version}</version>
      </dependency>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>${spring.version}</version>
      </dependency>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
       <version>${spring.version}</version>
      </dependency>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-web</artifactId>
       <version>${spring.version}</version>
      </dependency> 
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>${spring.version}</version>
      </dependency>
      <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
     </dependency>
     <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
     </dependency>            
     <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
     </dependency>
      </dependencies>
      <build>
        <finalName>mvc_MultiActionController</finalName>
      </build>
    </project>
    

    En esta ocacion no utilizaremos ninguna libreria rara, solo spring y las taglib para trabajar con JSP un poquito.

    Paso 1: Definir el dispacher

    web.xml: Primero debemos modificar nuestro archivo web.xml de base para definir el dispachservlet de spring y la locacion del spring bean configuration file.


    <web-app id="WebApp_ID" 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>Spring Web MVC Application</display-name>
      
      <servlet>
       <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
      <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
      </servlet-mapping>
    
        <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
      </context-param>
      
      <listener>
        <listener-class>
          org.springframework.web.context.ContextLoaderListener
        </listener-class>
      </listener>
      
    </web-app>
    

    En este archivo lo que realizamos es mendiante definimos el dispatchservlet y el orden de prioridad para las solicitudes. La tarea de este recurso es procesar las llamadas realizadas a la aplicación y determinar que Controlador sera el encargado de atenderla para su resolución.

    Luego mediante definimos el patron de filtro para la escucha de solicitudes si por ejemplo agregamos *.htm le estamos indicando que escuche todo las llamadas que hagan referencia a archivos htm.

    Mediante definimos donde buscara el spring bean configuration file que utilizara nuestro proyecto. Ahora si esta información no es proporcionada en el archivo de analisis, spring por si solo buscara el archivo en WEB-INF  con el nombre resultante de una concatenacion simple.

    lo que pusimos en servlet-name + "-servlet.xml", por consiguiente si tuviera una definición como  <servlet-name>mvc-dispatcher</servlet-name>  la busqueda del archivo se realizaria a mvc-dispatcher-servlet.xml.

     Por ultimo tendremos la definición de nuestro ContextLoaderListener es el encargado de inicializar todo el contexto del framework para el funcionamiento de la mecanica MVC.

    Paso 2: Definir nuestro controlador

    Para nuestro ejemplo vamos a definir 1 controlador muy simples con el cual vamos a trabajar para que pueda reconocer diferentes metodos desde spring bean configuration. 

    Como lo unico que intentamos es demostrar nuestros paso por los metodos definidos en el controlador, vamos a crear los diferentes metodos crud en la clase y la logica simplemente sera pasar un mensaje  a la vista que indique que paso por el metodo.

    Los métodos crud son estos y la definición del nombre te garantizo que lo vas a entender mucho mas adelante.
    • testaddCustomer
    • testdeleteCustomer
    • testupdateCustomer
    • testlistCustomer
    Ahora estos metodo retorna un ModelAndView que como su nombre lo indica es una composicion del modelo logico de datos y vista o interfaz de usuario. En pocas palabras representa la vista y el modelo devueltos por un controlador al dispatchservlet, la vista toma la forma de una referencia a un objeto view o nombre de vista que por supuesto sera analizado y redireccionado por un ViewResolver. El modelo es un mapa, lo que permite el uso de múltiples objetos de datos con clave de nombre.

    Nota: ModelAndView es un objeto que vale la pena mirarlo mas a fondo por que tiene muchos metodos y formas de crearlo de acuerdo a nuestra necesidad. 

    En nuestro ejemplo en el contructor le mandamos el nombre de la vista JSP "PaginaRetorno" y luego con el metodo addObjetc le agregamos un valor a key de nombre msg. Esto no es magia de seguro nuestra vista PaginaRetorno debe contener la clave a la que hacemos referencia por que de caso contrario no va tirar en Tucumano  "un loco que haces esto a lo que haces referencia no existe en la vista"

    Nota son controladores muy simple solo pasamos a la vista en cada metodo una leyenda que corresponde a la operacion a la que atendimos.

    com.company.controlador.CustomerController.java: definimos nuestro controlador.

    package com.company.controlador;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
    
    public class CustomerController extends MultiActionController{
     
     public ModelAndView testaddCustomer(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
      
      return new ModelAndView("PaginaRetorno", "msg","add() method");
       
     }
     
     public ModelAndView testdeleteCustomer(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
       
      return new ModelAndView("PaginaRetorno", "msg","delete() method");
        
     }
     
     public ModelAndView testupdateCustomer(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
       
      return new ModelAndView("PaginaRetorno", "msg","update() method");
        
     }
     
     public ModelAndView testlistCustomer(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
        
      return new ModelAndView("PaginaRetorno", "msg","list() method");
         
     }
     
    }
    


    Paso 3: Definir nuestro Vista 

    src/main/webapp/WEB-INF/pages/PaginaRetorno.jsp: Es simplemente la vista que utilizamos para el ejemplo.

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
    <h1>Spring MVC Ejemplo</h1>
    
    <h2>${msg}</h2>
    
    </body>
    </html>
    


    Primero que nada  Vamos a usar la Librería Estandar JSP (JSP Standard Tag Library - JSTL) por lo que primero tenemos que comenzar incluyéndolas en nuestro pom.XML de caso contrario nuestro jsp se va romper por todos lados, las librerías usadas son:




  • JSTL



  • TAGLIBS


  • Ojo ya que estamos hablemos de una buena practica en construir archivos JSP como la utilizacion de las taglibs va ser de moneda corriente en todo los jsp con los que vamos a trabajar, una buena practica es definir un archivo include.jsp como cabecera que tendra lo que sea necesario en todos los jsp que vamos a crear.

    ejemplo de un include.jsp

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

    una pagina ejemplo jsp que utilice esta buena practica seria 


    <%@ include file="/WEB-INF/views/include.jsp" %>
    <html>
      <head><title>Hello :: Spring Application</title></head>
      <body>
        <h1>Hello - Spring Application</h1>
        <p>Greetings, it is now <c:out value="${now}"/></p>
      </body>
    </html>

    Pero de verdad nos fuimos por las ramas, de vuelta con nuestro ejemplo encontramos la clave ${msg} que es a la cual hicimos referencia desde nuestros controladores. Te das cuenta que esto no es magia!! para todo una linea de código.


    Paso 4: Definir nuestro Spring Bean Configuration File

    src/main/webapp/WEB-INF/mvc-dispacher-servlet.xml: me voy a contradecir un poco en mis comentarios, aqui es donde comienza la magia, por que definiendo los aspectos necesarios la maquina spring comienza a funcionar.


    <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.5.xsd">
     
     <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
        
        <bean class="com.company.controlador.CustomerController">
         <property name="methodNameResolver">
       <bean class="org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver">
        <property name="prefix" value="test" />
        <property name="suffix" value="Customer" />
       </bean>
      </property>
        </bean>
     
     
     <bean id="viewResolver"
         class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
            <property name="prefix">
                <value>/WEB-INF/pages/</value>
            </property>
            <property name="suffix">
                <value>.jsp</value>
            </property>
        </bean>
    </beans>
    

    como primera instancia vamos a definir el handler que vamos a utilizar se llama ControllerClassNameHandlerMapping, ojo por que este es el que permite que dispatchservlet pueda interpretar que mediante un bean mapeamos una URL  a una controladora.

    Ahora el siguiente paso es por supuesto definir el bean para el controlador que en nuestro caso hace referencia al que definimos CustomerController, los siguiente es configurar la propiedad methodNameResolver que sera la que se encarga de establecer el método a ejecutar mediante la opción del parámetro que establezcamos.

    Ahora InternalPathMethodNameResolver es el que se encarga de resolver los metodos de acuerdo a patrones de URL y en nuestro caso utilizamos 2 propiedades

    • prefix = test   (por esto todo los metodos que definimos en el controlador comienzan con test)
    • suffix  = Customer  (por esto todo los metodos que definimos en el controlador terminan con customer)

    Recordemos
    • testaddCustomer
    • testdeleteCustomer
    • testupdateCustomer
    • testlistCustomer
    Ahora si!! todo comienza a tomar sentido, esto quiere decir que yo podre resolver las llamadas utilizando de  la siguiente forma 
    • add.htm
    • delete.htm
    • update.htm
    • list.htm

    Como ultima opción y no menos importante es la definicion del ViewResolver que es utilizado por el controlador para realizar el redireccionamiento adecuado,  en nuestra definicion le decimos que dentreo del directorio /pages vamos a encontrar nuestras vistas y van a ser de extencion jsp.

    Paso 5:  Entendiendo la ejecución

     Para facilitar la prueba definimos un index que haga referencia a URL que posibiliten entender el mapeo que realizamos en el contenedor bean.

    Primero ejecutemos en tomcat.





    Tendremos una index que nos proporciona los link para visualizar los diferentes posibilidades que definimos en nuestro archivo de configuración. 


    Las siguientes imágenes en su URL podran mostrarnos la ejecución de cada uno de los metodos que definimos en la clase controladora.








    El código del proyecto lo puedes descargar desde aquí.

    codigofuente.zip