sábado, 22 de agosto de 2009

Spring MVC Implementando Internacionalización

La internacionalización de tu aplicación consiste en hacerla accesible no solo para las personas localizadas en un país(con un determinado lenguaje, como el español para nosotros), sino tambien para aquellas personas en otros países(como Inglaterra, Francia ó Italia, que leen inglés, francés, italiano) La internacionalización consiste básicamente en que el texto que muestra tu aplicación en un determinado lenguaje pueda ser visto en otro lenguaje si cambiar nada de código, es decir si desde Italia accedo a tu aplicación entonces todo el texto tiene que estar en italiano, si desde Inglaterra accedo a tu aplicación entonces todo el texto tiene que estar en inglés. Pero, ¿como implementamos esto? ¿cuál es la mejor manera?. A veces cuando no se tiene un conocimiento profundo de la tecnología realizamos la aplicación sin tener en cuenta este punto osea implementamos todas los texto solo en nuestro lenguaje, para muestra un ejemplo:

- Si tenemos un pedaz de código en una página jsp como el siguiente:
Usuario: <input type="text" name="usuario"/>
Clave: <input type="text" name="clave"/>

-Entonces el texto(ó etiquetas) Usuario y Clave siempre van a aparecer en español para cualquier tipo de usuario(sea inglés, italiano, alemán...)

La solución está en asociar claves a las etiquetas y centralizar esta asociación en un archivo de propiedades, teniendo un archivo por cada lenguaje que desee soportar nuestra aplicación. El código jsp anterior se puede cambiar por lo siguiente:

<spring:message code="etiq.usuario"/><input type="text" name="usuario"/>
<spring:message code="etiq.clave"/><input type="text" name="clave"/>

Y el archivo de propiedades para tres idiomas sería:
1. Para soporte español(por defecto cuando el sufijo no es especificado)
mensajes.properties
etiq.titulo = Spring Framework
etiq.usuario = Usuario
etiq.clave = Clave

2. Para soporte inglés
mensajes_EN.properties
etiq.titulo = Spring Framework
etiq.usuario = User
etiq.clave = Password

3. Para soporte italiano
mensajes_IT.properties
etiq.titulo = Spring Framework
etiq.usuario = Utente
etiq.clave = Chiave

Cuando la aplicación sea invocada esta va a detectar el lenguaje del navegador y va a utilizar el archivo de propiedades apropiado.

Bueno, ahora veamos como Spring soporta todo este contexto de internacionalización. A diferencia de los post anteriores ahora vamos a agregar a la aplicación un contenedor de beans(un ApplicationContext.xml) adicionando al descriptor de despliegue lo siguiente:

<!--
Una variable de contexto que tendrá como valor el archivo de configuración del contenedor de beans.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<!--
Un listener de contexto que usará el archivo anterior para instanciar todos los beans que contiene
-->
<listener><listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class></listener>

Una vez declarado la ruta del contenedor de beans ahora lo definimos:
ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--
Declaración del bean que obtendrá el archivo y que a su vez lo hará disponible al resto de la aplicación(ya no tenemos que colocar los mensajes en duro) ahora podemos centralizar estos mensajes en un archivo mensajes.properties, este archivo debe de estar en la raiz de tu classpath, osea /WEB-INF/classes
-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>mensajes</value></property>
</bean>
</beans>

Los archivos de propiedades con los textos en cada idioma debemos de colocarlos en la raiz del classpath, es decir en /WEB-INF/classes . Esta carpeta quedaría como:

/WEB-INF/classes/mensajes.properties
/WEB-INF/classes/mensajes_EN.properties
/WEB-INF/classes/mensajes_IT.properties

Una vez configurada la aplicación procedemos a probarla con un archivo jsp:
index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><spring:message code="etiq.titulo"/></title>
</head>
<body>
<form action="/usuario/procesar.do" method="POST">
<spring:message code="etiq.usuario"/><input name="usuario" type="text"/>
<spring:message code="etiq.clave"/><input name="clave" type="text"/>
</form>
</body>
</html>

Para finalizar, este tipo de diseño simplifica el desarrollo y mantenimiento de tu aplicación, ya no tienes que estar buscando en las miles de líneas de código en tu aplicación, todos los mensajes y textos los centralizas en un archivo de mensajes y si quieres cambiar algo, pues solo lo haces allí.


Spring MVC implementando la interface Controller

En los 2 post anteriores hemos visto como podemos implementar un controlador(la C en MVC) mediante anotaciones, para aquellos a quienes no les guste este tipo de implementación pues hay una forma un poco más extensa y un tanto vieja, esta forma es mediante la implementación de la interface Controller, allí las va el código:

1. En el Front Controller(-servlet.xml) de tu aplicación declara el siguiente bean:

<!--
El siguiente bean mapeará las solicitudes /login/procesar.do hacia el controlador pe.com.slcsccy.springmvc.controllers.LoginController, estas solicitudes serán procesadas por el metodo: handleRequest(HttpServletRequest,HttpServletResponse) que deacuerdo al procesamiento ó lógica de negocio retornará hacia la vista apropiada
-->
<bean name="/login/procesar.do" class="pe.com.slcsccy.springmvc.controllers.LoginController">
<property name="vistaOk" value="iniciar"/>
<property name="vistaError" value="error"/>
</bean>

2. Crea una clase que implemente la interface Controller y agregale la lógica de negocia que tu quieras:

package pe.com.slcsccy.springmvc.controllers;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import pe.com.slcsccy.springmvc.dominio.Usuario;

public class LoginController implements Controller {

private String vistaOk;
private String vistaError;

/*
Método definido en la interfaz Controller e implementado aquí para dar soporte a las solicitudes cuyo patrón URL está declarado en el Front Controller(frontController-servlet.xml)
*/
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("VistaOk:"+getVistaOk());
System.out.println("VistaError:"+getVistaError());
String usuarioRequest = request.getParameter("usuario");
if ("ccacique".equals(usuarioRequest)) {
/* Creamos los datos que supuestamente los obtenemos con un Service */
Map datosLogin = new HashMap();
Usuario usuario = new Usuario();
usuario.setNombres("Carlos Alberto");
usuario.setApellidos("Cacique Yupanqui");
usuario.setCodigo("ccacique");
datosLogin.put("usuario", usuario);
/* El constructor de ModelAndView recibe como parametros el nombre de la
vista hacia la cual vamos a responder y el modelo de datos que hemos obtenido.*/
return new ModelAndView(getVistaOk(), datosLogin);
} else {
Map datosError = new HashMap();
datosError.put("mensaje", "Usuario incorrecto");
return new ModelAndView(getVistaError(), datosError);
}
}

public String getVistaOk() {
return vistaOk;
}
public void setVistaOk(String vistaOk) {
this.vistaOk = vistaOk;
}
public String getVistaError() {
return vistaError;
}
public void setVistaError(String vistaError) {
this.vistaError = vistaError;
}
}

3. Define las vistas, para el caso solo 2 páginas jsp; error.jsp e iniciar.jsp

error.jsp:
<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Pagina de Error</title>
</head>
<body>PAGINA DE ERROR:
<c:out value="${mensaje}"/>
</body>
</html>

iniciar.jsp
<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>Aplicación J2EE</title></head>
<body>Bienvenido: <c:out value="${usuario.apellidos}"/>,<c:out value="${usuario.nombres}"/>
</body>
</html>


Comparando la forma de implementación de Controllers ya sea mediante anotaciones(@Controller) o mediante la implementación de la interfaz Controller(como vimos en este post), pues la tendencia es ir hacia lo que es anotaciones, con las anotaciones podemos prescindir de declarar los controllers en el FrontController, lo unico que tenemos que hacer es escanear el contexto del contenedor en busca de estereotipos y es el contenedor quien se encargará de detectar e instanciar los controllers adecuados, recuerda que el escaneo lo realizamos con la siguiente declaración:
<context:component-scan base-package="pe.com.slcsccy.springmvc.controllers"/>

Si aún así pues no te animas por las anotaciones pues en los siguientes post vamos a ver algo más de controllers más detallados y especificos para un problema particular.


viernes, 21 de agosto de 2009

Spring MVC Usando @Controller y Formularios

En el post anterior vimos como podemos usar el estereotipo @Controller para mapear las solicitudes con la anotación @RequestMapping a métodos, esta vez vamos a ver como podemos usar formularios, validarlos y hacer uso de una clase estereotipada(denuevo @Controller) que controle y mapee adecuadamente las solicitudes que se hagan con el formulario, para esto la configuración de la aplicación no ha cambiado(no he modificado web.xml ni frontController-servlet.xml del post anterior y que voy a reutilizar).

Veamos el primer archivo cliente:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La línea anterior la podemos colocar en cualquier archivo jsp y lo que hará es solicitar al controlador(UsuarioController.java) que me retorne la página donde voy a registrar al usuario, la clase estereotipada UsuarioController.java es la siguiente:


package pe.com.slcsccy.springmvc.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
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.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import pe.com.slcsccy.springmvc.dominio.Usuario;
import pe.com.slcsccy.springmvc.validacion.UsuarioValidador;

@Controller
@RequestMapping("/forms/usuario.do")
@SessionAttributes(types = Usuario.class)
public class UsuarioController {

/**
Este método es el que se ejecutará cuando las solicitudes tengan el patrón URL de la forma /forms/usuario.do y sean del tipo GET, es decir desde un link a href, desde un URL, desde javascript o cuando en el formulario html el atributo method sea GET o no esté especificado.
*/
@RequestMapping(method = RequestMethod.GET)
public String inicializarFormulario(Model model) {
/*
Al formulario le enviamos los datos por default de un usuario, este método actua como el inicializador o constructor del formulario
*/
Usuario usuario = new Usuario();
model.addAttribute(usuario);
return "forms/usuario";
}

/*
Este método será ejecutado solo cuando el patrón URL del formulario sea /forms/usuario.do y además el método del formulario que envía los datos solo sea POST, el detalle de los argumentos de los métodos es el siguiente:
@param @ModelAttribute contiene todos los datos del usuario que ha sido enviado desde el formulario
@param BindingResult, BindinResult es una interface, lo que hace Spring es crear un objeto que implementa esta interface,especificamente Spring implementa org.springframework.validation.BeanPropertyBindingResult este objeto se enlazará el objeto usuario enviado para permitir realizar las validaciones necesarias, extiende de Errors.
@param SessionStatus, objeto que determina si el PROCESAMIENTO de la session a sido completado o no, podemos usar este objeto para indicar a la aplicación que el PROCESAMIENTO de la session ha sido completado y que los atributos en él ya no son necesarios.
*/
@RequestMapping(method = RequestMethod.POST)
public String procesarFormulario(@ModelAttribute Usuario usuario,
BindingResult resultado, SessionStatus estado) {
/* Validamos los datos del usuario */
new UsuarioValidador().validar(usuario, resultado);

/* Si no hay errores limpiamos la session y redireccionamos */
if(!resultado.hasErrors()){
/* Limpiamos los objetos de session para este procesamiento*/
estado.setComplete();
/* redireccionamos al método inicializarFormulario */
return "redirect:usuario.do";
}

/* Si hay errores los mostramos en el formulario */
return "forms/usuario";
}

}


La clase anterior usa un validador que es el siguiente:

UsuarioValidador.java
package pe.com.slcsccy.springmvc.validacion;

import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import pe.com.slcsccy.springmvc.dominio.Usuario;

public class UsuarioValidador {

public void validar(Usuario usuario, Errors errors) {

if (!StringUtils.hasLength(usuario.getCodigo())) {
errors.rejectValue("codigo", "required", "required");
}
if (!StringUtils.hasLength(usuario.getNombres())) {
errors.rejectValue("nombres", "required", "required");
}
if (!StringUtils.hasLength(usuario.getApellidos())) {
errors.rejectValue("apellidos", "required", "required");
}
}
}


Espero haber sido lo suficientemente claro en los comentarios java, si tienes dudas no dudes en comentar.

La clase anterior será llamada cuando el usuario hace click en un enlace como:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La clase será llamada porque el url(href) satisface el patrón declarado en la clase:
@RequestMapping("/forms/usuario.do") y el método a ejecutar será inicializarFormulario, porque dicho método está anotado para que le lleguen todas las solicitudes GET @RequestMapping(method = RequestMethod.GET) debes de recordar que una de las formas de realizar solicitudes GET es mediante links(href) en las páginas html.

Una vez ejecutado el método, este instanciarà un objeto usuario y lo devolverá en el modelo de datos a la siguiente vista:

usuario.jsp
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Registro de Usuarios</title>
</head>
<body>
<form:form modelAttribute="usuario">
Codigo: <form:input path="codigo" /><form:errors path="codigo"/><br/>
Nombres: <form:input path="nombres" /><form:errors path="nombres"/><br/>
Apellidos: <form:input path="apellidos" /><form:errors path="apellidos"/><br/><br/><br/>
<input type="submit" value="Registrar Usuario"/>
</form:form>
</body>
</html>


Podemos ver el código html generado y notar que el atributo method(del formulario) es POST, entonces cuando le demos al botón "Registrar Usuario" este procesamiento será enviado hacia el método procesarFormulario de la clase UsuarioController.

Eso es todo por el momento, si tienes alguna duda puedes resolverla comentando...

martes, 18 de agosto de 2009

Spring MVC Usando Controllers con Anotaciones

Veamos como podemos implementar una pequeña aplicación en Spring que use SpringMVC con anotaciones de la manera más fácil posible.

Aquí estan los componentes:

1. Descriptor de despliegue(web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!--
Declaración del servlet que Spring utilizará como Front Controller
para nuestra aplicación.
-->
<servlet>
<servlet-name>frontController</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Mapping del servlet anterior-->
<servlet-mapping>
<servlet-name>frontController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<!--Tiempo en minutos que dura una session -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>

<!--Listado de archivos de bienvenida -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>

2. Front Controller de SpringMVC(frontController-servlet.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">

<!--
La siguiente declaración buscará todos los estereotipos(Clases declaradas
con las anotaciones @Service,@Component,@Controller,@Repository)
en el paquete pe.com.slcsccy.springmvc.controllers una vez identificado el
estereotipo entonces el contenedor instanciará el bean y por lo tanto estará
listo para ser utilizado por la aplicación
-->
<context:component-scan base-package="pe.com.slcsccy.springmvc.controllers"/>
<!--
El siguiente bean configura las propiedades prefijo(prefix) y sufijo(suffix)
de Internalresourceviewresolver, el cual resuelve nombres de vistas lógicas
retornadas por los controladores(en el ejm:clases en el paquete controllers)
Por ejemplo, un nombre de vista lógico podría ser "resultadoPlanillaQuinta"
entonces esta vista será mapeada a "/WEB-INF/jsp/resultadoPlanillaQuinta.jsp"
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
</beans>


3. Un Controller de la aplicación(PlanillaController.java) que mapeará las solicitudes a métodos en esta clase:

package pe.com.slcsccy.springmvc.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class PlanillaController {


/*
Si el método en el controller esta mapeado pero no retorna nada, entonces
Spring lo que hará es buscar una vista con el mismo nombre que el patron
del mapping pero sin la extensión, es decir buscara un archivo jsp con el
siguiente nombre: iniciarApp.jsp, este archivo debe de estar en /WEB-INF/jsp/
*/
@RequestMapping("/iniciarApp.do")
public void iniciarAplicacion() {
System.out.println("Inicio de la aplicación.");
}

/*
El siguiente método acepta todas las solicitudes que vienen del patrón URL(en realidad es una ruta directa) /planilla/procesar.do,este patrón lo declaramos en la anotaciòn @RequestMapping, como salida tenemos un nombre de vista que va estar relacionado a un resolvedor de vistas, quién resolverá el recurso(archivo jsp) a presentar.
*/
@RequestMapping("/planilla/procesarQuinta.do")
public String procesarQuinta(@RequestParam("periodo") String periodo, ModelMap modelo) {
modelo.addAttribute("periodo", periodo);
return "planilla/resultadoPlanillaQuinta";
}

@RequestMapping("/planilla/procesarCuarta.do")
public String procesarCuarta(@RequestParam("periodo") String periodo, ModelMap modelo) {
modelo.addAttribute("periodo", periodo);
return "planilla/resultadoPlanillaCuarta";
}


/*
Este método difiere con los 2 anteriores en el tipo de retorno, ahora el
tipo de retorno no es una cadena sino un ModelMap, este modelMap debe
contener el modelo de datos a ser presentado en la vista, en los métodos
anteriores colocabamos el modelMap como un argumento al método y después
usabamos ese modelMap para colocar atributos(modelo de datos) que iban
a ser pintados en la vista. En este método la vista es resuelta hacia el
mismo URL que lo solicitó, es decir la vista se resuelve hacia:
/WEB-INF/jsp/planilla/cierre.jsp, recordar que el prefijo: WEB-INF/jsp/
y el sufijo son declarados en el frontController:
frontController-servlet.xml, ModelMap si revisas las fuentes de Spring extiende
LinkedHashMap que es una forma especializada de HashMap
*/
@RequestMapping("/planilla/cierre.do")
public ModelMap cerrarPlanilla(@RequestParam("periodo") String periodo) {
/*Aquí consulta compleja a la base de datos para sacar los nombres*/
String[] nombres = {"Carlos","Alberto","Cacique","Yupanqui"};
return new ModelMap("nombres",nombres);
}
}

4. Ahora solo veamos los archivos jsp:
4.1 cierre.jsp (/WEB-INF/jsp/planilla/cierre.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cierre de Planilla</title>
</head>
<body>
<br><br>Planilla Cerrada<br><br>
<table>
<c:forEach var="nombre" items="${nombres}" varStatus="status">
<tr>
<td>${status.index}</td>
<td>${nombres[status.index]}</td>
</tr>
</c:forEach>
</table>
</body>
</html>

4.2 resultadoPlanillaCuarta.jsp (/WEB-INF/jsp/planilla/resultadoPlanillaCuarta.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Resultado planilla Cuarta</title>
</head>
<body>
La planilla fue procesada para el periodo ${periodo}
</body>
</html>

4.2 resultadoPlanillaQuinta.jsp (/WEB-INF/jsp/planilla/resultadoPlanillaQuinta.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Resultado planilla Quinta</title>
</head>
<body>
La planilla fue procesada para el periodo ${periodo}
</body>
</html>

El desarrollo de esta pequeña aplicación fué realizada con Spring 2.5.6, y el propósito es ver como podemos usar anotaciones en la capa de control de una aplicación MVC. Las anotaciones vistas fueron @Controller y @RequestMapping.

Podemos ver como las solicitudes que enviamos a la aplicació se mapean correctamente probando de la siguiente manera, en el URL de tu navegador puedes poner:


y debería ejecutar el método adecuado sgún el mapping y redirigirse a la vista apropiada.


sábado, 15 de agosto de 2009

Spring Básico, inyección de dependencias desde xml

Este post tratará el concepto de inyección de dependencias declarado en un archivo xml, y ¿cómo? desde código java podemos obtener el ensamblado(todos los beans instanciados y relacionados en memoria), para este caso utilizo un modelo de dominio de pedidos de compra, aquí hay que tener cuidado ya que el objetivo del post es ver como Spring inyecta las dependencias en los objetos que va creando, en un entorno de producción Spring no se utiliza para instanciar objetos de negocio o de un modelo de un dominio especifico(de esto se puede encargar Hibernate, Ibatis o el ORM de tu preferencia), Spring en entornos de producción nos servirá para instanciar servicios, daos, acciones de struts, etc...

Veamos la base de código, del modelo de dominio.

Proveedor.java
package pe.com.slcsccy.springbasico.dominio;
public class Proveedor {
private Integer idProveedor;
private String nombreProveedor;
private String nombreContacto;
private String direccion;
private String telefono;
//siguen setters y getters...
}

PedidoCompra.java
package pe.com.slcsccy.springbasico.dominio;
import java.util.ArrayList;
public class PedidoCompra {
private Integer idPedidoCompra;
private String numeroPedidoCompra;
private String descripcionPedidoCompra;
private String fechaPedido;
private String fechaEntrega;

//Aqui agregamos referencias a un listado de items
//de pedido DetallePedido
private ArrayList detalle;

//Aqui mantenemos la información del proveedor
//a quien se realiza la compra
private Proveedor proveedor;

//siguen setters y getters...
}

DetallePedido.java
package pe.com.slcsccy.springbasico.dominio;
import java.math.BigDecimal;
public class DetallePedido {
private Integer idDetallePedido;
private Integer idPedidoCompra;
private Integer idProducto;
private BigDecimal precioUnitario;
private Integer unidadesPedidas;
//siguen setters y getters...
}

Ahora veamos el archivo de configuración de spring que hace que la magia de la instanciación y la inyección de componentes ó beans se lleve a cabo. El siguiente archivo lo colocamos en el directorio raiz de las clases java

pedido1.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- Definimos los namespaces que vamos a usar; recordar que un namespace define un enlace(Entre la instancia XML, este archivo, y el esquema con las reglas de formación que debe tener este archivo, el esquema está representado por una URL), el namespace en este archivo lo podemos identificar por la palabra clave xmlns, si despues de esta palabra clave hay dos puntos y otra palabra, pues esta otra palabra es llamada el prefijo que deberemos usar en el archvo XML(este archivo), para este caso el namespace principal que usamos es
beans xmlns="http://www.springframework.org/schema/beans" y como no está asociado a un prefijo pues su uso en ste archivo es a través de la palabra "beans"(ojo podemos usar cualquier otra palabra, es como si declararamos una variable), resumiendo lo dicho entre instancias Xml y esquemas Xsd pues como una analogía podemos tomar un esquema como una clase y un instancia Xml como una instancia(objeto) de esa clase.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!--Definimos un bean proveedor-->
<!--Bueno ahora veamos que es lo que Spring hace cuando se encuentra con un bean como este, Spring lo que hará es crear una instancia con el nombre proveedor(atributo id) de la clase Proveedor(atributo class) y le asignará valores a cada una de sus propiedades.
Tambien lo podemos ver de la siguiente manera:
Proveedor proveedor = new Proveedor();
proveedor.setIdProveedor(1000);
proveedor.setNombreProveedor("CcySolutions");
proveedor.setNombreContacto("Carlos Cacique Yupanqui");
proveedor.setDireccion("Av. Tupac Amaru 094");
proveedor.setTelefono("989873846");
-->
<bean id="proveedor" class="pe.com.slcsccy.springbasico.dominio.Proveedor">
<property name="idProveedor" value="1000"/>
<property name="nombreProveedor" value="CcySolutions"/>
<property name="nombreContacto" value="Carlos Cacique Yupanqui"/>
<property name="direccion" value="Av. Tupac Amaru 094"/>
<property name="telefono" value="989873846"/>
</bean>

<!-- Definimos beans del tipo detalle de pedido-->
<!--
Una forma más simplificada de asignar los valores a las propiedades es usando el alias p del namespace http://www.springframework.org/schema/p
-->
<bean id="item1" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1478" p:idPedidoCompra="1000" p:idProducto="1024"
p:precioUnitario="3000.14" p:unidadesPedidas="100"/>

<bean id="item2" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1479" p:idPedidoCompra="1000" p:idProducto="1024"
p:precioUnitario="7800.14" p:unidadesPedidas="10"/>

<!--Tambien podemos signar los valores a las propiedades de la forma más usual-->
<bean id="item3" class="pe.com.slcsccy.springbasico.dominio.DetallePedido">
<property name="idDetallePedido" value="1480"/>
<property name="idPedidoCompra" value="1000"/>
<property name="idProducto" value="1026"/>
<property name="precioUnitario" value="800.14"/>
<property name="unidadesPedidas" value="17"/>
</bean>

<bean id="item4" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1481" p:idPedidoCompra="1000" p:idProducto="1025"
p:precioUnitario="40.14" p:unidadesPedidas="50"/>

<!-- Ahora INYECTAMOS los beans anteriores al bean principal -->
<bean id="pedido1" class="pe.com.slcsccy.springbasico.dominio.PedidoCompra">
<!-- La siguiente linea INYECTA el bean proveedor a este bean(pedido1) -->
<property name="proveedor" ref="proveedor"/>
<property name="idPedidoCompra" value="100000"/>
<property name="numeroPedidoCompra" value="CX-TX-1000"/>
<property name="descripcionPedidoCompra" value="Pedido realizado."/>
<property name="fechaPedido" value="2009-08-15"/>
<property name="fechaEntrega" value="2009-08-17"/>

<!-- Las siguiente líneas INYECTARAN los items anteriores-->
<property name="detalle">
<list>
<ref bean="item1"/>
<ref bean="item2"/>
<ref bean="item3"/>
</list>
</property>
</bean>
</beans>

Ahora veamos la clase principal que levanta el archivo pedido1.xml y muestra los valores de cada instancia/objeto:

Inicio.java
package pe.com.slcsccy.springbasico;
import java.util.ArrayList;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pe.com.slcsccy.springbasico.dominio.DetallePedido;
import pe.com.slcsccy.springbasico.dominio.PedidoCompra;
import pe.com.slcsccy.springbasico.dominio.Proveedor;

public class Inicio {

public static void main(String[] arg) {
//Instanciamos un contenedor de Spring:
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"pedido1.xml"});

PedidoCompra pedido1 = (PedidoCompra) context.getBean("pedido1");
System.out.println("Datos del Pedido:");
System.out.println("*****Descripción:" + pedido1.getDescripcionPedidoCompra());
System.out.println("*****Fecha Entrega:" + pedido1.getFechaEntrega());

Proveedor proveedor = pedido1.getProveedor();
System.out.println("Datos del Proveedor:");
System.out.println("*****Contacto:" + proveedor.getNombreContacto());
System.out.println("*****Nombre:" + proveedor.getNombreProveedor());

System.out.println("Detalle del pedido:");
ArrayList detalle = pedido1.getDetalle();
for (DetallePedido item : detalle) {
System.out.println("****Precio:"+item.getPrecioUnitario());
}
}
}

miércoles, 12 de agosto de 2009

Introducción a Jboss Seam

Seam es un "application framework", mismo spring, para aplicaciones empresariales Java que está inspirado en los siguientes principios:

Un tipo de "componente"
Seam define un modelo de componentes uniforme para toda la lógica de negocio de tu aplicación. Un componente Seam puede ser stateful(con estado), y este estado asociado con cualquiera de uno de los varios predefinidos contextos, incluyendo long-running, persistencia, procesos de negocio y el contexto conversacional, el cual es preservado a través de multiples solicitudes(request) en una interacción de usuario.

En seam no hay distinción entre los componentes de la capa de presentación y los componentes de la lógica de negocio. A diferencia de los componentes Java EE, los componentes Seam pueden simultaneamente acceder al estado asociado con la solicitud web(request) y el estado de los recursos transaccionales(sin la necesidad de propagar el estado de la solicitud web manualmente mediante parámetros de métodos).

Tú pódrías objetar que hacer una aplicación en capas conforme a la plataforma J2EE es un "buen mecanismo", bueno nada te detiene de crear una arquitectura equivalente en capas usando Seam, la diferencia es que tienes que llegar a la arquitectura de tu aplicación y decidir cuales son las capas y como trabajan juntas.

JSF integrado con EJB3
Jsf y Ejb son dos de las mejores nuevas características de Java EE5. Ejb3 es una "marca"(mismo pepsi ó coca-cola) en el modelo de componentes para lógica de persistencia y de negocio del lado del servidor; mientras tanto, Jsf es un gran modelo de componentes para la capa de presentación. Desafortunadamente, ningún modelo de componentes es capaz de resolver todos los problemas en una aplicación por sí sola. En cambio, Jsf y Ejb3 trabajan mejor usadas juntas, pero la especificación Java EE5 no proporciona una manera para integrar estos dos modelos de componentes. Afortunadamente, los creadores de ambos modelos visionaron esta situación y proporcionaron puntos de extensión standarizados para permitir justamente la extensión e integración con otros frameworks.

Seam unifica el modelo de componentes de Jsf y Ejb3, eliminando el código de acoplamiento, y dejando al desarrollador que piense en del problema del negocio.

Es posible escribir aplicaciones Seam donde "todo" es un Ejb, Esto puede ser una sorpresa si estás acostumbrado a pensar en un Ejb como un objeto pesado, como vemos, la versión Ejb3 a cambiado completamente la naturaleza de Ejb desde el punto de vista del desarrollador. Un Ejb es un objeto ligero, es decir, nada más complejo que un JavaBean con anotaciones. Seam incluso alienta el uso de beans de sesion como escuchadores de acciones("action listeners") Jsf.

Sobre el otro lado, si tu prefieres no adoptar Ejb3 en este momento, pues simplemente no lo hagas. Virtualmente cualquier clase Java puede ser un componente Seam, y Seam proporciona toda la funcionalidad que tu esperas de un contenedor "ligero", y más, para cualquier componente, ó Ejb en otros casos.

Ajax Integrado.
Seam soporta las mejores soluciones Ajax de fuente abierta basada en Jsf: JBoss Richfaces y ICEFaces. Estas soluciones te permiten agregar capacidad Ajax a tu interface de usuario sin la necesidad de escribir ninguna linea de código javascript.

Alternativamente, Seam proporciona un capa remota de JavaScript que permite llamar a componentes asincrónicamente desde JavaScript del lado del cliente sin la necesidad de una capa intermedia, puedes incluso suscribirte a "topics JMS" del lado del servidor y recibir mensajes via "Ajax push".

Ninguno de estos enfoques funcionaría bien sino fuera por las capacidades de Seam en lo que se refiere a concurrencia y a gestion del estado, lo cual asegura que muchas solicitudes concurrentes asincrónicas Ajax son controladas en forma segura y eficientemente sobre el lado del servidor.

Proceso de negocio como una construcción de primera clase.
Opcionalmente, Seam proprciona gestión de procesos de negocio transparente vía jBPM, no podrás creer cuán fácil es implementar flujos de trabajo complejos, colaboración y gestión de tareas usando jBPM y Seam. Seam incluso permite definir flujos de página en la capa de presentación usando el mismo lenguaje(jPDL) que jPBM usa para definición de procesos de negocio.

Jsf, proporciona un enriquecido modelo de eventos para la capa de presentación, Seam implementa este modelo exponiendo eventos relacionados a procesos de negocio jBPM's vía exactamente el mismo mecanismo de manipulación de eventos, proporcionando un modelo de eventos uniforme para el modelo de componentes uniforme de Seam.

Gestión de estados declarativo.
Todos estamos acostumbrados al concepto de gestión de transacciones declarativas y seguridad declarativa desde los primeros días de Ejb, Ejb3 incluso introduce gestión del contexto de persistencia en forma declarativa. Los siguientes son tres ejemplos de un amplio problema de gestión de estados que es asociado con un contexto particular, mientras nos aseguramos que todas las necesidades de limpieza ocurren cuando un contexto finaliza, Seam toma el concepto de gestión de estados en forma declarativa en forma más profunda y aplica esto al estado de la aplicación.

Tradicionalmente, las aplicaciones J2EE implementan gestión del estado manualmente, obteniendo y colocando atributos de sesión y de solicitud(request), este enfoque de gestión del estado es la fuente de muchos bugs y fugas de memoria, cuando la aplicación falla en la limpieza de atributos de sesión, ó cuando la data asociada con diferentes flujos de trabajo colisionan en una aplicación de multiples ventanas(cuando el usuarioabre varias ventanas del navegador para usar su sesión).

Seam tiene el potencial para enteramente eliminar esta clase de bugs.

La gestión de estados de la aplicación en forma declarativa es hecha posible por la riqueza del modelo contextual definido por Seam. Seam extiende el modelo contextual definido por la especificación servlet(request,session,application) con dos nuevos contextos: conversacional(conversation) y procesos de negocio(business process), que son más significativos
desde el punto de vista de la lógica de negocio. Tú estarás maravillado de cuántas cosas pueden ser más fáciles una vez que nos iniciamos usando conversaciones(conversations).

¿Alguna vez has sufrido tratando con "lazy association fetching"(sacar consultas de base de datos y ejecutarlas conforme lleguen) en una solución ORM como Hibernate o JPA?.
Usando los contextos de persistencia de Seam en un alcance conversacional raramente
veremos una excepción del tipo LazyInitializationException.

¿Has tenido problemas con el botón F5(el botón de actualización)?,
¿Con el botón de flechita atrás(cuando el usuario quiere ver la página anterior)?,
¿Con envios de formularios duplicados?
¿Con propagación de mensajes a través de un post y luego una redirección?

La gestión conversacional de Seam resuelve estos problemas sin la necesidad de pensar en ellos.


Biyección
La noción del control invertido o inyección de dependencias existe tanto en Jsf como Ejb3, asi como tambien en numerosos asimismos llamados "contenedores ligeros".

Muchos de estos contenedores enfatizan la inyección de componentes que implementan servicios sin estado. Incluso cuando la inyección de componentes con estado es soportada(como con Jsf), es virtualmente menos usado para controlar el estado de la aplicación porque el alcance del componente con estado no puede ser definido con sufiente flexibilidad, y porque componente pertenecientes a amplios alcanes no pueden ser inyectados dentro de componentes pertenecientes a alcances más limitados.

Biyección difiere del control invertido en que es dinámico y bidireccional, podriamos pensar en esto como un mecanismo de alias para variables contextuales(nombres en alguno de los contextos enlazados al hilo de ejecución actual) a atributos de el componente. La biyección permite auto ensamblaje de componentes con estado por el contenedor, esto incluso permite a un componente asegurar y facilmente manipular el valor de una variable contextual, simplemente asignandola a un atributo del componente.

Preferir anotaciones a XML
Tradicionalmente, la comunidad java a estado en una confusión profunda acerca precisamente de que tipo de metainformación debe estar como configuración. J2EE y populares contenedores ligeros han proporcionado descriptores de despliegue basados en XML tanto para cosas que son verdaderamente configurables entre diferentes despliegues del sistema, y para cualquier otro tipo de declaración la cual no puede ser fácilmente expresada en java. Las anotaciones de Java 5 cambian todo esto.

Ejb3 adopta las anotaciones y la "configuración por excepcion" como la más fácil manera para proporcionar información a el contenedor en una forma declarativa. Seam extiende la anotaciones proporcionadas por Ejb3 con un conjunto de anotaciones para gestión del estado en forma declarativa y demarcación del contexto tambien en forma declarativa. Esto permite eliminar las declaraciones de beans gestionados en Jsf(managed beans) y reduce el XML requerido a justamente la información que verdaderamente pertenece a XML(las reglas de navegación Jsf).


viernes, 7 de agosto de 2009

SCDJWS: WSIT Interoperabilidad de Servicios Web 1

¿Qué es WSIT?
Sun y Microsoft están trabajando para asegurar la interoperabilidad de las tecnologías empresariales de servicios web como son optimización de mensajes(message optimization), mensajería confiable(reliable messaging) y seguridad(security). La versión inicial de WSIT es un producto de esfuerzo conjunto entre estas dos compañias. WSIT es una implementación de un número de especificaciones de servicios web abiertos que soportan características empresariales. Además de la optimización de mensajes, mensajería confiable y seguridad, WSIT incluye bootstrapping y tecnología de configuración.
La siguiente figura muestra los servicios bajo los cuales son implementados cada una de las siguientes tecnologías:



Iniciando con el soporte de core Xml y que actualmente está construido dentro de la plataforma Java, WSIT usa o extiende características existentes y agrega más soporte para servicios web interoperables, Vamos a ver las siguientes secciones para un resumen de cada característica:

Boostraping and Configuration
Consiste en el uso de un URL para accesar a un servicio web, recibir su archivo WSDL, y usar este archivo para crear un cliente de un servicio web que pueda accesar y consumir un servicio web, el proceso consiste de los siguientes pasos:

1. El cliente adquiere el URL para un servicio web que quiera acceder y consumir. Esto lo puedes hacer buscando en un registro de servicios web como UDDI, ó si ya tienes el URL pues este paso ya está listo.

2. El cliente usa el URL y la herramienta wsimport(que se encuentra en el directorio bin de tu jdk6) para enviar una solicitud de intercambio de metadata para acceder al servicio web y recibir el archivo WSDL. El archivo WSDL contiene una descripción del punto final del servicio web, incluyendo las afirmaciones(assertions) WS-Policy(politicas del servicio web: que es lo que está permitido y caracteriza a tu servicio web) que describen las capacidades de seguridad y/o confiabilidad y requerimientos del servicio. Tambien describe los requerimientos que deben ser satisfechos para acceder y consumir el servicio web.

3. El cliente usa el archivo WSDL para crear el cliente del servicio web.

4. El cliente del servicio web accede y consume el servicio web.

Tecnología de Optimización de Mensajes
Una función primaria de las aplicaciones con servicios web es compartir data entre aplicaciones sobre la internet. La data compartida puede variar en formato y puede incluir extensas cargas binarias, como documentos, imágenes, archivos de música, y cosas así. Cuando objetos binarios extensos son encodificados en un formato XML para su inclusión en mensajes SOAP entonces estos mensajes SOAP tambien son extensos y cuesta transmitirlos por internet. Cuando un servicio web procesa y transmite estos extensos archivos sobre la red, el desempeño de la aplicación y la red son afectados negativamente. En el peor escenario los efectos son los siguientes:

El performance de la aplicación del servicio web se degrada al punto que no es más usable.
La red se atasca con más tráfico que el ancho de banda asignado pueda soportar.

Una manera para tratar este problema es encodificar los objetos binarios tanto como para optimizar el proceso de la mensajería SOAP como el ancho de banda requerido para transmitir el mensaje sobre la red. En resumen, XML necesita ser optimizado para los servicios web. Esto es exactamente lo que la tecnología de optimización de mensajes hace. Asegura que los mensajes de los servicios web son transmitidos sobre internet en la manera más eficiente.

Sun recomienda que usemos optimización de mensajes si en nuestro cliente del servicio web ó nuestro punto final del servicio web será requerido procesar documentos XML encodificados en forma binaria y que sean más grandes que 1Kb.

Tecnología de Mensajería Confiable
Mensajería confiable es tecnología sobre calidad de servicio para construir más servicios web confiables. La confiablidad en un sistema es medido por la capacidad de entregar mensajes desde un punto A a un punto B sin errores. El proposito principal de la mensajería confiable es asegurar la entrega de mensajes desde nuestra aplicación al punto final de un servicio web.

La tecnología de mensajería confiable asegura que los mensajes en una secuencia dada de mensajes son entregados al menos una vez y no más que una vez y opcionalmente en el orden correcto. Cuando los mensajes en una secuencia dada son perdidos en la transmisión ó entregados desordenadamente, esta tecnología permite a los sistemas recuperarse de estos fallos. Si un mensaje es perdido en la transmisión, el sistema que lo envió retransmite el mensaje hasta que su recepción es reconocida por el sistema receptor de mensajes. Si los mensajes son transmitidos en desorden, el sistema receptor puede reordenar los mensajes en el orden correcto.

La tecnología de mensajería confiable puede tambien ser usada para implementar gestión de sesiones. Una secuencia de mensajes únicos es creada para cada proxy del lado del cliente y el tiempo de vida del identificador de la secuencia coincide con el tiempo de vida del proxy. Por consiguiente cada secuencia de mensajes puede ser visto como una sesión y puede ser implementado para gestión de sesiones.

Deberiamos considerar el uso de la mensajería confiable si el servicio web está experimentado los siguientes problemas:

Existen fallos en la comunicación como resultado de una red indisponible o conexiones rotas.
Los mensajes de las aplicaciones son perdidos al transmitirse.
Los mensajes de las aplicaciones están llegando a su destino en desorden, y la entrega ordenada es un requisito.

Algunas ventajas y desventajas de usar mensajeria confiable:

Habilitar la mensajería confiable asegura que los mensajes son entregados exactamente una vez desde la fuente al destino, y si la opción de entrega ordenada está habilitada, entonces asegura que los mensajes son entregados en orden.
Habilitar la mensajería confiable causa una degradación del desempeño del servicio web, especialmente si la opción de entrega ordenada está habilitada.
Los clientes que no habiliten la mensajería confiable no pueden interoperar con servicios web que tienen habilitado la mensajería confiable.


Tecnología de Seguridad
Hasta ahora los servicios web tienen confiada su seguridad basada en transporte sobre SSL para proporcinar una seguridad punto a punto. WSIT implementa WS-Security para proporcionar interoperabilidad de mensajes con contenido integro y confidencial. Incluso cuando los mensajes pasan a través de nodos antes de llegar al destino. WSIT tambien realiza la seguridad implementando WS-Secure Conversation(Conversación Segura de Servicios Web), lo cual habilita a un consumidor y productor establecer un contexto de seguridad compartido cuando una secuencia de intercambio múltiple es iniciado, así los siguientes mensajes usan claves deriavados de una sesión incrementando la seguridad total mientras reduce el procesamiento de seguridad para cada mensaje.

WSIT implementa dos caracteristicas adicionales para mejorar la seguridad en los servicios web:

Web Services Security Policy(Politicas de Seguridad en los Servicios Web): Habilita a los servicios web a usar afirmaciones de seguridad para claramente representar preferencias y requerimientos de seguridad para servicios web.

Web Services Trust(Confianza en los Servicios Web): Habilita a las aplicaciones de servicios web usar mensajes SOAP para solicitar tokens de seguridad que pueden ser usados para establecer comunicaciones de confianza entre un cliente y un servicio web.

Sacado de WSITTutorial