Structs 2
Structs 2
Structs 2
Jrme LAFOSSE
Ingeniero en informtica y graduado en la CNAM, Jrme Lafosse trabaja como asesor,
diseador y formador en las tecnologas Java. Especialista en tecnologas Web, trabaja para
fomentar las herramientas y soluciones de cdigo abierto (Open Source) para el desarrollo de
proyectos de Internet. Tambin ensea la plataforma Java Enterprise Edition y el diseo de
proyectos Web en Licenciaturas y Masters.
Qu es un framework?
En programacin existen dos tipos de individuos:
Los programadores de sistemas.
Los programadores de aplicaciones.
Los programadores de sistemas escriben el cdigo que utilizarn los programadores de aplicaciones.
Los programadores de sistemas desarrollan los lenguajes Java, PHP, C o C++ y los programadores de
aplicaciones utilizan estos lenguajes y herramientas para crear valor aadido con fines comerciales.
Los programadores de aplicaciones se concentran en sus proyectos sin preocuparse de las tcnicas y
las mecnicas de bajo nivel. Los programadores de aplicaciones utilizan una serie de bibliotecas o
herramientas que reciben el nombre de framework.
Un framework es un conjunto de bibliotecas, herramientas y normas a seguir que ayudan a
desarrollar aplicaciones. Los frameworks los desarrollan los programadores de sistemas. Un
framework est compuesto por varios segmentos/componentes que interactan los unos con los
otros. Las aplicaciones pueden escribirse de manera ms eficaz si utilizamos un framework adaptado
al proyecto en lugar de tener que volver a inventar la rueda cada vez. Un framework Java proporciona
un conjunto de caractersticas a partir de una implementacin de objeto. En proyectos de desarrollo a
gran escala y de diseo en equipo, los frameworks son muy tiles, incluso imprescindibles.
En la actualidad, existen diferentes tipos de framework:
los frameworks de infraestructura de sistema, que permiten desarrollar sistemas de
explotacin, herramientas grficas y plataformas Web (Struts, Spring...);
los frameworks comunicativos (llamados software);
los frameworks de empresa (desarrollos especficos);
los frameworks de gestin de contenido (tipo Content Management System).
Los frameworks permiten la reutilizacin de cdigo, la estandardizacin del desarrollo y la utilizacin
del ciclo de desarrollo de tipo interactivo-incremental (especificacin, codificacin, mantenimiento y
evolucin). En ocasiones hablamos de paquetes de programas evolucionados cuando diseamos un
framework y su ciclo de vida. En la actualidad, existen muchos frameworks en todos los dominios de
aplicacin y en prcticamente cualquier idioma. Esta es una lista no exhaustiva de los frameworks
utilizados en Java:
Apache Struts
WebWork
JSF (Java Server Faces)
Spring
Wicket
1. Normas y estndares
El desarrollo de las TI con la utilizacin de normas permite generalizar las buenas prcticas y
armonizar los desarrollos dentro de la empresa, facilitando el mantenimie nto. La plataforma de
desarrollo Java EE permite utilizar normas, adems de complejas herramientas que vienen tambin
acompaadas de otras normas.
Qu framework elegimos?
Poco a poco, las API y herramientas de todo tipo han ido sumergiendo el desarrollo de Internet
basado en la tecnologa Java. La eleccin de un framework debe basarse en los siguientes criterios:
Debemos disear todo de principio a fin?
El desarrollo permite el uso de una aplicacin previamente desarrollada o de una parte?
Podemos usar un entorno como base para la aplicacin?
El diseo de principio a fin permite dominar perfectamente una tecnologa, pero requiere de mucho
tiempo y dinero. El desarrollo a partir de aplicaciones existentes es interesante nicamente si los
desarrolladores de los proyectos anteriores estn presentes. El tercer enfoque (usar un entorno
como base para la aplicacin) es sin duda la mejor opcin en la mayora de los casos.
Struts 1
El proyecto Open Source Jakarta-Struts desarrollado por el consorcio Apache permite agilizar el
desarrollo de aplicaciones de Internet. Struts 1 casi se ha convertido en el estndar de facto para los
proyectos Java EE. Struts 1 es un entorno agradable y potente que gestiona la aplicacin as como las
tareas ms frecuentes (enrutamiento, acciones, validaciones...). Otra ventaja de su uso es el nmero
creciente de usuarios que tienden a perpetuar el proyecto. En la actualidad, muchos entornos de
desarrollo como Eclipse ofrecen herramientas para la programacin de Struts 1.
Struts 1 es un framework que ofrece herramientas de validacin de las entradas de los usuarios
(introducidas mediante un formulario), bibliotecas de etiquetas JSP para la creacin rpida de pginas,
una tcnica de enrutamiento para las pginas y acceso Web, as como un proceso de creacin de
formularios basados en archivos XML. Struts 1 tambin ofrece otras ventajas:
Struts 1 funciona con todos los servidores Java EE (Tomcat, WebSphere, Weblogic...).
Struts 1 ofrece una arquitectura slida y estable (proyecto Apache).
Struts 1 se adapta a las aplicaciones Web de gran tamao.
Struts 1 permite descomponer una aplicacin compleja en componentes ms simples.
Struts 1 garantiza un desarrollo similar para los equipos de programadores.
Struts 1 posee una documentacin abundante.
Struts 1 permite un desarrollo rpido y poco costoso.
El trmino Struts hace referencia a pilares o columnas, en el sentido arquitectnico del trmino, con el
concepto de ladrillos que sostienen los edificios, las casas y los puentes para evitar que se conviertan
en ruinas.
Struts 2
Como acabamos de mencionar en la introduccin, en la actualidad se utiliza el modelo de diseo de
tipo MVC para el desarrollo de aplicaciones Web evolucionadas. Sin embargo, los principales
inconvenientes de este tipo de diseo son:
la dificultad de comprender el modelo y el nivel de conocimientos que requiere;
el nmero de archivos que es necesario producir (cerca de tres veces ms);
el aspecto reiterativo de las tareas que se realizan.
La pregunta entonces es: cmo reducir estos inconvenientes y aumentar la productividad?
Poco antes del ao 2000, Craig R. McClanahans decidi crear una herramienta de diseo de tipo
framework para agilizar los desarrollos Java EE. Despus, en mayo de 2000, don a la fundacin
Apache su framework llamado Struts 1.0 cuyo primer lanzamiento se program para junio de 2001,
que despus se convertira en el framework de diseo Java EE ms popular del mundo.
Casi al mismo tiempo, varios desarrolladores trabajaban en la creacin de otro framework de
desarrollo llamado WebWork. Este framework nunca llegara a tener la popularidad de Struts, a pesar
de ser superior en varios puntos, incluyendo la aplicacin de validaciones de formularios y la
arquitectura global para la gestin de JavaBeans (sin necesidad de utilizar Beans de formularios). Un
punto importante de WebWork en relacin con Struts 1.X es el concepto de las pruebas. Con Struts
1.X se requiere un navegador Web para realizar una parte de las pruebas, mientras que WebWork
puede funcionar sin utilizar un navegador.
A finales de 2005, el producto WebWork y el framework ms popular Struts 1.0 se fusionaron para
fundar Struts TI (Titanium) que poco despus pas a ser Struts 2.0. Los diseadores de Struts 1.0 y
Jason Carreiras, responsable de WebWork, proponen entonces un framework que reagrupa las
ventajas de las dos herramientas anteriores (WebWork y Struts 1.0). Sin embargo, Struts 2 no es una
extensin de Struts 1 y, desafortunadamente, eso puede decepcionar a muchos desarrolladores y
arquitectos Web, ya que este nuevo framework tiene un diseo completamente nuevo. Por lo tanto,
los especialistas debern volver a aprender completamente los comandos y las funcionalidades de
Struts 2.0. El nuevo diseo correspondera en mayor medida a WebWork 2.2 (WebWork basado en
XWork de Open Symphony) que a una evolucin de Struts 1.0.
El framework Struts 2 se basa en una declaracin de la arquitectura en forma de archivos XML o con
anotaciones Java localizadas en los archivos de clases de acciones. Struts 2 es un framework
orientado a acciones. Las acciones se descomponen en tres funciones. Primero, las acciones tienen la
funcin ms importante del framework, encapsular el procesamiento y el trabajo que deber realizar
el servicio. Segundo, las acciones permiten manipular automticamente los datos de las consultas
durante las transferencias. Tercero, el framework determina qu resultado debe ser devuelto y la
vista presentada en respuesta a un procesamiento. Las acciones de Struts 2 implementan objetos
JavaBeans (clases Java simples) para cada grupo de datos enviado en la consulta. Cada parmetro
de la consulta se declara en la clase de accin con un nombre idntico para realizar automticamente
la asignacin de valores. La finalidad de una accin es devolver una cadena de caracteres,
permitiendo seleccionar el resultado que se va a mostrar.
En resumen, Struts 2 se basa en el modelo de diseo de tipo MVC II tal y como se explica en el
siguiente esquema. Permite un desarrollo ms rpido, ms flexible y resuelve varios problemas de
diseo ofreciendo los siguientes servicios:
un sistema evolucionado de gestin del enrutamiento o la navegacin;
un sistema de validacin de formularios y de entradas, fcil de aplicar;
un potente sistema de complementos o de extensiones (para los grficos, fuentes de
datos...);
gestin de la internacionalizacin para el desarrollo de sitios multilinges;
compatibilidad con la tecnologa Ajax;
Arquitectura MVC II
Struts 2 ofrece (al igual que Struts 1) la posibilidad de utilizar un mnimo de reglas del diseo que no
son obligatorias:
No utilizar cdigo 100% Java en las pginas JSP. Toda la lgica de control reside en las clases
de acciones (Servlets).
Utilizar bibliotecas de etiquetas para acceder a los objetos y navegar por las colecciones.
Escribir un mnimo cdigo repetitivo y utilizar las herramientas que ofrece el framework.
Este framework tambin tiene como objetivo ayudar a los desarrolladores de aplicaciones Web en
Java a crear proyectos de calidad siguiendo una norma o estndar. Este framework tambin ayuda a
los desarrolladores a organizar la lgica de la aplicacin.
La eleccin del framework Struts 2 se basa en los siguientes puntos:
Fiabilidad: el proyecto se desarroll en mayo de 2000 y desde entonces ha tenido un
seguimiento. Este proyecto tiene una reputacin excelente y mejora constantemente sus
defectos.
Flexibilidad: cada accin puede ser personalizada, los archivos de configuracin son muy
flexibles en trminos de utilizacin y las validaciones son fciles de aplicar.
Rendimiento: la arquitectura diseada por Struts 2 est basada en WebWork. Tiene un
rendimiento especialmente alto y su mantenimiento es sencillo gracias a la separacin por
capas.
Las principales caractersticas del framework Struts 2 son las siguientes:
los tipos de conversiones automticas para las colecciones procedentes de consultas HTTP;
los archivos de configuracin modulables que se usan por paquetes;
las anotaciones Java 5 que reducen las lneas de cdigo para la configuracin;
el uso de etiquetas permite aplicar temas o plantillas (templates);
el uso del lenguaje de expresin OGNL;
el uso opcional del complemento interceptor, que permite ejecutar consultas complejas y
largas como tareas en segundo plano con el envo mltiple (actualizacin de pginas);
la integracin simple de herramientas como JSTL, Spring o Hibernate.
El framework Struts 2 es una segunda generacin de framework MVC. La principal ventaja del
concepto de los interceptores es la flexibilidad del conjunto y la configuracin propuesta. Struts 2
responde tambin al principio de empaquetamiento de la acciones. Cuando declaramos clases de
accin con un archivo XML o anotaciones Java, el framework organiza todos estos componentes en
forma de un software de paquete (packages). Los paquetes de Struts 2 son similares a los de Java.
Este mecanismo permite igualmente agrupar las acciones por dominio. Las URL de la aplicacin se
asociarn a los paquetes donde estn declaradas las acciones.
Proyecto Eclipse
Editar el proyecto y cambiar el nombre de los paquetes sin el trmino java.
El rbol del proyecto debe ser el siguiente:
rbol de instalacionstruts2
Iniciar Tomcat y abrir un navegador.
Introducir la siguiente URL en el navegador: http://localhost:8080/instalacionstruts2/
Tambin podemos probar la aplicacin de autenticacin que viene por defecto:
http://localhost:8080/instalacionstruts2/example/Login.action
Formulario de instalacionstruts2
En resumen
En este captulo se ha explicado el concepto de framework y las ventajas de la utilizacin de dicha
herramienta para los desarrollos Web.
En la segunda parte se han presentado los distintos framework y se ha aportado la informacin
necesaria para reflexionar acerca de la eleccin de un framework. En el siguiente punto se recuerdan
los servicios que ofrece el framework Struts 1 al mismo tiempo que se presenta la nueva herramienta
Struts 2, se explica su instalacin y la puesta en marcha del primer ejemplo en blanco.
Presentacin
Como ya se ha explicado, es recomendable utilizar el modelo de diseo Modelo Vista Controlador
(MVC) para el desarrollo de aplicaciones Web en Java. Antes de comenzar el diseo, es importante
comprender el funcionamiento de este modelo de desarrollo. La arquitectura MVC que ofrece Sun es la
solucin de desarrollo Web del lado del servidor que permite separar la parte lgica de la
presentacin en una aplicacin de Internet. Este es un punto esencial del desarrollo de proyectos ya
que permite a todo el equipo trabajar por separado (cada usuario gestiona sus propios archivos, sus
programas de desarrollo y sus componentes).
Esta arquitectura tiene su origen en el lenguaje SmallTalk a principios de la dcada de 1980, por lo
tanto no es un nuevo modelo (design pattern) nicamente vinculado a Java EE. El objetivo principal es
el de dividir la aplicacin en tres partes distintas: el modelo, la vista y el controlador.
En la arquitectura MVC nos encontramos con:
El modelo representado por los EJB y/o JavaBeans y/o sistemas de persistencia (Hibernate, objetos
serializados en XML, almacenamiento de datos por medio de JDBC...).
La vista representada por los JSP o clases SWING.
El controlador representado por los Servlets o clases Java.
Arquitectura MVC I
Principio de funcionamiento de la arquitectura MVC
El cliente enva una consulta HTTP al servidor. En general, esta consulta es un Servlet (o un
programa ejecutable del lado del servidor) que procesa la solicitud.
El Servlet recupera la informacin transmitida por el cliente y delega el procesamiento a un
componente adaptado.
Los componentes del modelo manipulan o no los datos del sistema de informacin (lectura,
escritura, actualizacin, eliminacin).
Una vez finalizados los procesamientos, los componentes le devuelven el resultado al Servlet.
El Servlet entonces almacena el resultado en el contexto adaptado (sesin, consulta,
respuesta...).
El Servlet llama a la pgina JSP adecuada que puede acceder al resultado.
El JSP se ejecuta, utiliza los datos transmitidos por el Servlet y genera la respuesta al cliente.
En proyectos simples, las consultas HTTP las administran los componentes Web que reciben las
consultas, crean las respuestas y las devuelven a los clientes. En este caso tenemos un nico
componente responsable de la lgica de visualizacin, de la lgica empresarial y de la lgica de
persistencia. En la arquitectura anterior, la visualizacin y la manipulacin de los datos se mezclan en
un nico componente de Servlet. Esto puede ser en gran medida adecuado para un servicio especfico
no evolutivo y simple, pero puede convertirse en un problema cuando el sistema se desarrolla. En
esta arquitectura se introduce cdigo Java y cdigo HTML en los Servlets o JSP. Existen varias
soluciones a este problema. La ms sencilla tiene que ver con la aparicin de las pginas JSP y
consiste en crear archivos de encabezado, de pie de pgina, de procesamiento... e incluir todo en una
pgina general.
Con el modelo MVC, cada consulta HTTP debe ser enviada al controlador. La sintaxis del URI (Uniform
Resource Identifier) indica al controlador qu comando debe ser ejecutado. Con Struts 2, una clase de
accin puede ejecutar varias operaciones sobre el mismo principio que las MappingDispatchAction de
Struts 1.
return contrasena;
}
public void setContrasena(String contrasena) {
this.contrasena= contrasena;
}
}
El Servlet ServletControlador, hereda de la clase javax.servlet.http.HttpServlety permite procesar
consultas HTTP de tipo Post y Get. El cdigo de este Servlet es muy sencillo, analiza el URI, ejecuta la
accin
y
la
redireccin
adaptada
en
consecuencia.
Por
ejemplo,
si
el
URI
esConfirmarAgregar_cliente.action, se crea un objeto cliente a partir de los datos introducidos en el
formulario y se redirige al internauta a la pgina JSP MostrarCliente.jsp.
Cdigo: ejemplo01.ServletControlador.java
package ejemplo01;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletControlador extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)throws IOException, ServletException
{
// el uri tiene el siguiente formato
/ejemplo01/Agregar_cliente.action
String uri=request.getRequestURI();
// se utiliza el cdigo para dividir este URI
int lastIndex=uri.lastIndexOf("/");
String action=uri.substring(lastIndex+1);
System.out.println("ACCIN: "+action);
// para las redirecciones
String urlRetorno=null;
// ejecutar la accin adaptada
if (action.equals("Agregar_cliente.action"))
{
urlRetorno="/jsp/AgregarModificarCliente.jsp";
}
else if (action.equals("ConfirmarAgregar_cliente.action"))
{
// instanciar el objeto
Cliente cliente=new Cliente();
// actualizacin del objeto
cliente.setIdentificador(request.getParameter("identificador"));
cliente.setContrasena(request.getParameter("contrasena"));
// devolver el objeto en la vista
request.setAttribute("cliente", cliente);
//pgina de visualizacin del cliente
urlRetorno="/jsp/MostrarCliente.jsp";
}
// redireccin a la vista adaptada
if (urlRetorno!=null)
{
request.getRequestDispatcher(urlRetorno).forward(request,
response);
}
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws IOException, ServletException
{
doPost(request, response);
}
}
Por ltimo, las dos vistas son muy sencillas. Permiten mostrar el formulario de registro de un nuevo
cliente (AgregarCliente.jsp) as como los datos de este mismo cliente (MostrarCliente.jsp).
Cdigo: /jsp/AgregarCliente.jsp
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<form method="post" action="ConfirmarAgregar_cliente.action">
<table>
<tr>
<td>Identificador:</td>
<td><input type="text" name="identificador"/></td>
</tr>
<tr>
<td>Contraseña:</td>
<td><input type="text" name="contrasena"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
value="Agregar el cliente"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<html>
<head>
<title>Mostrar el cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<p>
<h4>Información sobre el cliente:</h4>
Identificador: ${cliente.identificador}<br/>
Contraseña: ${cliente.contrasena}<br/>
</p>
</div>
</body>
</html>
El archivo de configuracin de la aplicacin, tambin llamado descriptor de implementacin web.xml, es
muy simple. Posee una sola definicin de Servlet que permite administrar todos los URI que terminan
por
.action.
As,
podremos
administrar
sin
problemas
nuestras
con
la
siguiente
URL:
<servlet-mapping>
<servlet-name>ServletControlador</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
El funcionamiento
de
un
filtro
es
diferente, cada
recurso
es
procesado
en
la
funcinfilterChain.doFilter() que sean las consultas o los elementos estticos. La principal ventaja es la
capacidad tanto de gestionar los recursos estticos como de prohibir el acceso directo.
La nueva aplicacin se llamar ejemplo01-2. Este proyecto est basado en el anterior, pero el archivo
de configuracin de la aplicacin web.xml utiliza en este caso un filtro en su lugar e introduce una
definicin de Servlet.
La clase Clientey las pginas JSP son idnticas; sin embargo, vamos a desarrollar un filtro al que
llamaremos FiltroControlador en lugar del archivo ServletControlador.
Cdigo: ejemplo01.FiltroControlador.java
package ejemplo01;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class FiltroControlador implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws
ServletException
{
this.filterConfig=filterConfig;
}
public void doFilter(ServletRequest req, ServletResponseresp,
FilterChain filterChain)throws IOException, ServletException
{
// conversin
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)resp;
// el uri tiene el siguiente formato /ejemplo01-2/Agregar_
cliente.action
String uri=request.getRequestURI();
// utilizamos el cdigo para dividir este URI
int lastIndex=uri.lastIndexOf("/");
String action=uri.substring(lastIndex + 1);
System.out.println("ACCIN: "+action+" con un filtro");
// para las redirecciones
String urlRetorno=null;
// ejecutar la accin adaptada
if (action.equals("Agregar_cliente.action"))
{
urlRetorno="/jsp/AgregarCliente.jsp";
}
else if (action.equals("ConfirmarAgregar_cliente.action"))
{
// instanciar el objeto
Cliente cliente=new Cliente();
// actualizacin del objeto
cliente.setIdentificador(request.getParameter("identificador"));
cliente.setContrasena(request.getParameter("contrasena"));
// devolver el objeto en la vista
request.setAttribute("cliente", cliente);
//pgina de visualizacin del cliente
urlRetorno="/jsp/MostrarCliente.jsp";
}
// redireccin a la vista adaptada
if (urlRetorno!=null)
{
request.getRequestDispatcher(urlRetorno).forward(request,
response);
}
// para los recursos estticos
else
{
filterChain.doFilter(request, response);
}
}
URL
para
agregar
un
cliente:
En resumen
En este captulo se ha presentado el mecanismo de desarrollo Web en Java con el modelo de diseo
MVC Modelo Vista Controlador. Tambin hemos observado dos tcnicas de programacin. La primera
utiliza el principio de mapping de los URI para interceptar las consultas HTTP con la ayuda de un
Servlet controlador. La segunda introduce el mecanismo de filtro destacado en Struts 2. En el
siguiente captulo, escribiremos nuestra primera aplicacin con el framework Struts 2.
Presentacin
Este captulo presenta el framework Struts 2 a travs de un ejemplo concreto para acelerar y facilitar
el desarrollo de aplicaciones Web.
A partir de este punto del libro, se utilizar el trmino Struts para hacer referencia a Struts 2.
Las aplicaciones de Struts tienen un archivo de configuracin llamado struts.xml. Este archivo de
configuracin es el ms importante y sustituye a la definicin de los Servlets en el descriptor de
implementacin web.xml. Este archivo permite administrar la configuracin de las acciones que se van
a realizar.
Struts tambin tiene un archivo de propiedades presente por defecto en el archivo de la
aplicacinstruts2-core-version.jar llamado default.properties. Este archivo utilizado por defecto contiene
los textos de validacin (mensajes de error y de confirmacin) y los parmetros de configuracin de
Struts. Este archivo predeterminado es suficiente para comenzar con una aplicacin simple (en ingls).
Como ya hemos dicho en el captulo anterior, Struts utiliza un filtro para realizar el enrutamiento hacia
un solo controlador de administracin correspondiente al modelo MVC II. El controlador de Struts es
capaz de:
Determinar el URI para la accin que se va a ejecutar.
Utilizar una clase de accin.
Ejecutar el mtodo de accin de la clase si est asociada.
Si se han introducido datos, crear un objeto y actualizarlo o posicionar los valores de los
parmetros.
Regresar a la vista (pgina JSP) para mostrar la respuesta.
Gracias a la utilizacin de un filtro y de un controlador global, no hemos escrito un controlador
complejo para cada servicio (por ejemplo, administracin de clientes, de artculos...) para administrar
el enrutamiento de la aplicacin. Lo ms importante para el desarrollador es administrar las acciones
asociadas a una demanda especfica.
XML
el
archivo
de
propiedades
Es posible que no haya ningn archivo de configuracin en una aplicacin de Struts. A este tipo de programacin se le conoce
como configuracin cero (zero configuration). Esta tcnica utiliza las anotaciones Java en las clases para no declarar las
acciones en un archivo de configuracin.
En el archivo struts.xml vamos a definir la configuracin general de la aplicacin:
Los parmetros de configuracin de Struts.
Las acciones.
Los interceptores.
Los resultados de las acciones.
El archivo de configuracin struts.xml siguiente se incluye con la aplicacin struts-blank.war.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="false" />
<include file="example.xml"/>
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">HelloWorld</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
</struts>
1. La etiqueta <package/>
Las acciones de Struts se reagrupan por paquete segn un principio semejante al de los paquetes Java. En el ejemplo anterior, el
paquete se llama default.
Una etiqueta <package/>debe tener un atributo namepara hacer referencia al paquete. El atributo opcional namespaceposee el valor
por defecto /. Este espacio de nombre siempre se aade a los URI que ejecutan las acciones del paquete.
Los atributos de la etiqueta <package/>son los siguientes:
name: atributo obligatorio que determina el nombre del paquete.
namespace: atributo que determina el espacio de nombre para todas las acciones del paquete.
extends: atributo obligatorio que determina el paquete padre que se va a heredar.
abstract: atributo poco utilizado que determina si el paquete puede ser heredado o no.
Para invocar un URI se utiliza el siguiente modelo: /contextoaplicacion/nombreaccion.action (o nombreaccion.do).
Ahora, para invocar una accin en un espacio de nombre, debemos utilizar la siguiente sintaxis:
/contextoaplicacion/espaciodenombre/nombreaccion.action (o nombreaccion.do)
Por ejemplo, para ejecutar la parte administrativa del proyecto ejemplo01 definida como sigue, utilizamos el siguiente URI:
http://localhost:8080/ejemplo01/admin
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
...
<package name="ejemplo01" namespace="/admin" extends="strutsdefault">
...
</package>
...
</struts>
sus
propios
paquetes
heredando de este paquete
predeterminado que pone a
disposicin la mayora de los
interceptores tiles para la
2. La etiqueta <include/>
Como ya hemos mencionado, el archivo de configuracin de Struts se llama struts.xml. En una aplicacin compleja, podemos tener
varios paquetes y una gran cantidad de lneas en este archivo de configuracin. Para mejorar la administracin y el mantenimiento
de este archivo, es posible dividir este archivo en varios sub-archivos. Cada sub-archivo se incluye en este archivo principal. La
etiqueta <include/> permite dividir el archivo principal en varios sub-archivos. Idealmente, cada sub-archivo define su propio
paquete.
Mayscula inicial por ejemplo, podemos dividir el archivo de configuracin de la aplicacin por las partes frontoffice y backoffice del
sitio. Cada archivo incluido debe utilizar la misma gramtica (DOCTYPE) y la etiqueta raz del documento <struts/>.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="false" />
<include file="frontoffice.xml"/>
<include file="backoffice.xml"/>
</struts>
Cdigo: frontoffice.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">HelloWorld</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
</struts>
3. La etiqueta <action/>
La etiqueta <action/>se utiliza con una etiqueta <package/> y representa a la accin proporcionada por el paquete. Una accin
debe tener un nombre. El nombre es eleccin del desarrollador, pero debe ser lo ms explcito posible para mejorar el
mantenimiento. Una accin est, en general, asociada a una clase, pero esto no es obligatorio para las redirecciones. Una accin
que no precisa de clase utiliza una instancia de la clase por defecto, ActionSupport.
La sintaxis es la siguiente:
<action name="unaaccion"/>
Por contra, si se ha definido una clase, el nombre de la clase debe estar completamente cualificado (nombrepaquete.nombreclase).
<action name="unaaccion" class="nombrepaquete.nombreclase"/>
Tambin podemos especificar el nombre del mtodo de la clase que debe ejecutarse al inicio de la accin.
La sintaxis es la siguiente:
<action name="unaaccion" class="nombrepaquete.nombreclase"
method="nombremetodo"/>
Si el atributo class est presente en la definicin, pero solamente se ha precisado el atributomethod, por defecto se utilizar el
mtodo execute()de la clase al inicio de la accin.
Por lo tanto, las siguientes declaraciones son idnticas:
<action name="Agregar_Cliente" class="ejemplo02.acciones.Cliente"
method="execute"/>
<action name="Agregar_Cliente" class="ejemplo02.acciones.Cliente"/>
4. La etiqueta <result/>
La etiqueta <result/>se utiliza en el interior de una etiqueta <action/>como sub-elemento. Esta etiqueta permite indicar a Struts
donde devolver el resultado de la accin. Una accin puede devolver varios resultados (todos diferentes), por lo tanto puede tener
varias etiquetas <result/>, cada una correspondiente a un resultado posible. Por ejemplo, una accin puede devolver una pgina
de error en caso de existir un problema de sintaxis en los datos introducidos (input) o una pgina de confirmacin para mostrar el
resultado (con success).
El siguiente es un ejemplo de configuracin para nuestro proyecto de creacin de una cuenta de cliente a partir del identificador y de
la contrasea:
Se
ejecutar
el
primer
resultado
si
el
mtodo agregar() devuelve la
cadena de caracteres success.
En este caso, se mostrar la
pgina
Mostrar.jsp
en
el
navegador. Se ejecutar el
segundo resultado si el mtodo agregar()devuelve la cadena de caracteres input cuando se produce un error en la introduccin de
datos. En este caso, se muestra la pgina de introduccin de datos de nuevo sin perder los datos.
El atributo typede la etiqueta <result/>, determina el tipo de resultado que se va a devolver. Este tipo debe especificarse en el
paquete o un paquete heredado. En el caso de una redireccin, por ejemplo, se define el tipo dispatcher en el paquete struts-default.
Si no utilizamos el atributo typeen la etiqueta <result/>, el valor dispatcher (redireccin) se utiliza por defecto. Si no utilizamos el
atributo nameen la etiqueta <result/>, el valor successse utiliza por defecto.
Por lo tanto, las siguientes declaraciones son idnticas:
Si un mtodo de una
clase
de
accin
devuelve un resultado que
no est presente en una
etiqueta <result/> de la
accin, Struts buscar entonces un resultado correspondiente en el grupo <global-results/>. Esta etiqueta permite realizar
agrupaciones de etiquetas para evitar las definiciones repetitivas. En esta etiqueta, por ejemplo, podemos encontrar las
redirecciones hacia la pgina de inicio del sitio, la pgina de error, de autenticacin o de administracin.
5. La etiqueta <param/>
La etiqueta <param/> puede utilizarse con los elementos <action/>, <result-type/> e<interceptors/> para asignar un valor al
objeto afectado. La etiqueta <param/>posee un atributonamepara nombrar el valor.
Si se utiliza con una accin, la etiqueta <param/>permite asignar un valor a una propiedad.
Por ejemplo, el identificador por efecto para el cliente se determina con la etiqueta <param/>.
<action name="agregar_Cliente" class"...">
<param name="identificador">jlafosse</param>
</action>
6. La etiqueta <constant/>
El archivo de configuracin struts.xml puede estar asociado opcionalmente con el archivo de propiedades struts.properties. Podemos
crear parejas de claves/valores para modificar los valores predeterminados definidos en el archivo default.properties incluido con el
paquete struts2-core-version.jar.
Para modificar o sobrecargar un valor del archivo default.properties sin utilizar el archivostruts.properties, podemos utilizar la
etiqueta <constant/>en el archivo struts.xml.
La etiqueta <constant> posee los atributos name y value. Por ejemplo, para utilizar Struts en modo de desarrollo para la
administracin de los registros y depuraciones, podemos utilizar la siguiente configuracin:
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="false" />
...
</struts>
7. La etiqueta <global-results/>
La etiqueta <global-results/>se utiliza para definir los resultados generales. Si una accin no encuentra definicin de resultado en
su propia definicin, Struts entonces busca un resultado en la definicin de esta etiqueta. La declaracin de los resultados en esta
etiqueta es idntica a la declaracin presente en las acciones.
Si Struts no encuentra ninguna correspondencia de resultado, de manera local o en la etiqueta<global-results/>, se produce una
excepcin.
<global-results>
<result
name="inicio">/vistas/usuarios/index.jsp</result>
<result name="admin"
type="redirectAction">admin/Admin.action</result>
</global-results>
8. La etiqueta <interceptors/>
La etiqueta <interceptors/>se utiliza para definir a los interceptores (una especie de filtro). Como ya se ha explicado, una accin
puede contener una lista de interceptores que operarn sobre las acciones. Antes del poder utilizar un interceptor, debemos
declararlo en la etiqueta<interceptors/>.
Por ejemplo, el siguiente cdigo registra dos interceptores: confirmacin (para las confirmaciones del usuario) y alias (para la
administracin de los parmetros).
<package name="nombredelpaquete" extends="struts-default">
<interceptors>
<interceptor name="confirmacion" class="..."/>
<interceptor name="alias" class="..."/>
</interceptors>
</package>
Ahora
que
ya
se
han
registrado los interceptores,
podemos utilizarlos con la
etiqueta
<interceptorref/>que es un sub-elemento
de la etiqueta <action/>.
A continuacin se muestra un
El
orden
de
declaracin
de
los
interceptores tiene una gran
importancia.
De
hecho,
determina
el orden
de
ejecucin
de
los
interceptores
para
cada
accin.
En
el
ejemplo
anterior,
el
interceptor
confirmacin se ejecutar en
primer lugar seguido de
interceptor alias.
Para evitar la repeticin de declaraciones de interceptores, Struts ofrece la posibilidad de crear grupos de interceptores. El
desarrollador ya no tendr necesidad de realizar mltiples referencias para cada etiqueta <action/>que utiliza estos interceptores.
A continuacin se muestra un ejemplo de definicin de un grupo de interceptores:
<interceptor-stack name="interceptorGlobal">
<interceptor-ref name="confirmacion"/>
<interceptor-ref name="alias"/>
</interceptor-stack>
El
paquete
strutsdefault definir varios grupos de
interceptores.
La
etiqueta
<defaultinterceptor-ref/> especifica
el grupo de interceptores por
defecto.
<default-interceptor-ref name="defaultStack"/>
Si una accin necesita utilizar otros interceptores distintos a los definidos por defecto, podemos redefinir el grupo de interceptores
por defecto. El interceptor paramses el ms importante, ya que permite administrar la copia y el acceso automtico a los datos de los
parmetros de la consulta, en nuestras acciones y para los JavaBeans utilizados. A continuacin, se presenta una lista de los
interceptores de Struts utilizados por defecto:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param
name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param
name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
Arquitectura de Struts 2
El esquema arquitectnico siguiente presenta el funcionamiento del framework basado en la
utilizacin de interceptores y de propiedades de JavaBeans.
Arquitectura de Struts 2
1) El cliente enva consultas hacia un servicio de la aplicacin con los parmetros eventuales.
2) Se consulta el archivo de configuracin de la aplicacin o las anotaciones de clases.
3) Los interceptores asociados a la accin se ejecutan y realizan los servicios asociados (conservar los
parmetros, administrar las sesiones, guardar los mensajes de error...). El interceptor paramsasignan
los valores presentes en la consulta a la clase de accin asociada por medio de sus descriptores de
acceso y ejecutar el mtodo de procesamiento (execute()predeterminado).
4) La vista que se mostrar se selecciona de acuerdo con el archivo de configuracin struts.xml o la
anotacin correspondiente.
5) La clase de accin transmite los datos necesarios a la vista.
6) La vista muestra al cliente los resultados procesados.
utilizar Struts
en
modo
de
depuracin
en
el
struts.devMode=true
Los valores predeterminados de las propiedades del archivo default.properties se detallan a
continuacin:
struts.i18n.encoding=UTF-8: codificacin predeterminada utilizada por Struts.
struts.objectFactory=spring: el objeto Factory utilizado por defecto por Struts.
struts.objectFactory.spring.autoWire=name: el valor utilizado por defecto cuando utilizamos el objeto
SpringObjectFactory. Los valores posibles son: name, type, auto y constructor.
struts.objectFactory.spring.useClassCache=true: este parmetro permite indicar si las instancias de la
integracin de Struts-Spring deben almacenarse en el cach.
struts.objectFactory.spring.autoWire.alwaysRespect=false:
estrategia del autowire.
este
parmetro
permite
administrar
la
este
parmetro
permite
El valor por defecto es true pero por motivos de seguridad, podemos pasarlo a false si no utilizamos
ninguna invocacin dinmica.
struts.enable.SlashesInActionNames=false: este parmetro indica si se autoriza o no el uso de barras
en los nombres de las acciones.
struts.tag.altSyntax=true: este parmetro indica si se autoriza o no el uso de expresiones regulares %
{}.
struts.devMode=false: este parmetro indica si nos encontramos en modo de desarrollo o no. Cuando
este parmetro tiene el valor true, el archivo de configuracin struts.xml se recarga as como los
archivos de confirmacin y los mensajes de propiedades (bundles). Esto significa que no necesitamos
volver a cargar la aplicacin cada vez que se realice una modificacin de un archivo esttico. Asimismo,
este parmetro permite una visualizacin en modo detallado de la depuracin. Sin embargo, el modo
de produccin este parmetro debe establecerse como false para evitar mostrar las excepciones y
reducir la memoria.
struts.i18n.reload=false: este parmetro permite precisar si los archivos de lenguas (bundles) deben
ser recargados para cada consulta. Este principio es muy til en la fase de desarrollo, pero no debe
ser utilizado en un servidor de produccin.
struts.ui.theme=xhtml: este parmetro permite precisar el tema utilizado por defecto con la biblioteca
de etiquetas de Struts.
struts.ui.templateDir=template: este parmetro permite especificar la ruta de las plantillas (templates).
struts.ui.templateSuffix=ftl: este parmetro permite especificar el tipo de plantilla determinado. El valor
predeterminado corresponde a la plantilla propuesta por FreeMarker (ftl), aunque tambin estn
disponibles Velocity (vm) y JSP (jsp).
struts.configuration.xml.reload=false: este parmetro indica si los archivos de configuracin en formato
XML deben ser recargados (struts.xml y los archivos eventuales incluidos).
struts.velocity.configfile=velocity.properties: este parmetro permite
configuracin predeterminado para el motor de plantillas Velocity.
especificar
el
archivo
de
struts.velocity.contexts=: este parmetro permite declarar una lista de clases que puede utilizarse por
el contexto Velocity.
struts.velocity.toolboxlocation=: este parmetro permite declarar la ruta de la caja de herramientas
Velocity.
struts.url.http.port=80: este parmetro permite especificar el puerto utilizado para las consultas y URL
HTTP.
struts.url.https.port=443: este parmetro permite especificar el puerto utilizado por las consultas y
URL HTTPS.
struts.url.includeParams=none: este parmetro permite especificar si los parmetros se incluyen en la
URL.
struts.custom.i18n.resources=testmessages,testmessages2:
archivos de mensajes (bundles) por defecto.
este
parmetro
struts.dispatcher.parametersWorkaround=false:
este
parmetro
funcinHttpServletRequest.getParameterMap() debe activarse o no.
permite
indica
cargar
los
si
la
struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager:
parmetro permite especificar la clase que utilizar FreeMarker.
este
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
El archivo de configuracin del enrutamiento struts.xml se presenta a continuacin. Nos encontramos
con las acciones posibles, que son Agregar_Cliente.action y ConfirmarAgregar_Cliente.action. Utilizamos
igualmente dos constantes para suprimir las invocaciones dinmicas y para pasar al modo de
desarrollo. Tambin nos encontramos con la definicin del paquete con el nombre ejemplo02. La
accinAgregar_Cliente.action realiza una simple redireccin a la vista JSP de aadido. La
accinConfirmarAgregar_Cliente.action utiliza la clase Cliente, pero como el parmetro mtodo no est
definido en la accin, por defecto se llamar al mtodo execute().
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo02" namespace="/" extends="struts-default">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo02.Cliente">
<result>/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Ahora, podemos presentar la clase de accin llamada Cliente. Esta clase utilizar las dos propiedades
del formulario, el identificador y la contrasea. Este archivo es una simple clase POJO. Cada propiedad
posee descriptores de acceso que sern reasignados automticamente durante la llamada de la
accin gracias al interceptor params. As, los mtodos setIdentificador() y setContrasena() sern
ejecutados y afectados por Struts.
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Cliente implements Serializable {
private String identificador;
private String contrasena;
public String getIdentificador() {
return identificador;
}
public void setIdentificadort(String identificador) {
this.identificador= identificador;
}
<p>
<h4>Información sobre el cliente:</h4>
Identificador: <s:property value="identificador"/><br/>
Contraseña: <s:property value="contrasena"/><br/>
</p>
</div>
</body>
</html>
Especificamos la utilizacin de la biblioteca de etiquetas de Struts con la siguiente lnea:
<%@ taglib prefix="s" uri="/struts-tags" %>
Por ltimo, cada propiedad es accesible con la etiqueta Struts <s:property/>gracias a los getters de
la clase de accin.
<s:property value="identificador"/>
Podemos utilizar esta aplicacin, con la siguiente URL para agregar un cliente:
http://localhost:8080/ejemplo02/Agregar_cliente.action
Formulario de ejemplo02
En resumen
Este captulo ha presentado el funcionamiento general de Struts con los interceptores, el archivo de
configuracin de la aplicacin struts.xml y las distintas etiquetas XML de declaracin. Tambin se han
detallado los archivos de configuracin del framework y se ha explicado en profundidad un primer
ejemplo de administracin de clientes.
Presentacin
En el captulo anterior se ha presentado un primer proyecto simple de Struts. El mtodo execute()de
la clase de accin Cliente muestra un registro en la consola de Java con la ayuda del
mtodoSystem.out.println().
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Cliente implements Serializable {
private String identificador;
private String contrasena;
...
public String execute()
{
System.out.println("En el mtodo de la accin");
return "success";
}
}
Registro de Java
Observamos que Struts es muy detallado, muestra varios registros de advertencia para una simple
aplicacin. El framework reposa sobre la interfaz de registro Log4J (http://logging.apache.org/) y, por
lo tanto, puede utilizar un archivo de configuracin para eliminar estos registros o adaptarlos segn
nuestras necesidades.
Estos registros corresponden a las herramientas FreeMarker y XWork. Para eliminarlos, basta con
crear un archivo llamado log4j.properties en el directorio /WEB-INF/src (o /WEB-INF/classes) de la
aplicacin al mismo nivel que el archivo de gestin struts.xml.
El archivo de configuracin ms simple para detener los registros es el siguiente:
Cdigo: ejemplo02.log4j.properties
#definicin del nivel y de los Appender del rootLogger (orden: DEBUG
- INFO - WARN - ERROR - FATAL)
log4j.logger.freemarker=OFF
log4j.logger.com.opensymphony.xwork2=OFF
log4j.logger.org.apache=INFO
log4j.rootLogger=DEBUG
Administracin de la depuracin
Ahora, la depuracin es una operacin sencilla con Struts. El framework propone una etiqueta
XHTML<s:debug/>para mostrar los registros con la informacin de los parmetros, la sesin en curso
o incluso los objetos.
La etiqueta <s:debug/> puede ubicarse en cualquier pgina JSP. Esta etiqueta posee un nico
parmetro opcional llamado id.
A continuacin, retomamos nuestra pgina MostrarCliente.jsp y aadimos esta etiqueta al principio del
archivo.
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Mostrar el cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
...
</html>
Luego, hacemos clic sobre el enlace llamado [debug], lo cual nos permite visualizar la pila de registros
de los objetos presentes en el contexto de la aplicacin. Esta etiqueta permite depurar fcilmente una
aplicacin al igual que visualizar las propiedades de la accin y el contenido de los objetos presentes
en la sesin o en la coleccin de la aplicacin.
Esta consola no funciona actualmente con el navegador Internet Explorer, pero s con Firefox.
System.setProperty(UtilTimerStack.ACTIVATE_PROPERTY,"true");
System.out.println("En el mtodo de la accin");
return "success";
}
}
Para activar la creacin de perfiles de una aplicacin de Struts, sta debe estar configurada
en modo de desarrollo struts.devMode=true. La creacin de perfiles nicamente puede
UtilTimerStack.pop(activite);
}
return "success";
}
}
En resumen
Este captulo ha explicado en primer lugar cmo gestionar los registros con el framework Struts. Struts
utiliza un sistema de visualizacin muy detallado y debe configurarse correctamente en la fase de
desarrollo. En un segundo punto, hemos presentado la etiqueta XHTML <s:debug/> incorporada por
defecto con la biblioteca de etiquetas de Struts. Por ltimo, el ltimo prrafo explica el funcionamiento
de la creacin de perfiles en los desarrollos con la herramienta Struts.
Presentacin
Struts utiliza un controlador principal para la administracin del enrutamiento y de los parmetros. As,
podemos concentrarnos en la realizacin y la codificacin de las distintas acciones del proyecto. Cada
accin de Struts est definida en el archivo de configuracin struts.xml (o en las clases con la tcnica
de configuracin cero). Struts permite igualmente la utilizacin de expresiones regulares y las
invocaciones dinmicas de mtodos.
Clases de accin
La creacin de acciones es el trabajo ms importante de la fase de desarrollo que se debe realizar
con la ayuda del framework Struts. Por ejemplo, tendremos acciones para mostrar la pgina de inicio,
administrar los artculos, ocuparse de la autenticacin del cliente y otras. Algunas operaciones son
simples y no requieren de ninguna clase, sino que realizan simplemente una redireccin, mientras que
otras efectan procesamientos complejos asociados a una clase de accin.
Una clase de accin es simplemente una clase Java. Por lo tanto, esta clase contiene atributos y
mtodos. Sin embargo, es necesario respetar algunas reglas para tener una verdadera clase de
accin.
Cada atributo debe estar asociado a sus descriptores de acceso (getter y setter). Las reglas
son las mismas que para los JavaBeans.
Una clase de accin debe tener un constructor por defecto sin argumento. Sin embargo, si no
creamos ningn constructor por defecto, Struts lo har por nosotros durante la compilacin.
Tambin podemos utilizar otro constructor (por inicializacin), pero en este caso el constructor
por defecto sigue siendo obligatorio.
Una clase de accin debe tener al menos un mtodo para realizar la accin. Por defecto, este
mtodo se llama execute(), pero puede llevar cualquier nombre asociado con la
etiqueta<action/> y el atributo method presentes en el archivo de configuracin de la
accin struts.xml.
Una clase de accin puede estar asociado a varios mtodos. En este caso, la clase de accin
gestionar, por ejemplo, la visualizacin de los clientes, el formulario de creacin, la
confirmacin de la creacin, la consulta, la visualizacin del formulario de modificacin, la
confirmacin de la modificacin y, por ltimo, la eliminacin.
Una clase de accin no necesita heredar de una clase especfica o de una interfaz
determinada.
Sin
embargo,
es
muy
fcil
y
aconsejable
heredar
de
la
clase ActionSupportpresente en el paquete com.opensymphony.xwork2.action.
La clase com.opensymphony.xwork2.actionSupportes la clase de accin predeterminada de Struts. El
framework crea una instancia de esta clase si no se ha especificado ninguna declaracin. Si
implementamos la clase ActionSupport, estarn disponibles las siguientes constantes:
SUCCESS: esta constante indica que la ejecucin de la accin es correcta y que se debe
mostrar la pgina de confirmacin adaptada.
NONE: esta constante indica que la ejecucin de la accin es correcta, pero que no se debe
devolver ningn resultado.
ERROR: esta constante indica que la ejecucin de la accin es incorrecta y que el usuario debe
ser redirigido a una pgina de error.
INPUT: esta constante indica un error de validacin de las entradas o los datos introducidos
por el usuario. Por lo general, ser necesario actualizar la pgina de introduccin de datos sin
perder los datos.
LOGIN: esta constante indica que la accin no puede ejecutarse ya que el usuario no se ha
autenticado y fuerza la visualizacin de la pgina de identificacin.
Por lo tanto estos valores se utilizan para sustituir las cadenas de caracteres success, input Adems,
la herencia de la clase ActionSupport permite igualmente sobrecargar varios mtodos como la
funcinvalidate()para realizar validacin es del usuario en programacin.
Una de las principales ventajas de utilizar Struts es su administracin de las resistencias de
los atributos con la simple declaracin en las clases y los descriptores de acceso. Esto es
posible gracias al interceptor params que administra la pila de los datos. Esta pila de datos es
accesible en las pginas JSP.
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import com.opensymphony.xwork2.actionSupport;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
public Cliente()
{
}
// getter identificador
public String getIdentificador() {
return identificador;
}
// setter identificador
public void setIdentificador(String identificador) {
this.identificador= identificador;
}
// getter contrasena
public String getContrasena() {
return contrasena;
}
// setter contrasena
public void setContrasena(String contrasena) {
this.contrasena= contrasena;
}
public String execute()
{
System.out.println("En el mtodo de la accin");
return "success";
}
}
Dado que las clases de accin de Struts han pasado a ser clases de POJO, la realizacin de pruebas o
el uso de clases es relativamente simple. Podemos instanciar las clases y acceder directamente a las
propiedades mediante los descriptores de acceso.
El siguiente es un ejemplo de cdigo para la utilizacin de la clase Cliente.java.
Cliente cliente=new Cliente() ;
cliente.setIdentificador("jlafosse");
cliente.setContrasena("jerome");
String resultat=cliente.execute();
if(resultat.equals("success"))
{
System.out.println("Correcto");
}
else
{
System.out.println("Error");
}
return "success";
}
}
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" 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">
...
<!-- Parmetros globales -->
<context-param>
<param-name>email</param-name>
<param-value>[email protected]</param-value>
</context-param>
</web-app>
Cdigo: /jsp/AgregarCliente.jsp
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<form method="post" action="ConfirmarAgregar_Cliente.action">
<table>
<tr>
<td>Identificador:</td>
<td><input type="text" name="identificador" value="$
{identificador}"/></td>
</tr>
<tr>
<td>Contraseña:</td>
<td><input type="text" name="contrasena" value="$
{contrasena}"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
value="Agregar el cliente"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Mostrar el cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4>Información sobre el cliente:</h4>
Identificador: <s:property
value="#session.identificadorsesion"/><br/>
Contraseña: <s:property
value="#session.contrasenasesion"/><br/>
</p>
<a href="Eliminar_Cliente.action">Eliminar el cliente</a>
</div>
</body>
</html>
3. Trasladar parmetros
Las declaraciones de accin tambin pueden recibir parmetros estticos en el archivo de configuracin struts.xml. Esta tcnica permite asignar valores a las
propiedades de la clase. Si retomamos nuestro ejemplo anterior, podemos agregar una etiqueta <param/> en la declaracin de la accin. Cada parmetro
corresponde a una propiedad de la clase de accin (setter y getter). El interceptor staticParams se ocupa del mapping entre los parmetros declarados en el
archivostruts.xml y el cdigo de la accin.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
...
<action name="ConfirmarAgregar_Cliente"
class="ejemplo03.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
<param name="pagina">frontoffice</param>
</action>
...
</struts>
Cdigo: ejemplo03.Cliente.java
package ejemplo03;
import java.util.Map;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport implements SessionAware
{
private String identificador;
private String contrasena;
private String pagina;
...
public String getPagina() {
return pagina;
}
public void setPagina(String pagina) {
this.pagina= pagina;
}
...
}
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Mostrar el cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4>Información sobre el cliente:</h4>
Identificador: <s:property
value="#session.identificadorsession"/><br/>
Contraseña: <s:property
value="#session.contrasenasession"/><br/>
Página: <s:property value="pagina"/><br/>
</p>
<a href="Eliminar_Cliente.action">Eliminar el cliente</a>
</div>
</body>
</html>
ejecutar
la
accin
<action name="agregarmodificar_Cliente"
class="ejemplo.Cliente" method="agregarmodificar">
<result name="listadoCliente"
type="redirectAction">listado_Cliente</result>
<result
name="input">/vistas/administradores/Cliente/editarCliente.jsp</result>
</action>
<action name="consultar_Cliente" class="ejemplo.Cliente"
method="consultar">
<result>/vistas/administradores/Cliente/consultarCliente.jsp</result>
</action>
<action name="Eliminar_Cliente" class="ejemplo.Cliente"
method="eliminar">
<result name="listadoCliente"
type="redirectAction">listado_Cliente</result>
</action>
</package>
</struts>
Por supuesto, se puede duplicar este cdigo para, por ejemplo, la administracin de los artculos.
...
<action name="listado_Articulo" class="ejemplo.Articulo"
method="listado">
<result>/vistas/administradores/Cliente/listadoArticulo.jsp</result>
</action>
<action name="editar_Articulo" class="ejemplo.Articulo"
method="editar">
<result>/vistas/administradores/Articulo/editarArticulo.jsp</result>
</action>
...
Enseguida podemos observar las repeticiones de declaraciones en el archivo de configuracin y la
complejidad del cdigo a nivel de etiquetado. Con la utilizacin de las invocaciones dinmicas de
mtodos, podemos sustituir todo lo anterior por el siguiente cdigo:
...
<action name="*_*" class="ejemplo.{2}Accion" method="{1}">
<result name="listado{2}"
type="redirectAction">listado_{2}</result>
<result name="input">/vistas/administradores/
{2}/editar{2}.jsp</result>
<result>/vistas/administradores/{2}/{1}{2}.jsp</result>
</action>
...
Estas cinco lneas de declaraciones genricas sustituyen a docenas de lneas de cdigo. El
funcionamiento ser idntico y totalmente operativo para administrar clientes, artculos o incluso
categoras. Esta tcnica es muy til, pero requiere de un cierto rigor en la estructura del sitio.
rbol gentico
habilitada
en
el
archivo
de
struts.enable.DynamicMethodInvocation = true
Esta declaracin tambin puede especificarse en el archivo XML struts.xml con la etiqueta adaptada:
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="true" />
...
</struts>
Para poder utilizar la invocacin dinmica de mtodos es necesario declarar el parmetro
value con valor true si esta etiqueta se utiliza en un archivo de configuracin de la aplicacin.
No se recomienda utilizar la invocacin dinmica de mtodos en una aplicacin profesional por
razones evidentes de seguridad. De hecho, las URL pueden ser complejas y tener diferentes
formatos, por lo que se dificulta en gran medida la realizacin de pruebas de verificacin. Por ltimo,
el SEO efectuado por los principales motores de bsqueda en las URL por invocacin dinmica es
muy malo. Para desactivar este servicio, podemos utilizar nuestro archivo struts.xml y la siguiente
declaracin: <constant name="struts.enable.DynamicMethodInvocation" value="false" />
Podemos retomar el proyecto ejemplo04 y utilizar la invocacin dinmica de mtodos. Para ello, se
adapta el archivo struts.xml en consecuencia, as como los diferentes enlaces de la aplicacin.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="true" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="struts-default">
<default-action-ref name="Gestion_Cliente" />
<action name="Gestion_Cliente" class="ejemplo04.Cliente">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="agregar">/jsp/AgregarCliente.jsp</result>
<result name="mostrar">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: ejemplo04.Cliente.java
package ejemplo04;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
public Cliente()
{
}
...
// Mostrar el formulario de edicin (agregar o modificar)
public String agregar()
{
return "agregar";
}
// agregar la informacin del cliente en la sesin
public String ConfirmarAgregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// ningn error
else
{
return "mostrar";
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<form method="post" action="Gestion_Cliente!
ConfirmarAgregar.action">
...
</form>
</div>
</body>
</html>
<result type="redirect">http://www.google.es</result>
</action>
</package>
</struts>
El archivo de la accin simple permite administrar nicamente respuestas.
Cdigo: ejemplo04.Cliente.java
package ejemplo04;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
public Cliente()
{
}
//setter y getter
// agregar la informacin del cliente en la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// ningn error
else
{
return "success";
}
}
}
Por ltimo, la pgina de visualizacin del cliente permite devolver los valores de los atributos.
Podemos destacar que los valores se pierden, pero si se devuelve el parmetro identificador.
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Mostrar el cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4>Información sobre el cliente:</h4>
Identificador: <s:property value="identificador"/>
<%=request.getParameter("identificador") %><br/>
Contraseña: <s:property value="contrasena"/><br/>
<a href="../Google.action">Google</a>
</p>
</div>
</body>
</html>
Es
posible
probar
el
proyecto
ejemplo04
http://localhost:8080/ejemplo04/Agregar_Cliente.action
en
esta
direccin:
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="struts-default">
<default-action-ref name="agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo04.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success"
type="redirectAction">Mostrar_Cliente</result>
</action>
<action name="Mostrar_Cliente" class="ejemplo04.Cliente">
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
4. Redireccin encadenada
El tipo chain permite realizar acciones encadenadas, es decir, una redireccin a otra accin
conservando el estado de la accin original en la accin de destino. Tambin podemos encadenar
acciones hacia otro paquete de Struts 2, especificando el nombre de este ltimo. A continuacin,
retomaremos el ejemplo anterior y encadenar hemos la redireccin de la confirmacin de registro
hacia la visualizacin. Si una accin se encadena con otra accin, la primera accin almacena la el
valor de la pila de ejecucin y lo transmitir a la segunda accin y as sucesivamente.
Este tipo de redireccin permite realizar un enrutamiento sin prdida de parmetros. En el ejemplo
anterior, el encadenado permite no perder la informacin introducida por el usuario.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="strutsdefault">
<default-action-ref name="agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo04.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success"
type="chain">Mostrar_Cliente</result>
</action>
5. FreeMarker y Velocity
Estos tipos de resultados se utilizan para las presentaciones (templates) y las plantillas de
visualizacin. El captulo Los motores de plantillas explica en detalle estos dos tipos de resultados.
6. HttpHeader
Este tipo de resultados se utiliza para reenviar una respuesta HTTP al navegador. Los cdigos de
estado HTTP permiten al navegador tener informacin del lado del servidor. Por ejemplo, el cdigo
404 indica al navegador que el recurso no se ha podido encontrar. El cdigo 500 indica un error
interno relativo al servidor.
Por ejemplo, podemos reenviar una respuesta 403 al navegador durante la autenticacin para
indicar que el acceso a este recurso est prohibido.
Gracias a esta tcnica, Struts podr administrar respuestas especficas. Si las pginas 403
se gestionan a travs del descriptor de implementacin web.xml, nicamente se mostrar
esta pgina.
Cdigo: /WEB-INF/web.xml
<error-page>
<error-code>403</error-code>
<location>/403.html</location>
</error-page>
7. Stream
Este tipo de resultado representado por una secuencia de bytes se utiliza para las imgenes, vdeos
u otros. El captulo Complementos de Struts dedicado al complemento JFreeChart explica en detalle
este tipo de resultado.
8. XSLT
Este tipo de resultado representado por informacin en formato XML se explica en detalle en el
captulo XSLT.
9. PlainText
Este tipo permite devolver texto plano, es decir, el contenido textual de una pgina o de un archivo
fuente. Podemos retomar nuestro proyecto ejemplo04 y devolver tras la autenticacin el cdigo
fuente de la pgina JSP.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="struts-default">
<default-action-ref name="agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo04.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success"
type="plainText">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="struts-default">
<default-action-ref name="agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo04.Cliente" method="agregar">
<exception-mapping result="error"
exception="java.lang.Exception"/>
<result name="error">/jsp/Error.jsp</result>
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: /jsp/Error.jsp
<html>
<head>
<title>Error</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Error en la aplicación<br/>Póngase en
contacto con el webmaster</h3>
</div>
</body>
</html>
La etiqueta <global-exception-mappings/> permite administrar los grupos de excepciones. Por el
contrario, una excepcin declarada en una etiqueta <global-exception-mappings/> debe hacer
referencia a un resultado declarado en una etiqueta <global-results/>. Podemos adaptar nuestro
ejemplo anterior con este tipo de declaracin de excepcin.
La etiqueta de Struts <s:property/> permite visualizar la informacin sobre las excepciones en las
vistas JSP. Esta etiqueta utiliza el atributo exception.messagepara utilizar el mensaje asociado a las
excepciones en el archivo de propiedades y el atributo exceptionStackpara mostrar los registros de
la pila de excepcin de forma detallada.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo04" namespace="/" extends="struts-default">
<default-action-ref name="agregar_Cliente" />
<global-results>
<result name="error">/jsp/Error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="error"
exception="java.lang.Exception"/>
</global-exception-mappings>
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo04.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: /jsp/Error.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Error</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Error en la aplicación<br/> Pónganse en
contacto con el webmaster</h3>
<s:property value="exceptionStack"/>
<s:property value="exception.message"/>
</div>
</body>
</html>
En resumen
Este captulo ha presentado la administracin de acciones de Struts. En primer lugar, se han detallado
las clases de accin y sus resultados asociados. La segunda parte del captulo explica las distintas
tcnicas de acceso a los recursos y el procesamiento de los parmetros. Los siguientes prrafos se
han centrado en la administracin del mapping y la invocacin dinmica de mtodos. El penltimo
prrafo presenta en forma de ejemplos los distintos tipos de resultados con Struts y la ltima parte se
centra en el procesamiento de las excepciones.
Presentacin
El framework Struts 2 incluye una librera de etiquetas, para las pginas JSP denominadas vistas. La
biblioteca de etiquetas de Struts se compone de una primera categora para la gestin de datos y una
segunda para las propiedades, parmetros y estructuras de controles (condicionales, bucles...).
Dichas etiquetas permiten programar servicios dinmicos del lado del cliente sin utilizar cdigo Java.
De este modo, podemos rellenar campos de formularios, conservar datos o mostrar objetos
fcilmente.
1. La etiqueta <s:form/>
Esta etiqueta permite mostrar un formulario HTML en el documento. Existen parmetros habituales disponibles como la accin que se va a ejecutar, el mtodo HTTP
o el tipo de codificacin utilizado. El proyecto ejemplo05, basado en la aplicacin anterior ejemplo04, utiliza la etiqueta <s:form/>en la pgina /jsp/AgregarCliente.jsp.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador"
label="Identificador" />
<s:textfield name="contrasena" label="Contrasea"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
Utilizaremos de nuevo nuestro ejemplo05 y la pgina JSP AgregarCliente.jsp para modificar el tema.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente"
theme="simple">
<s:textfield name="identificador"
label="Identificador" />
<s:textfield name="contrasena" label="Contrasea"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente"
theme="css_xhtml">
<s:textfield name="identificador"
label="Identificador" />
<s:textfield name="contrasena" label="Contrasea"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
3. La etiqueta <s:textfield/>
La etiqueta <s:textfield/>permite crear un campo de texto en la pgina JSP. Encontraremos los atributos HTML habituales con maxlength, readonlyy size.
4. La etiqueta <s:password/>
La etiqueta <s:password/> permite
con maxlength, readonlyy size.
crear
un
campo
de
tipo
contrasea
en
la
pgina
JSP.
Encontraremos
los
atributos
HTML
habituales
5. La etiqueta <s:submit/>
La etiqueta <s:submit/>permite crear un botn de validacin del formulario. Si utilizamos de nuevo nuestra pgina JSP AgregarCliente.jsp, podemos observar el uso
de las etiquetas <s:textfield/>,<s:password/>y <s:submit/>.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" label="Identificador"
labelposition="top" cssClass="input"
cssErrorClass="inputerror" tooltip="Introduzca su
identificador"
tooltipConfig="#{tooltipDelay:500,tooltipIcon:/images/ayuda.
jpg}"/>
<s:password name="contrasena" label="Contrasea"
labelposition="top"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
6. La etiqueta <s:reset/>
La etiqueta <s:reset/>permite validar los datos del formulario. Esta etiqueta recoge las propiedades de las etiquetas anteriores.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" label="Identificador"
labelposition="top" cssClass="input"/>
<s:password name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar el cliente"/>
<s:reset/>
</s:form>
</div>
</body>
</html>
7. La etiqueta <s:label/>
La etiqueta <s:label/>permite crear una etiqueta HTML con el fin de asignar un vnculo HTML que permita posicionar el cursor en un campo determinado.
8. La etiqueta <s:head/>
La etiqueta <s:head/>permite crear una etiqueta HTML de encabezado para el contenido de los archivos asociados, por ejemplo archivos JavaScript o Ajax. Esta
etiqueta no se utiliza con frecuencia.
9. La etiqueta <s:textarea/>
La etiqueta <s:textarea/> permite crear una etiqueta HTML de tipo campo de texto de varias lneas. Contiene principalmente los atributos importantes
siguientes colsy rowspara precisar respectivamente el nmero de columnas y filas.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textarea name="contrasena" label="Contrasea"
labelposition="top" cols="18" rows="5"/>
<s:submit value="Agregar el cliente"/>
<s:reset/>
</s:form>
</div>
</body>
</html>
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private boolean Boletin;
public Cliente()
{
}
// getter y setter
public boolean isBoletin() {
return Boletin;
}
public void setBoletin(boolean Boletin) {
this.Boletin = Boletin; }
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de
error volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:checkbox name="Boletin" label="Inscripcin al Boletin"/>
<s:submit value="Agregar el cliente"/>
<s:reset/>
</s:form>
</div>
</body>
</html>
public Cliente()
{
}
public int getProfesion() {
return profesion;
}
public void setProfesion(int profesion) {
this.profesion = profesion;
}
public Map<Integer, String> getListaProfesiones() {
this.listaProfesiones.put(1, "Informtico");
this.listaProfesiones.put(2, "Formador");
return listaProfesiones;
}
public void setListaProfesiones(Map<Integer, String>
listaProfesiones) {
this.listaProfesiones = listaProfesiones; }
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
}
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0// EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo05" namespace="/" extends="struts-default">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente" class="ejemplo05.Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo05.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success"
type="chain">Mostrar_Cliente</result>
</action>
</package>
</struts>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:checkbox name="Boletin" label="Inscripcin al Boletin"/>
<s:select name="profesion" label="Profesin"
list="listaProfesiones"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
Cdigo: ejemplo05.Cliente.java
package ejemplo05;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private int profesion;
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
public Cliente()
{
}
public List<Profesion> getListaProfesiones() {
listaProfesiones.add(new Profesion(1,
"Informtico"));
listaProfesiones.add(new Profesion(2,
"Formador"));
listaProfesiones.add(new Profesion(3, "SGBDM"));
listaProfesiones.add(new Profesion(4, "Responsable
de red"));
return listaProfesiones;
}
public void setListaProfesiones(List<Profesion>
listaProfesiones) {
this.listaProfesiones = listaProfesiones;
}
public int getProfesion() {
return profesion;
}
public void setProfesion(int Profesion) {
this.profesion = profesion;
}
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
}
Clase: /vistas/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:select name="profesion" label="Profesin"
labelposition="top" list="listaProfesiones" listKey="idProfesion"
listValue="nombre"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
seleccin. Vamos a retomar el ejemplo anterior y a agregarle una etiqueta <s:optgroup/>para gestionar el nivel de cualificacin asociado a la profesion.
Cdigo: ejemplo05.Cliente.java
package ejemplo05;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private int profesion;
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
private Map<Integer,String> nivelProfesion=new
HashMap<Integer,String>();
public Cliente()
{
}
// getter y setter
public Map<Integer, String> getNivelProfesion() {
nivelProfesion.put(1, "Sec.");
nivelProfesion.put(2, "Diplom.");
nivelProfesion.put(3, "Lic.");
return nivelProfesion;
}
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:select name="profesion" label="Profesin"
labelposition="top" list="listaProfesiones" listKey="idProfesion"
listValue="nombre"/>
<s:optgroup label="Nivel"
list="nivelProfesion"/>
</s:select>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
es necesario hacer uso de los descriptores de acceso tanto para la lista como para la eleccin del botn.
Vamos a agregar una lista de botones radio para el tipo de contrato del cliente.
Cdigo: ejemplo05.Cliente.java
package ejemplo05;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private int profesion;
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
private Map<Integer,String> nivelProfesion=new
HashMap<Integer,String>();
private SortedMap<Integer,String> listaContratos=new
TreeMap<Integer,String>();
private int contrato;
public Cliente()
{
}
// getter y setter
public Map<Integer, String> getNivelProfesion() {
nivelProfesion.put(1, "Sec.");
nivelProfesion.put(2, "Diplom.");
nivelProfesion.put(3, "Lic.");
return nivelProfesion;
}
public SortedMap<Integer, String> getListaContratos() {
listaContratos.put(1, "Tiempo parcial");
listaContratos.put(2, "Temporal");
listaContratos.put(3, "Jornada completa");
return listaContratos;
}
public void setListaContratos(SortedMap<Integer, String>
listaContratos) {
this.listaContratos = listaContratos;
}
public int getContrato() {
return contrato;
}
public void setContrato(int contrato) {
this.contrato = contrato;
}
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:select name="profesion" label="Profesin"
labelposition="top" list="listaProfesiones" listKey="idProfesion"
listValue="nombre"/>
<s:optgroup label="Nivel" list="nivelProfesion"/>
</s:select>
<s:radio name="contrato" label="Tipo de contrato"
list="listaContratos"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
}
//Clase de gestin de las comida
class Comida
{
private int id;
private String nombre;
public Comida(int id, String nombre)
{
this.id=id;
this.nombre=nombre;
}
// getter y setter
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:select name="profesion" label="Profesin" labelposition="top"
list="listaProfesiones" listKey="idProfesion" listValue="nombre"/>
<s:checkboxlist name="comida" label="Comida" list="listaComida"
listKey="id" listValue="nombre" labelposition="top"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
1. La etiqueta <s:property/>
La etiqueta <s:property/> permite mostrar, a partir de expresiones OGNL, datos presentes en la
clase de accin asociada con sus descriptores de acceso o en el contexto de la aplicacin
(application, session, request, parameters, attr). El atributo valuepermite nombrar la propiedad y el
parmetro espacepermite evitar los caracteres HTML especiales (,, &, < y >).
Vamos a retomar el ejemplo anterior ejemplo05 y utilizar las etiquetas de control en nuestro nuevo
proyecto ejemplo06.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:property value="emailWebmaster"/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
La notacin JSP EL Expression Language tambin puede utilizarse con el dlar y las llaves:
${emailWebmaster}.
El segundo ejemplo permite leer un valor presente en el contexto de la aplicacin. El alcance de la
variable se precisa en el nombre de la misma con el carcter almohadilla #. Agregaremos un
parmetro de aplicacin en el archivo /WEB-INF/web.xml para la direccin de correo electrnico de
contacto.
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" 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">
<context-param>
<param-name>emailContacto</param-name>
<param-value>[email protected]</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filterclass>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAnd
ExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
Email Webmaster: <s:property
value="emailWebmaster"/><br/>
Email Webmaster: ${emailWebmaster}<br/>
Email de contacto: <s:property
value="#aplicacin.emailContacto"/><br/>
<s:submit value="Agregar el cliente"/>
</s:form>
</div>
</body>
</html>
Utilizacin de la notacin EL
2. La etiqueta <s:a/>
La etiqueta <s:a/>permite crear un vnculo HTML conforme. Esta etiqueta se utiliza muy poco con
Struts ya que en realidad no aporta ventajas en relacin con la etiqueta HTML estndar,
exceptuando que permite gestionar el id de la sesin.
3. La etiqueta <s:action/>
La etiqueta <s:action/>se utiliza para ejecutar una accin y recuperar el resultado de la misma en
una variable.
Por ejemplo, podemos
denominadaobjeto.
ejecutar
una
accin
almacenar
el
resultado
en
una
variable
4. La etiqueta <s:param/>
La etiqueta <s:param/> permite pasar parmetros a una accin. sta se utiliza en vnculos o
formularios.
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
Email Webmaster: <s:property
value="emailWebmaster"/><br/>
Email Webmaster: ${emailWebmaster}<br/>
Email de contacto: <s:property
value="#aplicacin.emailContacto"/><br/>
<s:submit value="Agregar el cliente"/>
</s:form>
<s:url id="mostrarURL" action="Mostrar" >
<s:param name="pginaActual">${header.host}</s:param>
</s:url>
<s:a href="%{mostrarURL}">Pasar un parámetro a
una acción</s:a>
</div>
</body>
</html>
Se define una URL, as como un parmetro denominado paginaActual que permite recuperar el
nombre del sistema local. El vnculo de Struts se configura automticamente con estos datos.
5. La etiqueta <s:bean/>
La etiqueta <s:bean/>permite crear una instancia de una clase JavaBean. Esta etiqueta es similar a
la etiqueta JSP <jsp:useBean/>. Esta etiqueta facilita el atributo name, que permite definir una clase
JavaBean, y el atributo id, que permite nombrar la instancia. Esta etiqueta <s:bean/> se utiliza
frecuentemente asociada con la etiqueta <s:param/>para asignar los valores al JavaBean.
Vamos a definir una nueva clase JavaBean denominada Profesiony a utilizar la etiqueta <s:bean/>.
Cdigo: ejemplo06.Profesion.java
package ejemplo06;
public class Profesion {
private int id;
private String nombre;
public Profesion()
{
}
// getter y setter
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:bean name="ejemplo06.Profesion" id="profesion">
<s:param name="id" value="1"/>
<s:param name="nombre" value="Formador"/>
</s:bean>
Profesión: <s:property value="#profesion.id"/>
<s:property value="#profesion.nombre"/>
</div>
</body>
</html>
La inicializacin de una propiedad de tipo cadena de caracteres en Struts se realiza con
comillas. En el ejemplo vemos cmo comienza la propiedad nombre de la profesion. <s:param
name="nombre" value="Formador"/>.
6. La etiqueta <s:date/>
La etiqueta <s:date/>se utiliza para configurar la visualizacin de las fechas. Esta etiqueta es muy
til para mostrar las fechas en funcin del pas del usuario habitual. Por ejemplo, un usuario espaol
preferir ver las fechas con formato dd/mm/aaaa, mientras que un ingls preferir el formato aaaamm-dd.
La modificacin de la vista JSP permite gestionar dos objetos de fecha con los dos tipos de formato
anteriores.
Cdigo: /jsp/FechaCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<s:bean name="java.util.Fecha" id="fecha"/>
7. La etiqueta <s:set/>
La etiqueta <s:set/> permite crear una propiedad asociada a su valor dinmico (tipo primitivo u
objeto). La variable se puede crear en el contexto de la aplicacin, en la sesin, en la solicitud actual
o en la pgina.
Cdigo: /jsp/FechaCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<s:bean name="java.util.Fecha" id="fecha"/>
<s:set name="fechasolicitud" value="#fecha"/>
Fecha con nueva variable: <s:property
value="#fechasolicitud"/>
</div>
</body>
</html>
Esta etiqueta se utiliza con frecuencia para almacenar un objeto complejo en una variable
con el fin de acceder a sus valores en el resto del cdigo.
8. La etiqueta <s:push/>
La etiqueta <s:push/> es muy similar a la etiqueta <s:set/>. Permite inicializar un objeto pero
utilizarlo entre la etiqueta de inicio <s:push>y la etiqueta de finalizacin </s:push>.
9. La etiqueta <s:url/>
La etiqueta <s:url/> permite crear URL dinmicas que ms tarde sern utilizadas en vnculos o
formularios HTML. Esta etiqueta se utiliza en el ejemplo anterior para configurar un vnculo HTML con
la ayuda de la etiqueta <s:param/>.
<s:url id="mostrarURL" action="Mostrar" >
<s:param name="pginaActual">${header.host}</s:param>
</s:url>
<s:a href="%{mostrarURL}">Pasar un parámetro a
una acción</s:a>
Cdigo: ejemplo06.Cliente.java
package ejemplo06;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar el cliente"/>
</s:form>
<s:iterator value="listaProfesiones">
<s:property value="id"/> - <s:property value="nombre"/><br/>
</s:iterator>
</div>
</body>
</html>
public Cliente()
{
}
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return "mostrar";
}
}
// comparar dos objetos
public Comparator<Object> getMyComparator()
{
return new Comparator<Object>()
{
public int compare(Object o1, Object o2)
{
return
o1.toString().compareTo(o2.toString());
}
};
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" label="Contrasea"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar el cliente"/>
</s:form>
<s:sort comparator="myComparator" source="listaProfesiones"
id="clasifListaProfesiones"/>
<s:iterator value="#atr.clasifListaProfesiones">
<s:property value="id"/> - <s:property
value="nombre"/><br/>
</s:iterator>
</div>
</body>
</html>
En resumen
En este captulo se presentan las diferentes etiquetas de Struts para formatear los datos y las
acciones. En la primera parte se detallan las etiquetas de los formularios utilizadas en las pginas JSP
o vistas, con el fin de facilitar el formateo de los datos. El segundo prrafo presenta, siempre a travs
de ejemplos, las etiquetas de controles para acceder a los datos, las estructuras y las clasificaciones.
Presentacin
La gestin de los mensajes de error y confirmacin es una tarea muy importante durante el desarrollo
de aplicaciones informticas. La internacionalizacin en programacin y desarrollo consiste en
gestionar los idiomas y as facilitar pginas multilinges. El trmino i18n se ha estandarizado para la
internacionalizacin y corresponde en realidad a los 18 caracteres que componen la palabra
internacionalizacin entre la i y la n. En programacin, el idioma se presenta mediante la configuracin
regional. Dicha configuracin se define en funcin del idioma y el pas.
La configuracin regional se compone de dos parmetros:
La primera parte, el idioma, guin bajo, y la segunda parte, el pas.
As, por ejemplo el espaol de Espaa se representara de la siguiente forma: es_ES.
El espaol de Argentina: es_AR.
El ingls de EE.UU.: en_US.
Para la localizacin de los mensajes en Java, utilizamos un conjunto de archivos denominados bundle
en ingls. Se utiliza un archivo de propiedades para cada idioma/configuracin regional. Cada archivo
cuenta con un prefijo comn, basename, y debe tener la extensin .properties.
Los archivos de idiomas concretos deben tener el mismo prefijo seguido de guin bajo y del cdigo del
idioma. Para ser accesibles, estos archivos deben estar incluidos en el classpath.
La internacionalizacin permite cambiar la visualizacin del texto o las imgenes por ejemplo, en
funcin del tipo de usuario. La plataforma Java EE se ha concebido para ser compatible con las
aplicaciones multilinges mediante un principio simple y eficaz que en cierta medida se utiliza en
Struts. Las clases de accin de Struts utilizan el mtodo getText()para leer los mensajes presentes
en un archivo de texto de propiedades en funcin del idioma.
Por ltimo, todas las aplicaciones profesionales deberan utilizar el mecanismo de internacionalizacin
incluso en los desarrollos monolinges (un solo idioma). Recurrir a un archivo de propiedades, incluso
en un mismo idioma, permite mejorar el futuro mantenimiento y modificar los textos estticos sin
necesidad de volver a compilar la aplicacin.
Aplicacin
Como hemos dicho anteriormente, la configuracin regional est definida por un idioma y un pas.
Tenemos por ejemplo el ingls de EE.UU. (en_US) y el ingls de Canad (en_CA). El idioma es la parte
ms importante de la definicin y en la mayora de los casos se puede utilizar sin el pas (por
ejemplo:propiedades_es.properties, propiedades_en.properties).
Las aplicaciones multilinges utilizan archivos de texto compuestos de una clave/valor para cada
configuracin regional. Cada par de valores se compone de una clave nica y de su valor asociado.
Las claves son de tipo cadena de caracteres y los valores de las claves pueden ser de cualquier tipo.
El nombre de los archivos de propiedades utilizados para leer la informacin debe estar compuesto de
la forma siguiente:
basename_idioma_pas.properties
As, tenemos el campo basename que corresponde al nombre del archivo (por ejemplo: recursos), el
idioma (por ejemplo: es), el pas (por ejemplo: ES) y por ltimo la extensin del archivo (.properties).
De
este
modo,
para
nuestros
ejemplos
tendramos
dos
archivos: recursos_es.properties yrecursos_en.properties.
La utilizacin de la internacionalizacin con Struts es muy sencilla, ya que cada clase heredera de la
clase
ActionSupport
puede
utilizar
la
internacionalizacin.
Asimismo,
la
interfazcom.opensymphony.xwork2.TextProvider facilita el acceso a los bundles del proyecto. El
mtodogetText(String clave) permite recuperar el valor asociado a la clave convertida en
parmetro. El resultado de este mtodo ser null si la clave no se encuentra en el archivo de
propiedades.
La segunda escritura del mtodo getText(String clave, String valorpordefecto)permite recuperar
el valor asociado a la clave convertida en parmetro y el valor predeterminado si dicha clave no se
encuentra en el archivo de propiedades.
La tercera escritura getText(String clave, String[] formato)permite recuperar el valor asociado a
la clave convertida en parmetro, en relacin con el formato especificado en los parmetros.
La escritura getText(String clave, String valorpordefecto, String[] formato)permite recuperar
el valor asociado a la clave convertida en parmetro, en relacin con el valor predeterminado y el
formato asociado.
Cliente.java
est
asociada
con
el
archivo
de
Esta tcnica, aunque de sencilla implementacin, se utiliza poco por razones de agilidad en el
mantenimiento (un archivo por clase).
A un archivo de propiedades que tenga el mismo nombre que una interfaz utilizada por cada
clase que lo necesite. Por ejemplo: la interfaz Recursos utilizada por cada clase usuaria y el
archivo Recursos.properties.
A un archivo de propiedades que tenga el mismo nombre que una clase heredada por cada
clase que lo necesite. Por ejemplo: el archivo de propiedades ActionSupport.properties utilizado
por cada clase heredera de la clase ActionSupport.
A
un
archivo
de
modelo
si
interfazcom.opensymphony.xwork2.ModelDriven.
nuestras
clases
implementan
la
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="false" />
<package name="ejemplo07" namespace="/" extends="struts-default">
<default-action-ref name="agregar_Cliente" />
<action name="Agregar_Cliente" class="ejemplo07.Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo07.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="SUCCESS">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: ejemplo07.Cliente.java
package ejemplo07;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
public Cliente()
{
}
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return SUCCESS;
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.agregar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3><s:property value="%{getText(cliente.agregar)}"/></h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="%{getText(cliente.identificador)}" labelposition="top"
cssClass="input"/>
<s:textfield name="contrasena" label="%
{getText(cliente.contrasena)}" labelposition="top"
cssClass="input"/>
<s:submit value="%{getText(cliente.agregar)}"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%{getText(cliente.identificador)}"/>:
<s:property value="identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/>:
<s:property value="contrasena"/><br/>
</p>
</div>
</body>
</html>
Cdigo: /ejemplo07/package.properties
cliente.agregar=Agregar un cliente
cliente.identificador=Identificador
cliente.contrasena=Contrasea
cliente.mostrar=Mostrar un cliente
Datos multilinges
En el ejemplo anterior hemos mostrado las dos tcnicas que ofrece Struts para acceder a los datos de
los archivos de propiedades, la etiqueta <s:property/>y el atributo labelde las diferentes etiquetas.
Asimismo, Struts propone la etiqueta <s:text/>para el acceso a las propiedades. Esta etiqueta es
equivalente al uso del mtodo getText(...)en la etiqueta <s:property/>, y permite nicamente una
escritura ms ligera del acceso a los datos.
Las declaraciones siguientes tambin son equivalentes:
<s:property value="%{getText(cliente.agregar)}"/>
<s:text name="cliente.agregar"/>
Si el atributo idest presente en la etiqueta <s:text/>, esta no se muestra pero se almacena en la
variable indicada en la propiedad. As, esta propiedad puede mostrarse con la etiqueta<s:property/>.
<s:text name="cliente.agregar" id="msj_clienteagregar"/>
<s:property value="#msj_clienteagregar"/>
Por ltimo, la etiqueta <s:text/> permite transmitir parmetros presentes en el archivo de
propiedades. Esta tcnica se utiliza frecuentemente para mejora la ergonoma. Vamos a agregar una
lnea a nuestro archivo de propiedades paquete.properties.
cliente.bienvenida=Hola {0}
Ahora podemos transmitir el identificador de la
etiqueta <s:text/>a la pgina /jsp/MostrarCliente.jsp.
persona
autenticada
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%
{getText(cliente.identificador)}"/>: <s:property
value="identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/
>: <s:property value="contrasena"/><br/>
<s:text name="cliente.bienvenida">
<s:param><s:property
value="identificador"/></s:param>
</s:text>
</p>
</div>
</body>
</html>
travs
de
la
bundles
dinmicos
deben
Cdigo: ejemplo07.MisRecursos.java
package ejemplo07;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ListResourceBundle;
public class MisRecursos extends ListResourceBundle{
// objeto fecha y presentacin
Date fecha=new Date();
SimpleDateFormat fechadia=new
SimpleDateFormat("dd/MM/yyyy");
// devolver el contenido adaptado
public Object[][] getContents()
{
return traducciones;
}
// contenido dinmico (clave/valor)
Object[][] traducciones= {
{"cliente.bienvenida","Buenos das{0} - conexin:
"+fechadia.format(fecha)}
};
}
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%
{getText(cliente.identificador)}"/>: <s:property
value="identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/
ser
herederas
de
la
Cdigo: ejemplo07.package.properties
...
cliente.inicio=Gracias por su autenticacin
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
En resumen
En este captulo se ha presentado la gestin de mensajes de propiedades y el mecanismo de
internacionalizacin de Struts. Las aplicaciones profesionales deben utilizar este mecanismo avanzado
que permite un fcil mantenimiento y la evolucin de un proyecto tanto monolinge como multilinge.
Presentacin
La validacin de datos permite mejorar la seguridad de un sistema, as como su solidez, mediante
reglas y controles. Las validaciones permiten tambin conservar la coherencia y homogeneidad de
dicho sistema, ya que cada dato u objeto presente en la base de datos se verifica antes de su
insercin o modificacin.
Las verificaciones y controles realizados en los campos de un sistema son complicados y representan
una etapa de compleja implementacin dentro de las aplicaciones Java EE. Para ello, Struts facilita un
mtodo de validacin sencillo y eficaz basado en el framework de validacin XWork.
Los validadores de Struts no requieren programacin. Cada definicin de una regla se configura en
relacin con una propiedad o un objeto en un archivo de texto en formato XML de fcil mantenimiento
o con anotaciones Java. Asimismo, Struts propone, de acuerdo con sus estndares, el uso de reglas
en los archivos de validacin, la asociacin de mensajes con los archivos de propiedades o bundles y
las validaciones de la programacin en las clases.
Aplicacin
En Struts, se utilizan dos tipos de validadores:
Los validadores de tipo campo o atributo. Estos validadores se utilizan en relacin con los
campos de formularios HTML y estn asociados con una o varias reglas de validacin.
Los validadores de tipo condicional, que no estn asociados con los campos de formularios
pero permiten realizar comprobaciones de la programacin en las clases de accin de las
aplicaciones.
La aplicacin de las validaciones en una aplicacin de Struts se divide en tres etapas:
Etapa 1: definicin de la accin que debe verificar sus entradas.
Etapa 2: creacin del archivo de validacin asociado. El nombre del archivo debe adaptarse al
modelo siguiente: NombreClaseAccion-validacion.xml.
Etapa 3: definicin de la pgina a la que se deber acudir en caso de error en la introduccin
de datos. Esta definicin se realiza en el archivo de configuracin struts.xml a travs de la
etiqueta <result name="input"/>.
El nombre del archivo de configuracin depende de las validaciones que deseemos llevar a
cabo. El modelo presentado a continuacin, con el nombre de la clase seguido del trmino
validacin, es el ms frecuente (NombreClaseAccion_validacion.xml). No obstante, si deseamos
realizar nicamente la verificacin del mtodo agregarmodificar_Cliente() de la clase de
accin ClienteAccion, tendremos un archivo de tipo ClienteAccion-agregarmodificar_Clientevalidacion.xml.
Esta tcnica permite as aplicar las validaciones a una accin concreta del controlador o utilizar un
archivo de validacin por ejemplo para crear un cliente y otro para realizar modificaciones con reglas
diferentes. Los archivos de validacin de Struts deben comenzar por la definicin de la gramtica
adaptada. La etiqueta raz del documento XML es <validators/>. Esta etiqueta puede contener
etiquetas <field/> relacionadas con campos de formularios o etiquetas <validator/> para la
definicin de validadores asociados. La etiqueta <field/>contiene un atributo denominado nameque
establece el vnculo con el nombre de un campo de formulario HTML que debe validarse. Podemos
realizar tantas validaciones de campos de formularios como deseemos.
La etiqueta <field/>contiene en s misma una o varias etiquetas <field-validator/>asociadas con
un atributo typeque define el tipo de validacin que se va a llevar a cabo (campo obligatorio, e-mail,
nmero entero...).
Tambin podemos transmitir parmetros a una etiqueta <field/>con el fin de precisar un valor o un
espacio. Por ltimo, la etiqueta <message/> presente en las etiquetas <field-validator/> permite
precisar el mensaje que debe mostrarse en caso de error de validacin.
Vamos a aplicar las validaciones a partir de nuestro formulario de cliente. El nuevo proyecto,
denominado ejemplo08 se basa en la aplicacin ejemplo07. Cambiaremos el nombre de la clase de
accin Clientepor ClienteAccioncon el fin de respetar las convenciones de Struts.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo08" namespace="/" extends="struts-default">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente"
class="ejemplo08.ClienteAccion">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo08.ClienteAccion" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="SUCCESS">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>
Cdigo: ejemplo08.ClienteAccion.java
package ejemplo08;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private String identificador;
private String contrasena;
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return SUCCESS;
}
}
}
Cdigo: /ejemplo08/ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="identificador">
<field-validator type="requiredstring">
<message>El campo identificador es obligatorio</message>
</field-validator>
</field>
<field name="contrasena">
<field-validator type="requiredstring">
<message>El campo contrasea es obligatorio</message>
</field-validator>
</field>
</validators>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" id="contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:submit value="Agregar un cliente"/>
</s:form>
</div>
</body>
</html>
Las definiciones configuradas son simples y se basan en los campos identificador y contrasea del
formulario. La relacin entre los campos del formulario y el archivo de validacin se realiza a travs de
las etiquetas <field name="identificador"> y <field name="contrasena">. En nuestro caso,
verificamos nicamente si los campos estn vacos. La definicin del enrutamiento se realiza en el
archivo struts.xml y permite precisar que en caso de error de validacin, se acuda a la pgina
precisada por el parmetro input sin perder datos.
<action name="ConfirmarAgregar_Cliente"
class="ejemplo08.ClienteAccion" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="SUCCESS">/jsp/MostrarCliente.jsp</result>
</action>
Podemos definir la clase CSS .errorMessage propuesta por Struts con el fin de configurar un color
adaptado (rojo) para mostrar los mensajes de error. Se agregar el estilo siguiente al
archivo/css/estilos.css.
.errorMessage
{
color:#D53A3E;
font-family:tahoma, verdana, arial, sans-serif;
font-size:13px;
}
Ahora, podemos intentar agregar una cuenta de cliente sin introducir el identificador ni la contrasea.
padding-right:50px;
text-align:justify;
border-style:solid;
border-top-width:1px;
border-top-color:#D53A3E;
border-right-width:2px;
border-right-color:#D53A3E;
border-bottom-width:2px;
border-bottom-color:#D53A3E;
border-left-width:1px;
border-left-color:#D53A3E;
width:600px;
}
#mensaje_error ul
{
padding-top:0px;
margin-top:2px;
padding-bottom:0px;
padding-right:20px;
margin-bottom:0px;
margin-left:8px;
margin-right:10px;
color:#D53A3E;
font-family:tahoma, verdana, arial, sans-serif;
font-size:13px;
font-weight:normal;
list-style-image:url(../imagenes/importante.gif);
}
#mensaje_error label
{
color:#D53A3E;
font-family:tahoma, verdana, arial, sans-serif;
font-size:12px;
font-weight:bold;
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
...
</body>
</html>
Validaciones
En el ejemplo anterior, hemos utilizado la validacin de tipo requirestring, que corresponde a la
verificacin de la presencia de una cadena de caracteres que no est vaca. Struts incluye varias
validaciones
estndar
para
facilitar
la
tarea:
required,
requiredstring,
int,
date,
expression,fieldexpression,
email, url, visitor, conversion, stringlenght o regexp. A continuacin, presentaremos cada una de estas
validaciones con el fin de comprender su inters.
1. required
Esta validacin permite verificar que un campo no tenga el valor null. No obstante, las cadenas de
caracteres vacas no son nulas, estn presentes pero vacas, algo diferente.
2. requiredstring
Esta validacin permite comprobar que un campo no tenga el valor null y no est vaco. Esta
validacin contiene un parmetro denominado trim que corresponde a los espacios situados antes y
despus de los datos.
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>El campo identificador es obligatorio</message>
</field-validator>
3. stringlength
Esta validacin permite validar la longitud de un campo que no est vaco. As, podemos especificar
la longitud mnima y mxima de un campo de formulario. En nuestro ejemplo, el
campo identificadordebe tener un mnimo de 4 caracteres y un mximo de 10.
Cdigo: /ejemplo08/ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="identificador">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">10</param>
<message>El campo identificador debe tener entre 4 y 10
caracteres</message>
</field-validator>
</field>
<field name="contrasena">
<field-validator type="requiredstring">
<message>El campo contrasea es obligatorio</message>
</field-validator>
</field>
</validators>
4. int
Esta validacin permite verificar si un campo puede convertirse en nmero entero y si se utilizan los
parmetros min y max, adems de verificar que los datos se han introducido en el espacio indicado.
<field-validator type="int">
<param name="min">0</param>
<param name="max">20</param>
<message>El campo identificador debe estar comprendido entre 0
y 20</message>
</field-validator>
5. date
Esta validacin permite verificar si un campo es de tipo fecha y se encuentra en un espacio
determinado. En cambio, el formato de la fecha depende de la configuracin regional actual de la
aplicacin. La validacin siguiente permite verificar si la fecha introducida no es posterior al 31 de
diciembre de 2000.
<field-validator type="date">
<param name="max">31/12/2000</param>
<message>El campo no debe ser posterior al
31/12/2000</message>
</field-validator>
6. e-mail
Esta validacin permite verificar si un campo de tipo cadena de caracteres es una direccin de correo
electrnico vlida. Retomaremos nuestro ejemplo08 y agregaremos un campo e-mail asociado a su
validador.
Cdigo: ejemplo08.ClienteAccion.java
package ejemplo08;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private String identificador;
private String contrasena;
private String email;
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return SUCCESS;
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" id="contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:textfield name="email" id="email" label="Email"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar un cliente"/>
</s:form>
</div>
</body>
</html>
7. url
Esta validacin permite verificar si un campo de tipo cadena de caracteres es una URL correcta. Este
validador utiliza la clase java.net.URLpara verificar la sintaxis.
<field-validator type="url">
<message>El campo URL es incorrecto</message>
</field-validator>
8. regex
Esta validacin permite especificar una expresin regular para verificar un campo de formulario. Este
validador utiliza la clase java.lang.regexp.Patternpara verificar la sintaxis.
<field name="cliente.cdigoPostal">
<field-validator type="regex">
<param name="expression"><![CDATA[^\d{5}$]]></param>
<message>El campo debe ser un cdigo postal con el formato NNNNN
</field-validator>
</field>
9. fieldexpression y expression
Estas validaciones permiten utilizar expresiones OGNL para validar campos de formulario. Las
validaciones de tipo fieldexpression utilizan dos parmetros, min y max, que permiten limitar los
datos introducidos por el usuario.
Mostraremos esta validacin con la ayuda de dos nuevos campos para la gestin del salario del
cliente.
Cdigo: ejemplo08.ClienteAccion.java
package ejemplo08;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private String identificador;
private String contrasena;
private String email;
private int salarioMx=4000;
private int salarioCliente;
// getter y setter
// agregar los datos del cliente a la sesin
public String agregar()
{
// verificar los datos introducidos, en caso de error
volver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// sin errores
else
{
return SUCCESS;
}
}
}
Cdigo: /ejemplo08/ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="identificador">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">10</param>
<message>El campo identificador debe tener entre 4 y 10
caracteres</message>
</field-validator>
</field>
<field name="contrasena">
<field-validator type="requiredstring">
<message>El campo contrasea es obligatorio</message>
</field-validator>
</field>
<field name="email">
<field-validator type="email">
<message>El campo email es incorrecto</message>
</field-validator>
</field>
<field name="salarioCliente">
<field-validator type="fieldexpression">
<param name="fieldName">salarioMax</param>
<param name="expression">
salarioMax > salarioCliente
</param>
<message>El salario del cliente no puede ser
superior al salario mximo</message>
</field-validator>
</field>
</validators>
En la prueba, el salario mximo debe ser superior al salario del cliente (mnimo) para que no
se produzca un error.
Las validaciones de tipo expression utilizan dos parmetros, min y max, que permiten definir una
limitacin a nivel de los datos introducidos por el usuario pero al contrario que la
validacinfieldexpression, desencadenan un error de accin (action error) y no un error de campo
(field error). Durante la utilizacin de las validaciones de tipo expression, la etiqueta de
Struts <s:action/>permite mostrar los mensajes de error.
10. conversion
Esta validacin permite mostrar un error cuando una accin se encuentra con un error de
conversin. Retomaremos nuestro ejemplo para agregar un campo y gestionar la edad del cliente en
forma de nmero entero.
Cdigo: ejemplo08.ClienteAccion.java
package ejemplo08;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
Cdigo: /ejemplo08/ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="identificador">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">10</param>
<message>El campo identificador debe tener entre 4 y 10
caracteres</message>
</field-validator>
</field>
<field name="contrasena">
<field-validator type="requiredstring">
<message>El campo contrasea es obligatorio</message>
</field-validator>
</field>
<field name="edad">
<field-validator type="conversion">
<message>El campo edad debe ser un nmero entero</message>
</field-validator>
</field>
</validators>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" id="contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:textfield name="edad" id="edad" label="Edad"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar un cliente"/>
</s:form>
</div>
</body>
</html>
Struts muestra por defecto el mensaje Invalid field value for field "nombredelcampo". Este
mensaje se puede sobrecargar o modificar en el archivo de propiedades. Esta tcnica se
explica detalladamente en el captulo Gestin de tipos y conversiones.
11. visitor
Esta validacin permite mejorar el mantenimiento y reutilizar un sistema si deseamos compartir
reglas de validacin que sern usadas por diversos formularios. En nuestro ejemplo de formulario de
creacin de un cliente, utilizamos los campos identificador y contrasena. A continuacin se asignarn
reglas precisas a los campos para los formularios de creacin y modificacin.
Ahora, si deseamos aplicar esas mismas reglas al formulario de autenticacin, que tambin
contendr los campos identificador y contrasea, podemos utilizar este validador.
en
nuestro
archivo
ClienteAccion-
<field name="identificador">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">10</param>
<message>El campo identificador debe tener entre 4 y 10
caracteres</message>
</field-validator>
</field>
Ahora, si deseamos utilizar la misma validacin para un campo identificador de otra accin,
utilizaremos la definicin siguiente:
<field name="identificador">
<field-validator type="visitor">
<message>El campo identificador debe tener entre 4 y 10
caracteres</message>
</field-validator>
</field>
El vnculo entre las validaciones se realiza a travs del atributo namede las etiquetas <field/>.
Cdigo: struts.xml
Cdigo: ejemplo09.ClienteAccion.java
package ejemplo09;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo09.javabeans.Cliente;
import ejemplo09.javabeans.Profesion;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
// objeto JavaBean
private Cliente cliente;
// lista de profesiones
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public List<Profesion> getListaProfesiones() {
listaProfesiones.add(new Profesion(0,""));
listaProfesiones.add(new Profesion(1,
"Informtico"));
listaProfesiones.add(new Profesion(2,"Formador"));
listaProfesiones.add(new Profesion(3,
"Administrador"));
listaProfesiones.add(new Profesion(4,
"Programador"));
return listaProfesiones;
}
Cdigo: ejemplo09.Cliente.java
package ejemplo09.javabeans;
@SuppressWarnings("serial")
public class Cliente {
private String identificador;
private String contrasena;
private Profesion profesion;
public Cliente() {
}
// getter y setter
}
Cdigo: ejemplo09.Profesion.java
package ejemplo09.javabeans;
@SuppressWarnings("serial")
public class Profesion {
private int idProfesion;
private String nombre;
public Profesion() {
}
public Profesion(int idProfesion, String nombre) {
this.idProfesion=idProfesion;
this.nombre=nombre;
}
// getter y setter
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>
<s:property value="%{getText(cliente.agregar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label><s:property value="%{getText(error)}"/></label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="carta">
<ul>
<li><a href="Agregar_Cliente.action?
request_locale=es">Espaol</a></li>
<li><a href="Agregar_Cliente.action?
request_locale=en">Inglés</a></li>
</ul>
<br/>
<h3><s:property value="%{getText(cliente.agregar)}"/></h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="%{getText(cliente.identificador)}"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="%{getText(cliente.contrasena)}"
labelposition="top" cssClass="input"/>
<s:select name="cliente.profesion.idProfesion"
id="cliente.profesion.idProfesion" label="%
{getText(cliente.profesion.idProfesion)}" labelposition="top"
list="listaProfesiones" listKey="idProfesion" listValue="nombre"/>
<s:submit value="%{getText(cliente.agregar)}"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>
<s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%
{getText(cliente.identificador)}"/>: <s:property
value="cliente.identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/
>: <s:property value="cliente.contrasena"/><br/>
<s:property value="%
{getText(cliente.profesion.idProfesion)}"/>: <s:property
value="cliente.profesion.idProfesion"/><br/>
</p>
</div>
</body>
</html>
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
...
Cdigo: ejemplo09.ClienteAccion.java
package ejemplo09;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo09.javabeans.Cliente;
import ejemplo09.javabeans.Profesion;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
...
// mtodo requerido tras la verificacin de los
datos introducidos por parte del validador
public String verificar()
{
// verificar si el identificador es correcto
if(!this.cliente.getIdentificador().equals("jlafosse"))
{
addFieldError("cliente.identificador",
getText("campologin"));
addActionError(getText("erroraccion.login"));
return "input";
}
// sin errores
else
{
addActionMessage(getText("correcto"));
return SUCCESS;
}
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%getText(cliente.agregar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label><s:property value="%{getText(error)}"/></label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<!-- Mensaje de error durante acciones -->
<s:if test="errorMessages.size()>0">
<div id="mensaje_error">
<label><s:property value="%
{getText(erroraccion)}"/></label>
<ul><s:actionerror/></ul>
</div>
</s:if>
<div id="carta">
<ul>
<li><a href="Agregar_Cliente.action?
request_locale=es">Español</a></li>
<li><a href="Agregar_Cliente.action?
request_locale=en">Inglés</a></li>
</ul>
<br/>
<h3><s:property value="%{getText(cliente.agregar)}"/></h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="%{getText(cliente.identificador)}"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="%{getText(cliente.contrasena)}"
labelposition="top" cssClass="input"/>
<s:select name="cliente.profesion.idProfesion"
id="cliente.profesion.idProfesion" label="%
{getText(cliente.profesion.idProfesion)}" labelposition="top"
list="listaProfesiones" listKey="idProfesion" listValue="nombre"/>
<s:submit value="%{getText(cliente.agregar)}"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<!-- Mensaje de confirmacin -->
<s:if test="actionMessages.size()>0">
<div id="mensaje_informacion">
<ul><s:actionmessage/></ul>
</div>
</s:if>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%
{getText(cliente.identificador)}"/>: <s:property
value="cliente.identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/
>: <s:property value="cliente.contrasena"/><br/>
<s:property value="%
{getText(cliente.profesion.idProfesion)}"/>: <s:property
value="cliente.profesion.idProfesion"/><br/>
</p>
</div>
</body>
</html>
Cdigo: package.properties
cliente.agregar=Agregar un cliente
cliente.identificador=Identificador
cliente.contrasena=Contrasea
Escribir un validador
Los validadores que incluye Struts son suficientes para la mayora de las aplicaciones Web. No
obstante, en ocasiones es necesario desarrollar validadores especficos en funcin de una necesidad
particular. Los validadores de Struts implementan la interfaz Validator definida en el
paquetecom.opensymphony.xwork2.validator.
Struts utiliza tambin un interceptor para la gestin de las validaciones. El validador se ocupa de
cargar y ejecutar los validadores. El interceptor, que para recordrselo funciona como un filtro,
desencadena el mtodo setValidatorContext()para asignar el validador en el contexto adaptado. En
un segundo momento, el interceptor desencadena el mtodo validate(Object objetavalider)con el
objeto que debe validarse como parmetro. Este mtodo es responsable del tratamiento de la
validacin y es el que debemos sobrecargar para llevar a cabo nuestras validaciones.
del
<validator name="long"
class="com.opensymphony.xwork2.validator.validators.LongRangeField
Validator"/>
<validator name="short"
class="com.opensymphony.xwork2.validator.validators.ShortRangeField
Validator"/>
<validator name="double"
class="com.opensymphony.xwork2.validator.validators.DoubleRangeField
Validator"/>
<validator name="date"
class="com.opensymphony.xwork2.validator.validators.DateRangeField
Validator"/>
<validator name="expression"
class="com.opensymphony.xwork2.validator.validators.Expression
Validator"/>
<validator name="fieldexpression"
class="com.opensymphony.xwork2.validator.validators.FieldExpression
Validator"/>
<validator name="email"
class="com.opensymphony.xwork2.validator.validators.EmailValidator
"/>
<validator name="url"
class="com.opensymphony.xwork2.validator.validators.URLValidator"/
>
<validator name="visitor"
class="com.opensymphony.xwork2.validator.validators.VisitorField
Validator"/>
<validator name="conversion"
class="com.opensymphony.xwork2.validator.validators.ConversionError
FieldValidator"/>
<validator name="stringlength"
class="com.opensymphony.xwork2.validator.validators.StringLength
FieldValidator"/>
<validator name="regex"
class="com.opensymphony.xwork2.validator.validators.RegexField
Validator"/>
<validator name="conditionalvisitor"
class="com.opensymphony.xwork2.validator.validators.Conditional
VisitorFieldValidator"/>
</validators>
<!-- END SNIPPET: validators-default -->
3. Aplicacin
Vamos a utilizar un nuevo proyecto ejemplo10 basado en el ejemplo anterior para configurar un
validador. Definiremos un validador complejo para el identificador del cliente. Este identificador debe
tener una longitud mnima, una longitud mxima y no puede estar ya en uso. Para ello, el validador
verificar si el identificador no se encuentra en una lista. Por supuesto, en una aplicacin
profesional, esta lista podra encontrarse en una base de datos en vez de formar parte del cdigo
de manera permanente.
Empezaremos creando una clase denominada ValidadorIdentificador heredera de la
claseFieldValidatorSupport. Con el fin de conservar un sistema homogneo, vamos a crear un
paquete dedicado a los validadores llamado ejemplo10.validator.
El archivo de validacin validators.xml se encuentra en el directorio /WEB-INF/src de la aplicacin y
contiene la declaracin del validador. Ahora que hemos guardado el validador con el
nombrevalidadoridentificador, podemos utilizarlo en los archivos de validacin del mismo modo que
cualquier validador.
Cdigo: /WEB-INF/src/validators.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator Config 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">
<validators>
<validator name="validadoridentificador"
class="ejemplo10.validator.ValidadorIdentificador"/>
</validators>
La
clase
de
validacin
ValidadorIdentificador
contiene
dos
parmetros
denominados
minLength
ymaxLength
que
sern
transmitidos
p or
el
archivo
de
validacin
ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
con
las
etiquetas
<param
name="minLength"/> y <param name="maxLength"/>. A continuacin, el mtodo validate(Object
objetoavalidar)desencadenado en cada validacin, verificar si el identificador se encuentra en la
lista siguiente: jlafosse, asoto,crenato, amartn. Estos identificadores no podrn utilizarse para validar
el formulario.
Cdigo: /ejemplo10/ClienteAccion-ConfirmarAgregar_Cliente-validation.xml
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="cliente.identificador">
<field-validator type="requiredstring">
<message key="campoobligatorio"/>
</field-validator>
</field>
<field name="cliente.identificador">
<field-validator type="validadoridentificador">
<param name="minLength">4</param>
<param name="maxLength">20</param>
<message key="validadoridentificador"/>
</field-validator>
</field>
<field name="cliente.contrasena">
<field-validator type="requiredstring">
<message key="campoobligatorio"/>
</field-validator>
</field>
<field name="cliente.profesion.idProfesion">
<field-validator type="int">
<param name="min">1</param>
<message key="campoobligatorio"/>
</field-validator>
</field>
</validators>
Cdigo: ejemplo10.validator.ValidadorIdentificador
package ejemplo10.validator;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;
public class ValidadorIdentificador extends FieldValidatorSupport{
private int minLength=0;
private int maxLength=0;
public int getMinLength() {
return minLength;
}
public void setMinLength(int minLength) {
this.minLength = minLength;
}
public int getMaxLength() {
return maxLength;
}
public void setMaxLength(int maxLength) {
this.maxLength = maxLength;
}
// validacin a realizar en el campo
public void validate(Object objeto) throws ValidationException
{
String nombreCampo=this.getFieldName();
String
valorCampo=(String)this.getFieldValue(nombreCampo, objeto);
// verificar la longitud mnima del identificador
if(valorCampo.length() < this.minLength)
{
addFieldError(nombreCampo,objeto);
return;
}
if(valorCampo.length() > this.maxLength)
{
addFieldError(nombreCampo,objeto);
return;
}
// verificar que el identificador slo contiene estos
caracteres
if(isUtilizaIdentificador(valorCampo))
{
addFieldError(nombreCampo,objeto);
return;
}
}
// verificar si el identificador no est utilizado
public boolean isUtilizaIdentificador(String identificador)
{
List<String> listaIdentificadores=new
ArrayList<String>();
listaIdentificadores.add("jlafosse");
listaIdentificadores.add("asoto");
listaIdentificadores.add("crenato");
listaIdentificadores.add("amartn");
if(listaIdentificadores.contains(identificador))
{
System.out.println(identificador+" est en la
lista");
return true;
}
System.out.println(identificador+" no est en la
lista");
return false;
}
}
Cdigo: ejemplo10.ClienteAccion.java
package ejemplo10;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo10.javabeans.Cliente;
import ejemplo10.javabeans.Profesion;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
// objeto JavaBean
private Cliente cliente;
// lista de profesiones
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
// getter y setter
Cdigo: ejemplo10.package.properties
...
validadoridentificador=El campo ${getText(fieldName)} debe tener
entre ${getText(minLength)} y ${getText(maxLength)} caracteres y
no debe estar en uso
Cdigo: ejemplo10.package_en.properties
...
validadoridentificador=The field ${getText(fieldName)} must be
between ${getText(minLength)} and ${getText(maxLength)} characters
and must not be used
Cdigo: ejemplo10.ClienteAccion.java
package ejemplo10;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo10.javabeans.Cliente;
import ejemplo10.javabeans.Profesion;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
// objeto JavaBean
private Cliente cliente;
// lista de profesiones
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
// getter y setter
// mtodo para verificar que el identificador utilizado sea
adurn
public void validate()
{
if(cliente!=null)
{
if(!
cliente.getIdentificador().equals("adurn"))
{
addFieldError("identificadorusuario",getText("identificador
usuario"));
}
}
}
// mtodo para agregar un mensaje de confirmacin
public String verificar()
{
addActionMessage(getText("correcto"));
return SUCCESS;
}
}
Cdigo: ejemplo10.package.properties
...
identificadorusuario=El campo Identificador debe ser "adurn"
Cdigo: ejemplo10.package_en.properties
...
identificadorusuario=The field Login must be "adurn"
En resumen
En este captulo se explica la configuracin de validaciones en una aplicacin de Struts. Para ello, el
framework ofrece varias herramientas basadas en la elaboracin de archivos XML, la creacin de
validadores personales y en casos ms concretos, de la configuracin de validaciones de software en
la programacin. Asimismo, Struts propone combinar estas tres tcnicas en proyectos de gran
envergadura.
Presentacin
En los captulos anteriores, hemos estudiado cmo recuperar los datos introducidos en los formularios
y llevar a cabo validacin es ms o menos complejas.
Cada parmetro transmitido mediante el protocolo HTTP se considera como una cadena de caracteres
de tipo String. Por ejemplo, la introduccin de la edad del cliente en el formulario se verificar por
medio de los validadores como un entero, pero se recibir como una cadena de caracteres. Para
evitar las conversiones mltiples y costosas, con Struts es posible utilizar servicios simples de
conversiones.
Cdigo: ejemplo11.package.properties
invalid.fieldvalue.edad=El campo edad debe ser un nmero entero para
poder confirmar el formulario
Aplicacin
La aplicacin de las conversiones automticas de tipos necesita una configuracin previa. Para ello,
debemos crear un archivo que deber contener el nombre de la clase de accin con el
sufijoconversion. Para nuestro ejemplo de la clase ClienteAccion, se colocar el archivo de conversin
en el mismo directorio que la clase y llevar el nombre ClienteAccion-conversion.properties.
El archivo de configuracin debe encontrarse en el mismo directorio que la clase de accin.
Este archivo de propiedades contiende los nombres de los campos que deben asociarse a las
conversiones de tipos, as como la clase asociada a la verificacin del tipo.. Las clases de
administracin de tipos asociadas a una accin siempre se ejecutarn, incluso en el caso de utilizar
validaciones. Por lo tanto, nuestra clase de validacin del tipo para el campo fechaNacimiento se
ejecutar por el interceptor al mismo tiempo que el setter de la propiedad afectada.
Para aplicar la administracin de los tipos y las conversiones vamos a retomar nuestro
proyectoejemplo10 basado en la administracin de cuentas de cliente y aadiremos una
propiedadfechaNacimiento de tipo java.util.Date.
Cdigo: ejemplo12.conversor.FeachaNacimientoConversor
package ejemplo12.conversor;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import ognl.DefaultTypeConverter;
public class FechaNacimientoConversor extends
DefaultTypeConverter {
public Object convertValue(Map context, Object valor, Class
clase)
{
System.out.println("En el conversor");
// los datos introducidos son de tipo Date
if (clase == Date.class)
{
DateFormat dateFormat=new
SimpleDateFormat("dd/MM/yyyy");
dateFormat.setLenient(false);
try
{
String[] s=(String[])valor;
Date date=dateFormat.parse(s[0]);
System.out.println("en la fecha:
"+dateFormat.format(date));
return date;
}
catch(ParseException e)
{
//System.out.println("Error:" + e);
}
}
return null;
}
}
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.agregar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label><s:property value="%{getText(error)}"/></label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<!-- Mensaje de error durante las acciones -->
<s:if test="errorMessages.size()>0">
<div id="mensaje_error">
<label><s:property value="%
{getText(erroraccion)}"/></label>
<ul><s:actionerror/></ul>
</div>
</s:if>
<div id="carta">
<ul>
<li><a href="Agregar_Cliente.action?
request_locale=es">Español</a></li>
<li><a href="Agregar_Cliente.action?
request_locale=en">Ingeés</a></li>
</ul>
<br/>
<h3><s:property value="%{getText(cliente.agregar)}"/></h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="%{getText(cliente.identificador)}"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="%{getText(cliente.contrasena)}"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.fechaNacimiento"
id="cliente.fechaNacimiento" label="%
{getText(cliente.fechaNacimiento)}" labelposition="top"
cssClass="input" />
<s:select name="cliente.profesion.idProfesion"
id="cliente.profesion.idProfesion" label="%
{getText(cliente.profesion.idProfesion)}" labelposition="top"
list="listaProfesiones" listKey="idProfesion" listValue="nombre"/>
<s:submit value="%{getText(cliente.agregar)}"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/MostrarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:property value="%{getText(cliente.mostrar)}"/></title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<!-- Mensaje de confirmacin -->
<s:if test="actionMessages.size()>0">
<div id="mensaje_informacion">
<ul><s:actionmessage/></ul>
</div>
</s:if>
<div id="carta">
<p>
<h4><s:property value="%
{getText(cliente.mostrar)}"/></h4>
<s:property value="%
{getText(cliente.identificador)}"/>: <s:property
value="cliente.identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/
>: <s:property value="cliente.contrasena"/><br/>
<s:property value="%
{getText(cliente.profesion.idProfesion)}"/>: <s:property
value="cliente.profesion.idProfesion"/><br/>
<s:property value="%
{getText(cliente.fechaNacimiento)}"/>: <s:date
name="cliente.fechaNacimiento" format="dd/MM/yyyy"/>
</p>
</div>
</body>
</html>
En resumen
El protocolo HTTP utiliza nicamente cadenas de caracteres para transmitir informacin a las
aplicaciones. Esta restriccin de tipo hace necesario realizar conversiones (de cadenas de caracteres
a entero, fecha un objeto). Con Struts es posible una administracin automtica de las conversiones
para los tipos simples a partir de las cadenas de caracteres recibidas. Adems, podemos definir
nuestras propias clases de conversiones y de administracin de tipos a partir de una nomenclatura
precisa y eficaz.
Presentacin
La mayora de las aplicaciones Web utilizan una base de datos para garantizar la persistencia de la
informacin. Por ejemplo, los sitios comerciales almacenan los datos relativos a los clientes, artculos o
encargos. La mayora de las bases de datos comercializadas son de naturaleza cliente-servidor y
recurren al lenguaje SQL para manipular los datos que contienen. Java cuenta con una API para
trabajar con las bases de datos. Esta tecnologa, denominada JDBC (Java DataBase Connectivity), es
una biblioteca de interfaces y clases, utilizada para acceder a los SGBDR (Sistemas de Gestin de
Bases de Datos Relacionales). JDBC ofrece a los desarrolladores todas las herramientas necesarias
para permitir que los programas cliente se conecten con las bases de datos y les enven sus
consultas. Estas consultas estn escritas en lenguaje SQL.
Las aplicaciones actuales Java EE utilizan el modelo de diseo MVC (Modelo Vista Controlador). Hasta el
momento hemos estudiado la parte Vista con las pginas JSP y la parte Controlador con las clases de
accin, por lo que nos falta la parte de persistencia y el acceso a los datos con la capa Modelo.
Arquitectura MVC
Las aplicaciones se dividen en varias capas o niveles, con el fin de separar cada funcionalidad del
sistema. La comunicacin entre dos niveles (vista con controlador, controlador con modelo y
viceversa) se basa en la invocacin de los descriptores de acceso (getter y setter) y la transmisin de
parmetros adaptados. Struts se considera un framework de presentacin y se ocupa principalmente
de las capas Vista y Controlador. La configuracin de la capa Modelo es tarea del desarrollador.
As, la ltima capa ser responsable de la persistencia de los objetos utilizados o del acceso al SGBD
para transformar los datos objeto en datos relacionales. Esta tcnica se denomina mapping relacional
hacia objeto. La tcnica contraria, que consiste en transformar un objeto en datos en una base de
datos relacional, se conoce como mapping objeto hacia relacional, y se realiza mediante consultas
SQL.
Por convencin, las clases de accin llevan el sufijo Accin (por ejemplo: ClienteAccion.java), y estn
asociadas con sus clases modelo, que cuentan con el trmino Modelo como sufijo o prefijo (por
ejemplo: ClienteModelo.java o ModeloCliente.java). Estas dos clases manipulan un JavaBean
correspondiente al objeto en s (sus atributos y mtodos) (por ejemplo: Cliente.java). Las clases de
accin se consideran el controlador y llevan a cabo los tratamientos importantes de una aplicacin. La
clase modelo realiza todos los tratamientos de acceso o de modificacin de datos (por ejemplo:
consultar, agregar, modificar, eliminar y realizar un listado).
Aplicacin
Podemos utilizar el modelo MVC con el ejemplo del formulario del cliente reducido al identificador y la
contrasea. La capa modelo ser realizada/simulada por una clase esttica que contiene una lista
dinmica de cuentas de cliente y podr ser actualizada.
El nuevo proyecto ejemplo13 utiliza la clase JavaBean Cliente para gestionar el identificador y la
contrasea. Esta clase se utiliza en la clase de accin ClienteAcciony en el modelo ClienteModelo. El
rbol del proyecto debe ser el siguiente:
class="ejemplo13.ClienteAccion" method="agregar">
<result name="input">/jsp/ListadoCliente.jsp</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
La clase de accin es simple y cuenta nicamente con sus descriptores de acceso y dos mtodos muy
tiles, listado() y agregar(). El mtodo listado() recupera la informacin presente en el modelo
para asignrsela a la coleccin que se mostrar en la vista a travs de su getter.
Cdigo: ejemplo13.ClienteAccion.java
package ejemplo13;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo13.javabeans.Cliente;
import ejemplo13.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private Cliente cliente;
private List<Cliente> listaClientes;
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public List<Cliente> getListaClientes() {
listaClientes=ClienteModelo.getListaClientes();
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {
this.listaClientes = listaClientes;
}
// devolver la lista de clientes tras la recuperacin
public String listado()
{
listaClientes=ClienteModelo.getListaClientes();
return SUCCESS;
}
// agregar el cliente al modelo
public String agregar()
{
ClienteModelo.agregar(this.cliente);
return SUCCESS;
}
}
La clase JavaBean Cliente, muy sencilla, est compuesta por sus atributos y sus descriptores de
acceso.
Cdigo: ejemplo13.javabeans.Cliente.java
package ejemplo13.javabeans;
@SuppressWarnings("serial")
public class Cliente {
private int idCliente;
private String identificador;
private String contrasena;
public Cliente() {
}
public Cliente(int idCliente,String identificador, String
contrasena){
this.idCliente=idCliente;
this.identificador=identificador;
this.contrasena=contrasena;
}
// descriptores de acceso de la clase
}
Por ltimo, el modelo permite devolver la lista de los clientes creados y agregar un nuevo cliente a la
lista. Esta etapa utiliza una lista esttica, pero en un proyecto avanzado debera sustituirse por una
base de datos.
Esta clase modelo utiliza una lista esttica que ser compartida por el conjunto de objetos
usuario.
Cdigo: ejemplo13.modelo.ClienteModelo.java
package ejemplo13.modelo;
import java.util.ArrayList;
import java.util.List;
import ejemplo13.javabeans.Cliente;
public class ClienteModelo {
private static List<Cliente> listaClientes;
private static int id=0;
static
{
listaClientes=new ArrayList<Cliente>();
listaClientes.add(new Cliente(id++, "jlafosse", "jerome"));
listaClientes.add(new Cliente(id++, "asoto", "amelia"));
listaClientes.add(new Cliente(id++, "amartn", "alejandro"));
listaClientes.add(new Cliente(id++, "palvarez", "pedro"));
}
// devolver la lista de clientes
public static List<Cliente> getListaClientes() {
return listaClientes;
}
public static void setListaClientes(List<Cliente>
listaClientes) {
ClienteModelo.listaClientes = listaClientes;
}
// agregar un cliente a la lista
public static void agregar(Cliente cliente) {
cliente.setIdCliente(id++);
listaClientes.add(cliente);
}
}
Puede acceder al proyecto en la direccin http://localhost:8080/ejemplo13/Listado_Cliente.action y
mostrar directamente, gracias al mtodo listado() de la clase de accin y a la
lnealistaClientes=ClienteModelo.getListaClientes(), la lista de los clientes presentes en el modelo. Ahora
podemos agregar una cuenta correcta y validar la creacin, a continuacin la lista se actualizar
automticamente.
El interceptor Preparable
El interceptor Preparable acude al mtodo prepare() cada vez que se ejecuta la clase de accin, si
este interceptor est asociado con la accin, por supuesto. Para ello, la clase de accin debe
implementar la interfaz com.opensymphony.xwork2.Preparable. Vamos a configurar un servicio completo
denominado ejemplo14 de realizacin de listado, creacin, modificacin y eliminacin de clientes a
partir de este interceptor.
</action>
<action name="Editar_Cliente"
class="ejemplo14.ClienteAccion" method="editar">
<result name="success">/jsp/EditarCliente.jsp</result>
</action>
<action name="Modificar_Cliente"
class="ejemplo14.ClienteAccion" method="modificar">
<result name="input">/jsp/EditarCliente.jsp</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Eliminar_Cliente"
class="ejemplo14.ClienteAccion" method="eliminar">
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
Existen cinco acciones declaradas que permiten efectuar tareas muy concretas:
Listado_Cliente.action: esta accin permite ejecutar el mtodo listado()de la clase de accin
para recuperar la lista completa de clientes a travs del modelo. Esta accin realiza una
redireccin hacia la vista ListaCliente.jsp para mostrar el formulario de creacin y la lista de
cuentas de clientes.
Agregar_Cliente.action: esta accin permite ejecutar el mtodo agregar()de la clase de accin
y agregar directamente el objeto cliente (informado por el interceptor params) al modelo.
Esta accin utiliza el archivo de validacin ClienteAccion-Agregar_Cliente-validacin.xml y realiza
una redireccin completa tras una creacin hacia la accin de realizacin del listado de
clientes.
Editar_Cliente.action: esta accin permite mostrar el formulario de edicin (modificacin) de una
cuenta
de
cliente.
Para
ello,
la
accin
utiliza
un
parmetro
denominado idClienteActualtransmitido al vnculo y que corresponde al id. del cliente que
desea editar.
Modificar_Cliente.action: esta accin permite modificar el objeto cliente a travs del modelo
utilizando los nuevos valores introducidos en el formulario. Al igual que el formulario de
creacin, esta accin utiliza un archivo de validacin de los datos introducidos
llamadoClienteAccion-Modificar_Cliente-validacin.xml. Podemos observar as la flexibilidad de
Struts, que permite utilizar validaciones diferentes dependiendo de la accin que deba
realizarse (creacin o modificacin). Por supuesto, si las validaciones son idnticas, podemos
utilizar unvisitor, como se ha explicado en el captulo Biblioteca de etiquetas de Struts,
dedicado a las validaciones. Esta accin realiza una redireccin completa a la accin de
creacin de una nueva cuenta y de realizacin del listado de clientes.
Eliminar_Cliente.action:
esta
accin
permite
eliminar
un
cliente
a
partir
del
parmetroidClienteActual, que corresponde al id. del cliente que desea eliminar. En caso de
eliminacin, esta accin redirige hacia la pgina de realizacin del listado.
Cdigo: ejemplo14.ClienteAccion.java
package ejemplo14;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import ejemplo14.javabeans.Cliente;
import ejemplo14.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport implements
Preparable, ModelDriven{
private Cliente cliente;
private List<Cliente> listaClientes;
private int idClienteActual;
public void prepare() throws Exception {
// en creacin, crear un nuevo objeto vaco
if(idClienteActual==0)
{
cliente=new Cliente();
}
// en modificacin, devolver la informacin del objeto
else
{
cliente=ClienteModelo.getCliente(idClienteActual);
}
}
public Object getModel() {
return cliente;
}
public int getIdClienteActual() {
return idClienteActual;
}
public void setIdClienteActual(int idClienteActual) {
this.idClienteActual = idClienteActual;
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public List<Cliente> getListaClientes() {
listaClientes=ClienteModelo.getListaClientes();
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {
this.listaClientes = listaClientes;
}
// devolver la lista de clientes tras la recuperacin
public String listado()
{
listaClientes=ClienteModelo.getListaClientes();
return SUCCESS;
}
// agregar el cliente al modelo
public String agregar()
{
ClienteModelo.agregar(cliente);
return SUCCESS;
}
Cdigo: ejemplo14.modelo.ClienteModelo.java
package ejemplo14.modelo;
import java.util.ArrayList;
import java.util.List;
import ejemplo14.javabeans.Cliente;
public class ClienteModelo {
private static List<Cliente> listaClientes;
private static int id=1;
static
{
listaClientes=new ArrayList<Cliente>();
listaClientes.add(new Cliente(id++, "jlafosse", "jerome"));
listaClientes.add(new Cliente(id++, "asoto", "amelia"));
listaClientes.add(new Cliente(id++, "amartin", "alejandro"));
listaClientes.add(new Cliente(id++, "palvarez", "pedro"));
}
// devolver la lista de clientes
public static List<Cliente> getListaClientes() {
return listaClientes;
}
public static void setListaClientes(List<Cliente>
listaClientes) {
ClienteModelo.listaClientes = listaClientes;
}
// agregar un cliente a la lista
public static void agregar(Cliente cliente) {
cliente.setIdCliente(id++);
listaClientes.add(cliente);
}
// eliminar un cliente de la lista
public static void eliminar(int idCliente) {
for(int i=0;i<listaClientes.size();i++)
{
Cliente c=listaClientes.get(i);
if(c.getIdCliente()==idCliente)
{
listaClientes.remove(c);
}
}
}
// modificar un cliente de la lista
public static void modificar(Cliente cliente) {
int idCliente=cliente.getIdCliente();
for(int i=0;i<listaClientes.size();i++)
{
Cliente c=listaClientes.get(i);
if(c.getIdCliente()==idCliente)
{
c.setIdentificador(cliente.getIdentificador());
c.setContrasena(cliente.getContrasena());
break;
}
}
}
// buscar un cliente en la lista
public static Cliente getCliente(int idCliente) {
for(int i=0;i<listaClientes.size();i++)
{
Cliente c=listaClientes.get(i);
if(c.getIdCliente()==idCliente)
{
return c;
}
}
return null;
}
}
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Lista de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="Agregar_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
Cdigo: /jsp/EditarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Editar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="carta">
<h3>Editar un cliente</h3>
<s:form method="post" action="Modificar_Cliente">
<s:hidden key="cliente.idCliente"/>
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<s:submit value="Modificar un cliente"/>
</s:form>
</div>
</body>
</html>
La administracin de los clientes ha terminado. Observamos que los servicios de visualizacin, adicin
y eliminacin funcionan correctamente, al contrario que el servicio de edicin. En realidad, al hacer clic
en el vnculo de edicin/modificacin, el formulario est vaco. Se trata de algo totalmente normal, el
mtodo prepare() de nuestra clase de accin, que permite buscar al cliente concernido
poridClienteActual no se ha ejecutado. Para ejecutar este mtodo automticamente cada vez que se
acuda a la accin, es necesario configurar el interceptor Preparable. La configuracin se realiza
incluyendo la lnea siguiente <interceptor-ref name="paramsPrepareParamsStack"/> en el archivo de
configuracin struts.xml.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
...
<action name="Editar_Cliente"
class="ejemplo14.ClienteAccion" method="editar">
<interceptor-ref
name="paramsPrepareParamsStack"/>
<result name="success">/jsp/EditarCliente.jsp</result>
</action>
...
</struts>
A partir de ahora, nuestra aplicacin ejemplo14 est totalmente operativa.
Cdigo: ejemplo15.modelos.DAO.java
package ejemplo15.modelos;
import java.sql.Connection;
public interface DAO {
// Definicin del mtodo a declarar en las clases
de los usuarios
public Connection getConnection();
}
La clase ModeloDAOpropone la implementacin del mtodo getConnection()a partir de un pool de
conexin (DataSource). Este mtodo devuelve una conexin SQL que puede ser utilizada por
cualquier clase secundaria del modelo. Los pools de conexiones (javax.sql.DataSource) permiten
declarar conexiones a partir del archivo XML de la aplicacin (web.xml) y gestionar ms fcilmente la
configuracin de la misma. Por tanto, la conexin con la fuente de datos se almacena en el contexto
de la aplicacin (ServletContext).
Cdigo: ejemplo15.modelos.ModeloDAO.java
package ejemplo15.modelos;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.ServletContext;
import javax.sql.DataSource;
import org.apache.struts2.ServletActionContext;
// Clase de conexin
public class ModeloDAO implements DAO
{
DataSource dataSource=null;
// Recuperar una conexin
public Connection getConnection()
{
ServletContext
servletContext=ServletActionContext.getServletContext();
if(this.dataSource==null)
{
dataSource=(DataSource)servletContext.getAttribute("dataSource");
}
Connection connection=null;
if(dataSource!=null)
{
try
{
connection=dataSource.getConnection();
}
catch(SQLException e)
{
System.out.println(e);
}
}
// devolver la conexin
return connection;
}
// Posicionar una dataSource
public void setConnection(DataSource dataSource)
{
this.dataSource=dataSource;
}
}
Para aplicar la conexin al SGBD, utilizaremos un escuchador (listener) activado al iniciar la aplicacin
y configurado en el archivo de gestin de la aplicacin web.xml. Tambin se declaran dos etiquetas
para la conexin al SGBD (<context-param/> y <resource-ref/>). Para ello, podemos utilizar la
clasejavax.servlet.ServletContextListener
y
los
mtodos contextInitialized() y contextDestroyed(). Esta clase se declara en un paquete
especfico con una clase esttica GestionBaseDeDatos.java que permite gestionar el cierre de las
conexiones.
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" 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">
<!-- Cargador de datasource -->
<listener>
<listenerclass>ejemplo15.cajaherramientas.ApplicationListener</listener-class>
</listener>
<!-- Parmetros globales -->
<context-param>
<param-name>dataSourceJNDI</param-name>
<param-value>java:/comp/env/jdbc_struts2_MySQL</paramvalue>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filterclass>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExec
uteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Informacin de conexin a la base de datos -->
<resource-ref>
<description>Conexin a la base de datos
MySQL</description>
<res-ref-name>jdbc_struts2_MySQL</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
Cdigo: ejemplo15.cajaherramientas.ApplicationListener.java
package ejemplo15.cajaherramientas;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
public class ApplicationListener implements
ServletContextListener{
Context context=null;
//funcin llamada durante la creacin del iniciador
public void contextInitialized(ServletContextEvent
servletContextEvent)
{
ServletContext
servletContext=servletContextEvent.getServletContext();
String
dataSourceJNDI=servletContext.getInitParameter("dataSourceJNDI");
try
{
context=new InitialContext();
DataSource
dataSource=(DataSource)context.lookup(dataSourceJNDI);
if(dataSource==null)
{
System.out.println("No hay DataSource
para el proyecto: ejemplo15");
}
else
{
System.out.println("DataSource:
ejemplo15 cargado!");
}
servletContext.setAttribute("dataSource",
dataSource);
}
catch(NamingException e)
{
throw new RuntimeException();
}
finally
{
try
{
//cerrar el contexto
if(context!=null)
{
context.close();
}
}
catch(Exception e)
{
System.out.println("Error en
initCtx!");
}
}
}
//funcin llamada durante la destruccin del iniciador
public void contextDestroyed(ServletContextEvent
servletContextEvent)
{
try
{
//cerrar el contexto
if(context!=null)
{
context.close();
}
}
catch(Exception e)
{
System.out.println("Error en
initCtx!");
}
}
}
Cdigo: ejemplo15.cajaherramientas.GestionBaseDeDatos.java
package ejemplo15.cajaherramientas;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class GestionBaseDeDatos
{
// Permite cerrar un resultset
public static void closeResulset(ResultSet resultado)
{
if(resultado!=null)
{
try
{
resultado.close();
}
catch(Exception e)
{
System.out.println("Error en el
cierre de la conexin de un resultset");
}
}
}
// Cierre de una consulta
public static void closeRequest(Statement consulta)
{
if(consulta!=null)
{
try
{
consulta.close();
}
catch(Exception e)
{
System.out.println("Error en el
cierre de una consulta");
}
}
}
// Cierre de una conexin
public static void closeConnection(Connection connection)
{
if(connection!=null)
{
try
{
connection.close();
}
catch(Exception e)
{
System.out.println("Error en el
cierre de una conexin");
}
}
}
}
La configuracin del pool de conexin casi ha finalizado, slo nos falta la declaracin del pool en el
archivo de configuracin del proyecto (/tomcat/conf/Catalina/localhost/ejemplo15.xml) o del servidor
(/tomcat/conf/Catalina/server.xml).
Cdigo: /tomcat/conf/Catalina/localhost/ejemplo15.xml
<Context path="/ejemplo15" reloadable="true"
docBase="C:\JAVA\PROJECTOWEB\ejemplo15"
workDir="C:\JAVA\PROJECTOWEB\ejemplo15\work">
<Resource name="jdbc_struts2_MySQL"
auth="Container"
type="javax.sql.DataSource"
username="root"
password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/struts2"
maxActive="20"
maxIdle="10"
validationQuery="SELECT 1"
/>
</Context>
La aplicacin de una conexin con un SGBD requiere la instalacin del piloto adecuado en el
directorio /Tomcat/lib o /WEB-INF/lib de la aplicacin. Para el proyecto, se utiliza el piloto
mysql-connector-java-3.1.11-bin.jar.
Para gestionar la persistencia de los datos vamos a crear la base de datos MySQL struts2 con una
tabla cliente y tres campos: idCliente, identificador y contrasea. La tabla cliente se completa con
varios almacenamientos para comprobar la aplicacin.
INSERT INTO `cliente` VALUES (1, jlafosse, jerome);
INSERT INTO `cliente` VALUES (2, asoto, amelia);
INSERT INTO `cliente` VALUES (3, amartn, alejandro);
INSERT INTO `cliente` VALUES (4, plvarez, pedro);
el
modelo
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public List<Cliente> getListaClientes() {
ModeloClienteDAO ModeloClienteDAO=new
modeloClienteDAO();
listaClientes=(ArrayList<Cliente>)modeloClienteDAO.getListaClientes();
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {
this.listaClientes = listaClientes;
}
// devolver la lista de clientes tras la recuperacin
public String listado()
{
ModeloClienteDAO modeloClienteDAO=new
ModeloClienteDAO();
listaClientes=(ArrayList<Cliente>)modeloClienteDAO.getListaClientes();
return SUCCESS;
}
// agregar el cliente al modelo
public String agregar()
{
ModeloClienteDAO modeloClienteDAO=new
ModeloClienteDAO();
ModeloClienteDAO.agregarCliente(cliente);
return SUCCESS;
}
// mostrar el formulario en edicin
public String editar()
{
return SUCCESS;
}
// modificar un cliente
public String modificar()
{
ModeloClienteDAO modeloClienteDAO=new
ModeloClienteDAO();
modeloClienteDAO.modificarCliente(cliente);
return SUCCESS;
}
// eliminar un cliente a partir del parmetro recibido llamado
idCliente
public String eliminar()
{
ModeloClienteDAO modeloClienteDAO=new
ModeloClienteDAO();
modeloClienteDAO.eliminarCliente(idClienteActual);
return SUCCESS;
}
}
Por ltimo, el modelo se basa en el esquema UML (Unified Modeling Language) anterior y propone las
caractersticas necesarias para la consulta, modificacin, eliminacin y adicin de clientes, as como el
mtodo de mapping relacional-objeto. Los mtodos de la clase corresponden a la definicin del del
pattern DAO modelo CRUD (Create, Request, Update, Delete) del pattern DAO con las funciones que
permiten realizar listados de clientes, crear un nuevo cliente (C), recuperar la coleccin (R),
modificarla (U) y por ltimo eliminarla (D).
Cdigo: ejemplo15.modelos.ModeloClienteDAO.java
package ejemplo15.modelos;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import ejemplo15.cajaherramientas.GestionBaseDeDatos;
import ejemplo15.javabeans.Cliente;
public class ModeloClienteDAO extends ModeloDAO{
// Variables
Connection conexion=null;
ResultSet resultado=null;
private static List<Cliente> listaClientes;
// devolver la lista de clientes
public List<Cliente> getListaClientes()
{
// Variables
PreparedStatement consulta=null;
Cliente cliente=null;
String consultaString=null;
listaClientes=new ArrayList<Cliente>();
try
{
// Apertura de una conexin
conexion=super.getConnection();
// consulta de lista de clientes
consultaString="SELECT * FROM cliente WHERE 1
ORDER BY idCliente";
consulta=conexion.prepareStatement(consultaString);
// Ejecucin de la consulta
resultado=consulta.executeQuery();
// Se almacena el resultado en una lista
if(resultado!=null)
{
while(resultado.next())
{
// Se efecta el mapping de
los atributos con los campos de la tabla SQL
cliente=mapperCliente(resultado);
// Se aade el objeto a la lista
de clientes
listaClientes.add((Cliente)cliente);
}
}
}
catch(Exception e)
{
System.out.println("Error en la consulta
de la clase ModeloClienteDAO funcin getListaClientes");
}
finally
{
try
{
// Cierre de la conexin
if(resultado!=null)
{
GestionBaseDeDatos.closeResulset(resultado);
}
if(consulta!=null)
{
GestionBaseDeDatos.closeRequest(consulta);
}
if(conexion!=null)
{
GestionBaseDeDatos.closeConnection(conexion);
}
}
catch(Exception ex)
{
System.out.println("Error en el
cierre de la conexion con la base de datos en la clase
ModeloClienteDAO funcin getListaClientes");
}
}
// Devolver la lista de clientes
return listaClientes;
}
// buscar un cliente en la base
public Cliente getCliente(int idCliente)
{
// Variables
PreparedStatement consulta=null;
Cliente cliente=null;
String consultaString=null;
try
{
// Apertura de una conexin
conexion=super.getConnection();
// Creacin de la consulta
consultaString = "SELECT * FROM cliente WHERE
idCliente=?";
// Se prepara la consulta
consulta=conexion.prepareStatement(consultaString);
consulta.setInt(1,idCliente);
// Ejecucin de la consulta
resultado=consulta.executeQuery();
// Se almacena el resultado en el objeto cliente
if(resultado!=null)
{
if(resultado.next())
{
// Se realiza el mapping de
los atributos con los campos de la tabla SQL
cliente=mapperCliente(resultado);
}
}
}
catch(Exception e)
{
cliente=null;
System.out.println("Error en la consulta
en la clase ModeloClienteDAO funcin getCliente");
}
finally
{
try
{
// Cierre de la conexin
if(resultado!=null)
{
GestionBaseDeDatos.closeResulset(resultado);
}
if(consulta!=null)
{
GestionBaseDeDatos.closeRequest(consulta);
}
if(conexion!=null)
{
GestionBaseDeDatos.closeConnection(conexion);
}
}
catch(Exception ex)
{
System.out.println("Error en el
cierre de la conexin con la base de datos en la clase
ModeloClienteDAO funcin getCliente");
}
}
// Devolver objeto cliente
return cliente;
}
// agregar un cliente a la base
public int agregarCliente(Cliente cliente)
{
// Variables
PreparedStatement consulta=null;
String consultaString=null;
int codigoError=0;
try
{
// Apertura de una conexin
conexion=super.getConnection();
// Creacin de la consulta
consultaString="INSERT INTO cliente
(identificador,contrasena) VALUES(?,?)";
// Preparacin de la consulta
consulta=conexion.prepareStatement(consultaString);
consulta.setString(1,
cliente.getIdentificador());
consulta.setString(2, cliente.getContrasena());
// Se vaca el cliente por seguridad
cliente=null;
// Ejecucin de la consulta
codigoError=consulta.executeUpdate();
}
catch(Exception e)
{
codigoError=0;
System.out.println("Error en la consulta
de la clase ModeloClienteDAO funcin agregarCliente");
}
finally
{
try
{
// Cierre de la conexin
if(resultado!=null)
{
GestionBaseDeDatos.closeResulset(resultado);
}
if(consulta!=null)
{
GestionBaseDeDatos.closeRequest(consulta);
}
if(conexion!=null)
{
GestionBaseDeDatos.closeConnection(conexion);
}
}
catch(Exception ex)
{
System.out.println("Error en el
cierre de la conexin con la base de datos en la clase
ModeloClienteDAO funcin agregarCliente");
}
}
// Devolver el cdigo de error
return codigoError;
}
consulta=conexion.prepareStatement(consultaString);
consulta.setString(1,
cliente.getIdentificador());
consulta.setString(2, cliente.getContrasena());
consulta.setInt(3, cliente.getIdCliente());
// Se vaca el cliente por seguridad
cliente=null;
// Ejecucin de la consulta
codigoError=consulta.executeUpdate();
}
catch(Exception e)
{
System.out.println("Error en la consulta
de la clase ModeloClienteDAO funcin modificarCliente");
}
finally
{
try
{
// Cierre de la conexin
if(resultado!=null)
{
GestionBaseDeDatos.closeResulset(resultado);
}
if(consulta!=null)
{
GestionBaseDeDatos.closeRequest(consulta);
}
if(conexion!=null)
{
GestionBaseDeDatos.closeConnection(conexion);
}
}
catch(Exception ex)
{
System.out.println("Error en el
cierre de la conexin con la base de datos en la clase
ModeloClienteDAO funcin modificarCliente");
}
}
// Devolver el cdigo de error
return codigoError;
}
{
cliente.setIdCliente(resultado.getInt("idCliente"));
}
if (resultado.getString("identificador")==null)
{
cliente.setIdentificador("");
}
else
{
cliente.setIdentificador(resultado.getString("Identificador"));
}
if (resultado.getString("contrasena")==null)
{
cliente.setContrasena("");
}
else
{
cliente.setContrasena(resultado.getString("contrasena"));
}
}
catch (Exception e)
{
//Si se produce un error durante el mapping
de atributos
cliente=null;
System.out.println("Error en el mapping de
atributos de un cliente de la clase ModeloClienteDAO, funcin
mapperCliente");
}
// Devolver objeto cliente
return cliente;
}
}
En resumen
En este captulo se ha presentado la configuracin de la persistencia de objetos Java a travs de una
base de datos relacional. El modelo de persistencia DAO se ha detallado mediante el ejemplo de
gestin de clientes. A partir de ahora, el proyecto dispone de todas las capas de una aplicacin
profesional Modelo Vista Controlador MVC y permite un mantenimiento y evolucin ms sencillos
mediante la divisin de los diferentes mdulos a travs de modelos de diseo adaptados.
Presentacin
La configuracin de la carga de archivos del cliente en el servidor a menudo es una operacin
compleja. Para facilitar esta tarea, Struts utiliza la librera commonsFileUpload del consorcio Jakarta. Sin
la utilizacin de bibliotecas adaptadas, el usuario debe hacer uso de los mtodos de gestin de flujo
(InputStreamy OutputStream). La configuracin de la carga de archivos requiere un formulario HTML
con la etiqueta <form/>. El atributo enctypede esta etiqueta debe estar posicionado enmultipart/formdata y el mtodo post debe utilizarse como parmetro. Esta declaracin permite indicar que el
formulario contiene datos multimedia.
<form action="miAccion" enctype="multipart/form-data"
method="post">
La etiqueta <form/>est asociada a un campo <input/> de tipo file. Este componente HTML permite
integrar un botn examinar que sirve para seleccionar el archivo que desea enviar al servidor.
<form action="miAccion" enctype="multipart/form-data"
method="post">
<input type="file" name="miarchivo"/>
</form>
La etiqueta <s:file/>
La configuracin de la carga de archivos en Struts requiere el uso del interceptor fileUpload. La carga
de archivos es progresiva. El formulario indica la accin a la que se debe acudir para la carga, as
como el archivo que se va a cargar. La accin de Struts declara un objeto de tipo java.io.File utilizado
para establecer el vnculo con el archivo enviado. Durante la utilizacin de una lista de archivos,
podemos declarar una coleccin de archivos e iterar esta lista para cada archivo.
<s:form action="uploadAction" enctype="multipart/form-data">
<s:file name="miArchivo" label="Archivo"/>
<s:submit/>
</s:form>
A continuacin, debemos crear una accin con las propiedades siguientes, as como los descriptores
de acceso respectivos:
private File miArchivo;
private String miArchivoFileName; // sintaxis nombredelarchivoFileName
private String miArchivoContentType; // sintaxis
nombredelarchivoContentType
El interceptor fileUpload
El interceptor fileUpload gestiona la carga del o los archivos de la consulta HTTP. Este interceptor
posee dos propiedades que permiten gestionar el tamao mximo del archivo que se va a enviar y los
tipos permitidos:
maximumSize: esta propiedad permite limitar el tamao (en bytes) del archivo que se va a
enviar. El tamao por defecto es 2 megabytes.
allowedTypes: esta propiedad permite precisar los tipos de archivo permitidos.
Para nuestra proyecto, vamos a gestionar la imagen del cliente (avatar) con un sistema de carga de
imgenes Web (gif, jpeg y png). Este proyecto ejemplo16 se desarrolla a partir de la
aplicacinejemplo13, que permite realizar un listado y agregar clientes. La declaracin de la gestin de
la carga se encuentra en el archivo struts.xml.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo16" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo16.ClienteAccin" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo16.ClienteAccin" method="agregar">
<interceptor-ref name="fileUpload">
<param name="maximumSize">102400</param>
<param
name="allowedTypes">imagen/gif,imagen/jpeg,imagen/png</param>
</interceptor-ref>
<interceptor-ref name="basicStack"/>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
El interceptor fileUpload puede declararse de forma independiente, pero en caso de utilizacin
de otros tipos de accin con en nuestro ejemplo (redireccin hacia otra accin), el interceptor
basicStack debe declararse tras fileUpload.
La accin Agregar_Cliente utiliza el interceptor para la gestin de la carga de archivos y declara un
tamao mximo de archivo de 100 kilobytes, as como las imgenes de tipo gif, jpeg y png permitidas.
Carga nica
Vamos a realizar una carga nica de archivo para la gestin de la imagen del cliente. La clase
JavaBean Cliente se modifica ligeramente para gestionar la imagen en forma de cadena de
caracteres que representa la ruta del directorio de almacenamiento.
Cdigo: ejemplo16.javabeans.Cliente.java
package ejemplo16.javabeans;
@SuppressWarnings("serial")
public class Cliente {
private int idCliente;
private String identificador;
private String contrasena;
private String imagen;
public Cliente() {
}
public Cliente(int idCliente,String identificador, String
contrasena, String imagen){
this.idCliente=idCliente;
this.identificador=identificador;
this.contrasena=contrasena;
this.imagen=imagen;
}
// descriptores de acceso
}
El cdigo de la clase de accin es simple y permite recuperar automticamente el archivo, copiarlo en
el directorio de imgenes de los clientes y aplicar el nombre de la imagen al objeto.
Cdigo: ejemplo16.ClienteAccion.java
package ejemplo16;
import java.io.File;
import java.util.List;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo16.javabeans.Cliente;
import ejemplo16.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private Cliente cliente;
private List<Cliente> listaClientes;
private File imagen;
private String imagenFileName;
private String imagenContentType;
public File getImagen() {
return imagen;
}
public void setImagen(File imagen) {
this.imagen = imagen;
}
return SUCCESS;
}
}
Por ltimo, la vista JSP tambin se modifica ligeramente para mostrar las imgenes de los clientes si
stas no son nulas.
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Lista de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los errores siguientes: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<!-- Mensaje de error durante las acciones -->
<s:if test="errorMessages.size()>0">
<div id="mensaje_error">
<label>Se han producido los siguientes errores de accin:
</label>
<ul><s:actionerror/></ul>
</div>
</s:if>
<!-- Mensaje de confirmacin -->
<s:if test="actionMessages.size()>0">
<div id="mensaje_informacion">
<ul><s:actionmessage/></ul>
</div>
</s:if>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="Agregar_Cliente"
enctype="multipart/form-data">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<s:file name="imagen" id="imagen" label="Imagen"
labelposition="top" cssClass="input"/>
<s:submit value="Agregar un cliente"/>
</s:form>
<table border="0" id="tabla" cellpadding="0"
cellspacing="0">
<tr><td><b>ID</b></td><td><b>Identificador</b></td><td><b>
Contrasea</b></td><td><b>Imagen</b></td></tr>
<s:iterator value="listaClientes" status="linea">
<s:if test="#linea.odd"><tr class="linea1"></s:if>
<s:if test="#linea.even"><tr class="linea2"></s:if>
<td><s:property value="idCliente"/></td>
<td><s:property value="identificador"/></td>
<td><s:property value="contrasena"/></td>
<s:if test="imagen!=null">
<td><img src="imagenesclientes/${imagen}" width="40"
height="40"/></td>
</s:if>
<s:else>
<td> </td>
</s:else>
</tr>
</s:iterator>
</table>
</div>
</body>
</html>
A partir de ahora podemos agregar un nuevo cliente y cargar su imagen desde el formulario.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo16" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo16.ClienteAccion" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo16.ClienteAccion" method="agregar">
<interceptor-ref name="fileUpload">
<param name="maximumSize">102400</param>
<param
name="allowedTypes">imagen/gif,imagen/jpeg,imagen/png</param>
</interceptor-ref>
<interceptor-ref name="validationWorkflowStack"/>
<result name="success"
type="redirectAction">Listado_Cliente</result>
<result name="input">/jsp/ListadoCliente.jsp</result>
</action>
</package>
</struts>
Ahora que el interceptor de gestin de las validaciones ya est operativo, vamos a enviar un archivo
demasiado pesado (tamao superior a 100 Kb) y de un tipo incorrecto (que no sea una imagen) a
propsito. Observamos que los errores gestionados por el interceptor de carga son de tipo
accin<s:actionerror/>. Los mensajes mostrados estn en ingls y se definen en el archivo strutsmessages.properties presente en el paquete org.apache.struts2 del archivo struts2-core-x.jar.
Carga mltiple
El interceptor fileUpload permite gestionar tambin diversos archivos del mismo formulario de una sola
vez. El principio consiste en crear las etiquetas <s:file/>necesarias con nombres idnticos.
<s:file name="imagen" id="imagen" label="Imagen1"
labelposition="top" cssClass="input"/>
<s:file name="imagen" id="imagen" label="Imagen 2"
labelposition="top" cssClass="input"/>
Los archivos se envan a la clase de accin en forma de tabla y de este modo podrn ser tratados en
consecuencia.
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private Cliente cliente;
private List<Cliente> listaClientes;
private File[] imagen;
private String[] imagenFileName;
private String[] imagenContentType;
...
// agregar el cliente al modelo
public String agregar() throws Exception
{
ServletActionContext.getServletContext();
String
directorioImagenesCliente=context.getRealPath("imagenesclientes");
for(int i=0;i<imagen.length;i++)
{
File almacenamientoImagen=new
File(directorioImagenesCliente,this.imagenFileName[i]);
this.imagen[i].renameTo(almacenamientoImagen);
}
...
return SUCCESS;
}
}
Carga en Ajax
La carga de archivos estudiada anteriormente es perfectamente funcional pero no permite visualizar
el estado de progreso del archivo enviado. Los archivos de gran tamao pueden tardar varios
minutos en cargarse, dejando al usuario a la espera sin informacin adicional. Java permite utilizar
libreras avanzadas que permiten cargar archivos en segundo plano con la tecnologa Ajax y hacer uso
del lenguaje JavaScript para informar al usuario sobre el estado de progreso.
Existen numerosos complementos o libreras Ajax Upload ms o menos especficos y funcionales.
Vamos a utilizar la librera Ajax File Upload Plug-in (AjaxFileUpload-x.jar) del proyecto Struts 2
http://www.davidjc.com/ajaxfileupload/demo!input.action
La aplicacin de este complemento de Struts 2 requiere la instalacin de las libreras siguientes en
elclasspath:
ajaxFileUpload-x.jar: esta librera permite gestionar la carga de archivos.
commons-fileupload-x.jar: esta librera desarrollada por el consorcio Jakarta, permite gestionar
la carga de archivos.
commons-io-x.jar: esta librera permite gestionar las entradas/salidas para las cargas.
json-lib-x.jar: esta librera se basa en JSON (JavaScript Object Notation), una librera JavaScript
que utiliza la tecnologa Ajax para el acceso a los datos.
Para aplicar la carga de archivos en modo asncrono y con una barra de progreso, crearemos un
nuevo proyecto, ejemplo17. A continuacin, debemos agregar dos etiquetas en nuestra vista usuaria,
(librera de etiquetas o taglib) para configurar las libreras necesarias y el formulario dinmico.
<%@ taglib uri="http://www.davidjc.com/taglibs" prefix="djc"%>
//Para incluir la librera de etiquetas
<djc:head /> //Etiqueta para incluir automticamente los archivos
JavaScript
Debemos precisar el nombre ajaxFileUploadForm en el id. y el parmetro onsubmit como false. A
continuacin, el nombre del archivo debe ser upload. El botn de validacin del
formulario <s:submit/>tiene que poseer el atributo onclickcon el mtodo Ajax que se debe ejecutar
y la barra de progreso, llamada fileuploadProgress.
Cdigo: /jsp/ImagenCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://www.davidjc.com/taglibs" prefix="djc" %>
<html>
<head>
<title>Agregar una imagen</title>
<style type="text/css">@import url(css/estilos.css);</style>
<djc:head />
</head>
<body>
<div id="carta">
<h3>Agregar una imagen</h3>
<s:form method="post" action="Uploader_Image"
enctype="multipart/form-data" id="ajaxFileUploadForm"
onsubmit="return false">
<s:file name="upload" id="upload" label="Imagen"
labelposition="top" cssClass="input" accept="*/*"/>
<s:submit value="Enviar la imagen"
labelposition="top" onclick="return
davidjc.AjaxFileUpload.initialise(undefined, undefined);"/>
</s:form>
<div id="fileuploadProgress">
<span id="uploadFilename">Iniciando,
por favor espere...</span>
<div id="progress-bar"><div id="progressbgrd"> </div></div>
<div id="progress-text"> </div>
<br/>
</div>
</div>
</body>
</html>
La declaracin de la accin tambin debe adaptarse. El interceptor empleado se
denominafileUploadStack y se utiliza un resultado de tipo httpheader para la informacin reenviada en
Ajax.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0
//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo17" namespace="/" extends="strutsdefault">
<default-action-ref name="Agregar_Imagen" />
<action name="Agregar_Imagen">
<result>/jsp/ImagenCliente.jsp</result>
</action>
<action name="Uploader_Image"
class="ejemplo17.ClienteAccion">
<interceptor-ref name="fileUploadStack">
</interceptor-ref>
<result name="success" type="httpheader">
<param name="status">200</param>
</result>
<interceptor-ref name="validationWorkflowStack"/>
<result name="success"
type="redirectAction">Agregar_Imagen</result>
</action>
</package>
</struts>
El cdigo de la clase de accin es simple, posee los diferentes atributos necesarios para la gestin de
la carga del archivo. Los mtodos getUpload(), getUploadContentType() y getUploadFileName() se
utilizan para realizar las acciones de carga.
Cdigo: ejemplo17.ClienteAccion.java
package ejemplo17;
import java.io.File;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.davidjc.ajaxfileupload.action.FileUpload;
import com.opensymphony.xwork2.Action;
@SuppressWarnings("serial")
public class ClienteAccion extends FileUpload {
private File imagen;
private String imagenFileName;
private String imagenContentType;
// agregar el cliente al modelo
public String execute()
{
this.imagen=this.getUpload();
this.imagenContentType=this.getUploadContentType();
this.imagenFileName=this.getUploadFileName();
// ubicar el nombre del archivo en el directorio
if(this.imagen!=null)
{
// copiar el archivo en el directorio de
la aplicacin imagenesclientes
ServletContext
context=ServletActionContext.getServletContext();
String
directorioImagenesCliente=context.getRealPath("imagenesclientes");
File almacenamientoImagen=new
File(directorioImagenesCliente,this.imagenFileName);
this.imagen.renameTo(almacenamientoImagen);
}
return Action.SUCCESS;
}
}
Nuestra aplicacin ejemplo17 funciona y podemos comprobarla con un archivo de gran tamao con el
fin de visualizar la carga asncrona y la barra de progreso.
En resumen
En este captulo se ha detallado la configuracin de la gestin de cargas de archivos, del cliente en el
servidor con en framework de Struts. El primer ejemplo explica en detalle la aplicacin de esta tcnica
a partir de la imagen del formulario del cliente. El segundo prrafo le introduce a la carga mltiple y
por ltimo, la tercera parte le permite implementar la carga de archivos asncronos con las tecnologas
JavaScript y Ajax.
Presentacin
La aplicacin de la descarga de archivos desde el servidor al cliente se ofrece de manera estndar con
el framework de Struts. El envo dinmico de archivos se utiliza, por ejemplo, en las fichas de
productos o para la gestin del CV de los clientes. La descarga de archivos funciona en forma de flujo
y por ello puede reenviar un vdeo o imagen directamente al navegador del cliente. Esta tcnica
tambin se utiliza en ocasiones para proteger el acceso a contenidos confidenciales y facilitar el flujo
tras autenticacin o descifrado.
El envo de un archivo desde el servidor al navegador del cliente se realiza utilizando el
atributoContent-Type en el encabezado HTTP, en funcin del tipo de archivo que deba enviarse. El
navegador
tambin
utiliza
los
atributos
Content-Disposition
y
el
parmetroattachment;filename=nombredelarchivo (a continuacin este nombre de archivo se utilizar
para guardar el contenido).
Desde el punto de vista de la programacin, el archivo se lee del lado servidor a travs de la
claseFileInputStreamy se enva al navegador con una instancia de la clase OutputStream.
El cdigo siguiente permite utilizar esta funcin en un Servlet Java EE:
FileInputStream flectura=new FileInputStream(archivo);
BufferedInputStream blectura=new BufferedInputStream(flectura);
byte[] bytes=new byte[blectura.available()];
response.setContentType(contentType);
OutputStream salida=response.getOutputStream();
blectura.read(bytes);
salida.write(bytes);
Resultado stream
Struts propone utilizar el resultado de tipo stream para la descarga de archivos. Durante la
implementacin del resultado de tipo stream, ya no es obligatorio el uso de una pgina JSP para el
resultado, ya que el flujo se enva directamente al navegador.
Vamos a proponer un nuevo proyecto, ejemplo18, que nos permitir descargar la imagen del cliente
mostrndola directamente en el navegador en forma de flujo o descargndola directamente de
manera forzada. Esta diferencia se establece a travs del parmetro Content-Type del encabezado. El
parmetro Content-Type imagen/png obliga al navegador a mostrar la imagen y el parmetro ContentType aplicacin/byte-stream fuerza la descarga de la misma.
El resultado de tipo stream de Struts dispone de los siguientes parmetros:
inputName: este parmetro permite precisar la funcin de la clase de accin que va a devolver
el objeto de tipo InputStream. Por tanto, la clase de accin deber declarar el setter asociado.
bufferSize: este parmetro permite precisar el tamao del bfer utilizado para leer los datos
que deben enviarse.
contentType: este parmetro permite precisar el tipo de respuesta que debe reenviarse al
encabezado del paquete HTTP.
contentLength: este parmetro permite precisar el tamao de la respuesta reenviada.
contentDisposition: este parmetro permite precisar el nombre del objeto que se mostrar en
el navegador o se guardar en el disco del cliente.
Para nuestro proyecto ejemplo18, presentaremos el archivo struts.xml con las declaraciones de las
dos acciones y los parmetros asociados. Las dos acciones son prcticamente iguales, con la
diferencia de que el parmetro contentType permite a la accin Mostrar_Imagen.action devolver la
imagen al navegador y a la accin Descargar_Imagen.action forzar el resultado en flujo de bytes y por
tanto la descarga.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="exemple18" namespace="/" extends="strutsdefault">
<default-action-ref name="Gestionar_Imagen" />
<action name="Gestionar_Imagen">
<result>/jsp/ImagenCliente.jsp</result>
</action>
<action name="Mostrar_Imagen"
class="ejemplo18.DescargarAccion">
<result name="success" type="stream">
<param name="inputName">inputStream</param>
<param name="contentType">imagen/png</param>
<param
name="contentDisposition">filename="elnombredemiimagen.png"</param>
<param name="bufferSize">2048</param>
</result>
</action>
<action name="Descargar_Imagen"
class="ejemplo18.DescargarAccion">
<result name="success" type="stream">
<param name="inputName">inputStream</param>
<param name="contentType">aplicacin/bytestream</param>
<param
name="contentDisposition">filename="elnombredemiimagen.png"</param>
<param name="bufferSize">2048</param>
</result>
</action>
</package>
</struts>
La clase de accin contiene el setter para la ruta del archivo que se va a manipular y el
mtodogetInputStream()asociado al parmetro inputName, que permite devolver el flujo.
Cdigo: ejemplo18.DescargarAccion.java
package ejemplo18;
import java.io.InputStream;
import javax.servlet.ServletContext;
import org.apache.struts2.util.ServletContextAware;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class DescargarAccion extends ActionSupport implements
ServletContextAware{
private String rutaArchivo;
private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext=servletContext;
}
public void setRutaArchivo(String rutaArchivo) {
this.rutaArchivo = rutaArchivo;
}
public InputStream getInputStream() throws Exception {
return servletContext.getResourceAsStream(rutaArchivo);
}
}
La vista JSP permite establecer los dos vnculos adaptados con la ruta del archivo que va a mostrarse
o descargarse como parmetro. La etiqueta <img/> de insercin de imagen permite mostrar el
contenido de la imagen de manera dinmica a partir de la accin. Este principio se utiliza
habitualmente para proteger contenidos.
Cdigo: /jsp/ImagenCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Descargar una imagen</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Descargar una imagen</h3>
<a href="Mostrar_Imagen.action?
rutaArchivo=/imagenesclientes/imagen.png">
Mostrar la imagen
</a>
<a href="Descargar_Imagen.action?
rutaArchivo=/imagenesclientes/imagen.png">
Descargar la imagen
</a>
<br/>
<img src="Mostrar_Imagen.action?
rutaArchivo=/imagenesclientes/imagen.png" align="bottom"/>
</div>
</body>
</html>
Cdigo: ejemplo19.DescargarAccion.java
package ejemplo19;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.servlet.ServletContext;
import org.apache.struts2.dispatcher.StreamResult;
import org.apache.struts2.util.ServletContextAware;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.ActionContext;
@SuppressWarnings("serial")
public class DescargarAccion extends ActionSupport implements
ServletContextAware{
private String rutaArchivo;
private String directorioArchivo;
private ServletContext servletContext;
public void setServletContext(ServletContext
servletContext) {
this.servletContext=servletContext;
}
public void setRutaArchivo(String rutaArchivo) {
this.rutaArchivo = rutaArchivo;
}
public void setDirectorioArchivo(String directorioArchivo)
{
this.directorioArchivo = directorioArchivo;
}
// Mostrar un archivo (contentType=text/plain)
public InputStream getInputStreamMostrar() throws Exception {
return
servletContext.getResourceAsStream(rutaArchivo);
}
// Descargar un archivo (contentType=aplicacin/bytestream)
public InputStream getInputStreamDescargar() throws
Exception {
String
directorio=servletContext.getRealPath(this.directorioArchivo);
File archivo=new
File(directorio,this.rutaArchivo);
if(archivo.exists())
{
Result
result=ActionContext.getContext().getActionInvocation().getResult(
);
if(result!=null && result instanceof StreamResult)
{
StreamResult stream=(StreamResult)result;
stream.setContentDisposition("filename="+archivo.getName());
stream.setContentType("aplicacin/bytestream");
}
try
{
return new FileInputStream(archivo);
}
catch(Exception e)
{
System.out.println(e);
}
}
return null;
}
}
Cdigo: /jsp/ArchivoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Descargar una imagen</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Descargar una imagen</h3>
<a href="Mostrar_Archivo.action?
rutaArchivo=/css/estilos.css">
Código fuente: hoja css
</a><br/>
<a href="Descargar_Archivo.action?
directorioArchivo=/css&rutaArchivo=estilos.css">
Descargar: hoja css
</a><br/>
<a href="Mostrar_Archivo.action?rutaArchivo=/WEBINF/src/ejemplo19/DescargarAccion.java">
Cdigo fuente: DescargarAccion.java
</a><br/>
<a href="Descargar_Archivo.action?directorioArchivo=/WEBINF/src/ejemplo19/&rutaArchivo=DescargarAccion.java">
Descargar: DescargarAccion.java
</a>
</div>
</body>
</html>
En resumen
En este captulo se ha presentado la aplicacin de la gestin de las descargas de archivos desde el
servidor al cliente con la ayuda del framework de Struts. El primer ejemplo presenta la aplicacin del
resultado stream y la declaracin de las descargas en el archivo de configuracin struts.xml. Se han
detallado las dos formas de descarga, a saber, la descarga en forma de flujo para la visualizacin en
el navegador y la descarga forzada para el almacenamiento en el disco duro. El ltimo ejemplo, ms
avanzado, permite gestionar los dos tipos de descarga de forma dinmica y genrica con la ayuda de
parmetros y tratamientos de la clase de accin.
Presentacin
La carga o visualizacin de una pgina durante un procesamiento complejo puede incluso llevar varios
minutos. Por lo tanto, en ocasiones es necesario mostrar el progreso de la carga, pero esta no es una
tarea sencilla de programacin de Internet. El framework ofrece para ello un interceptor dedicado que
simula la carga y el proceso de los procesamientos.
El funcionamiento del interceptor execandWait se basa en una sesin y unos parmetros actualizados
automticamente para informar sobre el progreso del procesamiento. El principio reposa sobre un
proceso o thread que se ejecuta en segundo plano y devuelve informacin al usuario en la accin
actual para informarle sobre el tiempo restante para que finalice totalmente la ejecucin.
La aplicacin de la carga de pginas se realiza siguiendo los siguientes puntos:
Definicin de la etiqueta HTML <meta/>que permite actualizar la pgina.
Definicin de la accin con el interceptor execAndWait en el archivo struts.xml.
Definicin del mtodo de accin y de la funcin getComplete().
La etiqueta <meta/>permite recargar la pgina actual (u otra pgina) cada x segundos. Por ejemplo,
es habitual establecer que se actualice la pgina cada segundo.
<meta http-equiv="refresh"
content="1;url=/ejemplo20/Carga.action"/>
El interceptor execAndWait contiene tres parmetros para gestionar la ejecucin. El
parmetrothreadPriority permite asignar la prioridad al thread. El parmetro delay determina el nmero
de milisegundos de espera antes de enviar el resultado al cliente (el valor por defecto es 0). Por
ltimo, el parmetro delaySleepInterval especificar el nmero de milisegundos del thread principal que
efecta la verificacin al final de la ejecucin. El mtodo getComplete()es obligatorio cuando se aplica
el interceptor execAndWait y se gestiona el progreso de carga, permite devolver un entero que
representa el progreso del proceso.
Aplicacin
El proyecto ejemplo20 permite visualizar el progreso de la carga de la pgina a partir de una accin
simple que efecta una espera simulando un procesamiento largo. El resultado se reenva a la
pgina/jsp/Cargador.jsp cada segundo si el parmetro delay se ha establecido en 1000 milisegundos.
La etiqueta <meta/>vuelve a cargar la accin en segundo plano.
El resultado llamado wait permite especificar la pgina que se mostrar durante la espera. El
resultado ejecutado en caso de xito se determina mediante el tributo name.
El servicio de espera es funcional, pero muestra una barra de espera sin informacin sobre el tiempo
de procesamiento, se utiliza este principio para informar al usuario, pero en ningn caso para mostrar
el progreso del procesamiento efectuado.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo20" namespace="/" extends="strutsdefault">
<default-action-ref name="Cargador" />
<action name="Cargador" class="ejemplo20.CargadorAccion">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="execAndWait">
<param name="delay">1000</param>
</interceptor-ref>
<result name="wait">/jsp/Cargador.jsp</result>
<result name="success">/jsp/Completado.jsp</result>
</action>
</package>
</struts>
Cdigo: ejemplo20.Cargador.action
package ejemplo20;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class CargadorAccion extends ActionSupport {
public String execute() {
System.out.println("En la accin");
try
{
Thread.sleep(10000);
}
catch(Exception e)
{
System.out.println(e);
}
return SUCCESS;
}
}
Cdigo: /jsp/Cargador.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Cargador</title>
<meta http-equiv="refresh"
content="1;url=/ejemplo20/Cargador.action"/>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Cargador</h3>
<img src="imagenes/cargacircular.gif"
align="absmiddle"/> Por favor, espere mientras se carga la
página
</div>
</body>
</html>
Cdigo: /jsp/Completado.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Cargador completado</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Carga completa</h3>
Se ha completado el procesamiento
</div>
</body>
</html>
Progreso de la carga
Cdigo: /jsp/Cargador.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Cargador</title>
<meta http-equiv="refresh"
content="1;url=/ejemplo21/Cargador.action"/>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Cargador</h3>
Por favor, espere mientras se carga la página
<s:property value="complete"/>% completado
</div>
</body>
</html>
En resumen
En este captulo se ha explicado cmo gestionar la carga de pginas con la ayuda del
interceptorexecAndWait. El objetivo de este interceptor es hacer esperar al usuario y proporcionar
informacin sobre los procesamientos realizados en segundo plano. El servicio incluido por defecto
permite llamar a una accin en intervalos de tiempo regulares y ofrecer informacin acerca del
progreso gracias a un parmetro dedicado.
Presentacin
Struts se basa en una lista de inte rceptores encargados de prestar servicios especficos para la
gestin de los parmetros, la validacin de formularios o incluso la carga de archivos. Estos
interceptores utilizan el principio de los filtros para el ciclo de vida del proceso. La mayora de los
interceptores que incluye Struts son suficientes para las aplicaciones profesionales. Sin embargo, en
ocasiones es necesario desarrollar un interceptor para un servicio determinado.
Un interceptor no es ni ms ni menos que una clase Java que implementa la
interfazcom.opensymphony.xwork2.Interceptor. Esta interfaz ofrece las mismas funciones que un filtro,
es
decir las
firmas
de los
mtodos init(), destroy(), pero
aade
una
nueva
llamadaintercept(ActionInvocation invocation). Se llama al mtodo init()antes de que se haya
creado el interceptor para inicializar de los recursos y nicamente cuando la aplicacin est cargada.
Se llama al mtodo intercept() cada vez que una accin se enva al interceptor realizar el
procesamiento de las operaciones. Se llama al mtodo destroy()despus de que se haya ejecutado
el interceptor para liberar los recursos y nicamente cuando la aplicacin est descargada.
As, el framework llama al mtodo intercept()de cada interceptor declarado por la accin. El objeto
instancia de la clase ActionInvocationrepresenta el estado de la accin y le permite recuperar los
objetos Action y Result asociados. La clase AbstractInterceptorimplementa la interfaz Interceptor y
proporciona una implementacin estndar de los mtodos init()y destroy().
Escribir un interceptor
Para aplicar el desarrollo de la creacin de un interceptor, vamos a crear una herramienta de gestin
de autnticacin del cliente, para el acceso a las acciones del usuario. Los interceptores personales
se utilizan principalmente para la autnticacin, la conexin al SGBD (mediante un pool JNDI) o incluso
el difrado/descifrado de datos.
El proyecto ejemplo22 utiliza la clase ejemplo22.AutenticacionInterceptor.java como interceptor
encargado de gestionar la autenticacin para el acceso a la accin Proteger.action.
El archivo de configuracin struts.xml contiene la definicin del interceptor con la
etiqueta<interceptors/> y la referencia a la clase ejemplo22.AutenticacionInterceptor.java. Se
declaran cuatro acciones, la accion Inicializar.action permite mostrar la pgina JSP para hacer una lista
de los recursos.
La accin Proteger.action utiliza el interceptor declarado mediante la etiqueta <interceptor-ref/> y
proporciona dos resultados. Por ltimo, existen dos acciones dedicadas a la gestin de la
autenticacin la etapa de conexin con la colocacin de dos paramtros para la informacin de
conexin (identificadorPredeterminado y contrasenaPredeterminada) y la etapa de desconexin.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="exemple21" namespace="/" extends="strutsdefault">
<interceptors>
<interceptor name="autenticacionInterceptor"
class="ejemplo22.AutenticacionInterceptor">
<param name="identificadorPredeterminado">jlafosse</param>
<param name="contrasenaPredeterminada">jerome</param>
</interceptor>
</interceptors>
<default-action-ref name="Inicializar" />
<action name="Inicializar">
<result>/jsp/Lista.jsp</result>
</action>
<action name="Proteger">
<interceptor-ref name="createSession"/>
<interceptor-ref name="defaultStack"/>
<interceptor-ref
name="autenticacionInterceptor"/>
<result
name="autenticacion">/jsp/Autenticacion.jsp</result>
<result>/jsp/Recurso.jsp</result>
</action>
<action name="Conectar"
class="ejemplo22.AutenticarAccion" method="conectar">
<param name="identificadorPredeterminado">jlafosse</param>
<param name="contrasenaPredeterminada">jerome</param>
<result
name="input">/jsp/Autenticacion.jsp</result>
<result type="redirectAction">Proteger</result>
</action>
<action name="Desconectar"
class="ejemplo22.AutenticarAccion" method="desconectar">
<result type="redirectAction">Inicializar</result>
</action>
</package>
</struts>
La clase AutenticarAccion.java permite verificar la autenticacin de los clientes en funcin de los
parmetros predeterminados, declarados en el archivo de configuracin de la aplicacin struts.xml. En
caso de xito, se guardar un parmetro llamado autenticacion en caso contrario, se volver a mostrar
el formulario de autenticacin.
Cdigo: ejemplo22.AutenticarAccion.java
package ejemplo22;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class AutenticarAccion extends ActionSupport implements
SessionAware {
private String identificador;
private String contrasena;
private String identificadorPredeterminado;
private String contrasenaPredeterminada;
private Map<String,Object> sessionMap;
public void setSession(Map<String,Object> map)
{
this.sessionMap=map;
}
// descriptores de acceso
public String conectar() {
// verificar si el identificador y la contrasea son
correctos
if(identificador!=null && contrasena!=null)
{
if(identificador.equals(identificadorPredeterminado) &&
contrasena.equals(contrasenaPredeterminada))
{
// autenticacin correcta,
guardar el valor en la sesin
this.sessionMap.put("autenticacion",
true);
return SUCCESS;
}
}
return INPUT;
}
public String desconectar() {
// vaciar la sesin del usuario
this.sessionMap.clear();
return SUCCESS;
}
}
La clase del interceptor AutenticacionInterceptor.java utiliza estos tres mtodos y los parmetros de la
sesin para verificar si el usuario puede acceder a la accin y a las pginas JSP afectadas. Los
desarrolladores podrn, de este modo, aadir el sistema de autenticacin para la totalidad de un
servicio utilizando el siguiente cdigo:
<action name="action">
...
<interceptor-ref name="autenticacionInterceptor"/>
...
</action>
Cdigo: ejemplo22.AutenticacionInterceptor.java
package ejemplo22;
import java.util.Map;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
@SuppressWarnings("serial")
public class AutenticacionInterceptor extends
AbstractInterceptor{
// mtodo ejecutado antes de la accin
public void init()
{
System.out.println("Antes del mtodo de accin");
}
// mtodo del interceptor
public String intercept(ActionInvocation invocation) throws
Exception
{
System.out.println("En el mtodo del interceptor");
// recuperar los objetos de la sesin
Map<String,Object>
session=invocation.getInvocationContext().getSession();
if(session.get("autenticacion")==null)
{
// el usuario no se autentica
regresar a la pgina de autenticacin
return "autenticacion";
}
else
{
// verificar la autenticacin
boolean autenticacion=(Boolean)
(session.get("autenticacion"));
if(autenticacion)
{
// el usuario se han autenticado,
continuar la accin
return invocation.invoke();
}
else
{
// el usuario no se ha autenticado
regresar a la pgina de autenticacin
return "autenticacion";
}
}
}
// mtodo ejecutado despus de la accin
public void destroy()
{
System.out.println("Despues del mtodo de accin");
}
}
Las pginas JSP permite mostrar respectivamente un enlace a los recursos protegidos por
autenticacin (/jsp/Lista.jsp), un formulario de autenticacin (/jsp/Autenticacion.jsp) y los propios
recursos protegidos (/jsp/Rescurso.jsp).
Cdigo: /jsp/Lista.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Lista</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Lista</h3>
<a href="Proteger.action">Acceder al recurso</a>
</div>
</body>
</html>
Cdigo: /jsp/Autenticacion.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Autenticación</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Autenticación</h3>
<s:form method="post" action="Conectar">
<s:textfield name="identificador" id="identificador"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" id="contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:submit value="Confirmar"/>
</s:form>
</div>
</body>
</html>
Cdigo: /jsp/Recurso.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Recurso</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Recurso</h3>
Este recurso está protegido, está
autenticado;
<br/><a href="Desconectar.action">Desconectarse</a>
</div>
</body>
</html>
En resumen
En este captulo se ha presentado la escritura personalizada de interceptores mejorar los servicios
que ofrece Struts mediante la implementacin de la interfaz Interceptor o heredando de la clase
abstracta AbstractInterceptor. La programacin de interceptores permite, por lo tanto, simplificar las
clases, las estructuras y la divisin de servicios.
Presentacin
Struts
incluye
distintos
tipos
de
resultados
utilizados
a
lo
largo
del
libro
como Dispatcher, Stream oredirectAction. El framework tambin ofrece la posibilidad de crear
resultados personalizados en funcin de una necesidad.
Un resultado de Struts debe implementar la interfaz com.opensymphony.xwork2.Result. Esta interfaz
cuenta con un solo mtodo llamado execute(ActionInvocation invocation), que se activa cuando se
ejecuta el resultado o doExecute(String finalLocation, ActionInvocation invocation), que permite
especificar el destino final de la accin para el enrutamiento. El objetivo de la creacin de resultados
personalizados es el de escribir el cdigo que se ejecutar cuando se reenve el resultado. El principio
es prcticamente idntico al del desarrollo de los interceptores y consiste en heredar de la
claseorg.apache.struts2.dispatcher.StrutsResultSupport que
a
su
vez
implementa
la
interfaz Result.
Escribir un resultado
El proyecto ejemplo23 basado en la aplicacin ejemplo14 (gestin de cuentas de clientes como una
coleccin) proporciona un resultado que permite devolver la lista de las cuentas de usuario en forma
de flujo RSS en lugar de texto.
RSS (Really Simple Syndication) es un formato de intercambio de datos en formato XML. Las fuentes
son ahora distribuidores, reutilizables y diseadas para el flujo RSS o sindicacin. Los navegadores
actuales pueden leer directamente los archivos RSS, pero tambin podemos utilizar un software
especializado llamado el lector RSS. Un documento RSS es un archivo en formato XML que contiene la
etiqueta <rss/>y, al menos, un canal con la etiqueta <channel/>que proporcionen la informacin del
sitio. Este canal est compuesto de un determinado nmero de artculos y de etiquetas <item/>que
representan la informacin.
El archivo struts.xml contiene la definicin del resultado con la etiqueta <result-types/> y la
asociacin de la clase:
<result-types>
<result-type name="rss" class="ejemplo23.rssResult"/>
</result-types>
El archivo struts.xml tambin define una accin llamada ListadoRss_Cliente.action que contiene un
resultado de tipo rss para el procesamiento de informacin.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo23" namespace="/" extends="strutsdefault">
<result-types>
<result-type name="rss" class="ejemplo23.rssResult"/>
</result-types>
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo23.ClienteAccion" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="ListadoRSS_Cliente"
class="ejemplo23.ClienteAccion" method="listado">
<result type="rss">/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo23.ClienteAccion" method="agregar">
<result name="input">/jsp/ListadoCliente.jsp</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Editar_Cliente"
class="ejemplo23.ClienteAccion" method="editar">
<interceptor-ref
name="paramsPrepareParamsStack"/>
<result name="success">/jsp/EditarCliente.jsp</result>
</action>
<action name="Modificar_Cliente"
class="ejemplo23.ClienteAccion" method="modificar">
<result name="input">/jsp/EditarCliente.jsp</result>
<result name="success"
type="redirectAction">Listar_Cliente</result>
</action>
<action name="Eliminar_Cliente"
class="ejemplo23.ClienteAccion" method="eliminar">
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
El cdigo del interceptor permite recuperar la instancia de la respuesta HTTP y definir un resultado en
forma de contenido XML (setContentType). La lista dinmica de clientes, presente en el objeto de
invocacin y devuelta por la clase de accin, se recupera y se le da formato para respetar la
presentacin RSS (canal y artculos).
Cdigo: ejemplo23.rssResult.java
package ejemplo23;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import com.opensymphony.xwork2.ActionInvocation;
import ejemplo23.javabeans.Cliente;
@SuppressWarnings("serial")
public class rssResult extends StrutsResultSupport{
// mtodo ejecutado por el resultado
public void doExecute(String finalLocation,ActionInvocation
invocation) throws Exception
{
// recuperar el objeto response
HttpServletResponse
response=(HttpServletResponse)invocation.getInvocationContext().get
(StrutsStatics.HTTP_RESPONSE);
// recuperar la lista de clientes de la pila
de ejecucin
List<Cliente>
listaClientes=(List<Cliente>)invocation.getStack().findValue("lista
Clientes");
// tipo de respuesta
response.setContentType("application/xml");
PrintWriter out=response.getWriter();
out.println("<?xml version=\"1.0\" encoding=\"UTF8\"?>");
out.println("<rss version=\"2.0\">");
// crear un canal
out.println("<channel>");
out.println("<title>Flujo RSS de los clientes</title>");
out.println("<link>http://localhost:8080/ejemplo23/</link>");
out.println("<description>Flujo RSS de
los clientes</description>");
// crear los artculos de los clientes
for(int i=0;i<listaClientes.size();i++)
{
Cliente cliente=(Cliente)listaClientes.get(i);
out.println("<item>");
out.println("<title> Cliente:
"+cliente.getIdentificador()+"</title>");
out.println("<link>http://localhost:8080/ejemplo23/Editar_Cliente.
action?idClienteActual="+cliente.getIdCliente()+"</link>");
out.println("<description> Cliente:
"+cliente.getIdCliente()+" - "+cliente.getIdentificador()+" "+cliente.getContrasena()+"</description>");
out.println("</item>");
}
out.flush();
out.close();
}
}
Se aade un enlace a la accin ListadoRss_Cliente.action en la vista JSP /jsp/ListadoCliente.jsp,
responsable de la visualizacin de la informacin.
<a href="ListadoRSS_Cliente.action">Mostrar la lista en formato
RSS</a>
En resumen
En este captulo se ha explicado en detalle la escritura de resultados personalizados que se utilizarn
en las acciones de Struts. El ejemplo propuesto permite manipular los datos enviados, modificarlos y
devolver el resultado en diferentes formatos. Esta tcnica se utiliza en los resultados propuestos por
Struts para realizar redirecciones, presentaciones en XML o para devolver flujos de bytes.
Presentacin
Al navegar entre las diferentes pginas de una aplicacin de Internet, sucede a menudo que una
accin es llamada 2 veces consecutivas o que se ha aadido un artculo a su carrito de la compra dos
veces por error. Este doble clic o doble envo es ms problemtico cuando se encuentra en la fase de
pago (monto retirado dos veces en la cuenta del cliente) o de insercin de datos (creacin de dos
artculos en lugar de uno).
Esta validacin doble se produce cuando el procesamiento del lado del servidor es largo y el usuario
hace clic varias veces sobre el botn de enviar, o cuando el usuario actualiza la pgina actual que
realiza la accin. Struts ofrece una tcnica de gestin del doble envo que puede aplicarse a otros
idiomas. La tcnica consiste en utilizar un testigo (token) nico para cada usuario, guardado en el
servidor y con una copia insertada en cada formulario HTML.
Cuando se enva el formulario al servidor, la aplicacin, para enviado con el que se encuentra en el
servidor, realiza la accin en caso de equivalencia y a continuacin elimina el testigo del servidor. As,
cuando se produce un doble envo por error o de manera involuntaria, con el testigo del servidor
eliminado, la accin no se ejecutar.
Struts proporciona la etiqueta <s:token/>para gestionar el testigo del lado del cliente. Esta etiqueta
debe colocarse en una etiqueta <s:form/> y generar un campo de cach de tipo <hidden/> para
guardar el testigo. Para colocar un testigo en una accin es necesario declarar el
interceptor Token oTokenSession. El interceptor Token devuelve un resultado llamado invalid.token de
la lista de errores de accin <s:actionerror/>.
Para
adaptar
el
mensaje
al
idioma
deseado,
struts.messages.invalid.token en el archivo del idioma.
podemos
crear
la
variable
Para evitar este doble envo, utilizaremos el interceptor token y el nombre invalid.token en el resultado
para gestionar la visualizacin del testigo en la definicin de la accin Agregar_Cliente.action. Tambin
utilizamos el interceptor validationWorkflowStack para conservar la aplicacin de las validaciones del
formulario.
<action name="Agregar_Cliente" class="ejemplo24.ClienteAccion"
method="agregar">
<interceptor-ref name="token"/>
<interceptor-ref name="validationWorkflowStack"/>
<result name="invalid.token">/jsp/ListadoCliente.jsp</result>
<result name="input">/jsp/ListadoCliente.jsp</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
Se mostrar el error del testigo mediante la etiqueta <s:actionerror/>.
Para gestionar el testigo del interceptor, aadimos la etiqueta <s:token/> en el formulario de
registro/jsp/ListadoCliente.jsp.
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Listado de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los siguientes errores: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<!-- Mensaje de error de las acciones -->
<s:if test="errorMessages.size()>0">
<div id="mensaje_error">
<label>Se han producido los siguientes errores de accin:
</label>
<ul><s:actionerror/></ul>
</div>
</s:if>
<!-- Mensaje de confirmacin -->
<s:if test="actionMessages.size()>0">
<div id="mensaje_informacion">
<ul><s:actionmessage/></ul>
</div>
</s:if>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="Agregar_Cliente">
<s:token/>
...
</div>
</body>
</html>
El cdigo fuente generado por la etiqueta <s:token/>en el formulario HTML podra ser como el
siguiente:
<input
type="hidden"
name="struts.token"
value="LQV6RKHKOLETXKRZSR32B5VPGXIWTJA4" />
Podemos probar de nuevo la aplicacin realizando varias validaciones del formulario a continuacin.
Se mostrar el mensaje de error vinculado al testigo y se realizar una nica creacin.
Cdigo: struts-messages.properties
struts.messages.invalid.token=Ha enviado el formulario dos
veces. Slo se validar un nico envo.
En resumen
En este captulo se ha explicado en detalle cmo evitar los errores relacionados con el doble clic o el
doble envo cuando el usuario hace clic varias veces sobre el botn de confirmacin o actualizar la
pgina. La tcnica utilizada para evitar el doble envo consiste en utilizar un testigo o token, similar
del lado del cliente y del servidor y vlido para un nico intercambio. Para aplicar esta tcnica, Struts
ofrece
la
posibilidad
de
utilizar
la
etiqueta
<s:token/> en
las
vistas
y
los
interceptores token ytokenSession.
Presentacin
En las primeras versiones de Struts 2 se inclua el complemento Dojo, un framework JavaScript
OpenSource que ofreca un conjunto de etiquetas para gestionar componentes Ajax. Esta librera
contaba por ejemplo con la etiqueta <sx:head/> para insertar automticamente las libreras
JavaScript, la etiqueta <sx:div/> para gestionar las llamadas Ajax o la etiqueta <sx:submit/> para
publicar formularios utilizando la tecnologa Ajax (sin volver a cargar la pgina).
Desafortunadamente, la versin de Dojo presente en el complemento es una versin antigua y no es
posible actualizar esta librera. Asimismo, esta librera, basada en la versin 0.4 de Dojo, es
particularmente lenta. Los diseadores de Struts han precisado adems que el uso del complemento
Dojo para Struts superior a la versin 2.1 no es adecuado y que el mantenimiento no es compatible
con esta librera. Los desarrolladores de Struts trabajan actualmente con un complemento basado en
la librera JavaScript JQuery, pero han aclarado que con Struts los desarrolladores pueden utilizar
cualquier framework JavaScript.
En la actualidad, los protagonistas de Internet hablan a menudo de las tecnologas Web 2.0. Tras
este trmino se esconde un conjunto de tecnologas y herramientas basados en la ergonoma de las
interfaces. La expresin se ha propuesto para designar las nuevas tecnologas y la renovacin de
Internet. Este trmino fue inventado por un miembro de la sociedad OReilly para designar el
renacimiento de la Web. El fin es utilizar las tecnologas de Internet acercndose a interfaces
hombre/mquina atractivas del software instalado en los equipos personales.
La definicin exacta de Web 2.0 an no est muy clara, pero se sabe que un sitio Web 2.0 cuenta con
las caractersticas siguientes:
Se facilita la gestin de la informacin del sitio (realizar listados, consultar, modificar y
eliminar).
El sitio se puede utilizar en su totalidad con un navegador estndar.
El sitio utiliza los estndares de la tecnologa.
Desde el punto de vista tecnolgico, la infraestructura Web 2.0 es bastante compleja, ya que incluye
a la vez el o los servidores, la sindicacin del contenido, la mensajera y las aplicaciones.
en
un
directorio
de
la
aplicacin
(por
Declaracin de las inclusiones de libreras en las pginas JSP usuarias (por ejemplo: <script
src="jscripts/jquery.min.js" type="text/javascript"></script>).
Tecnologa Ajax
AJAX (Asynchronous JavaScript And XML) es una tcnica de desarrollo de Internet basada en el
lenguaje JavaScript que permite efectuar consultas HTTP sin necesidad de volver a cargar las pginas.
Esta tecnologa hace que los sitios sean ms interactivos y propone a los usuarios una ergonoma
similar a la de los programas informticos. Ajax se basa en el uso de tecnologas HTML y CSS, as
como en el de DOM (Document Object Model) para la presentacin de objetos, y por ltimo del objeto
JavaScript XMLHTTPRequest para la realizacin de consultas en segundo plano.
La tecnologa Ajax obliga sin embargo a replantear el desarrollo de las aplicaciones. En realidad, se
acude a los servicios en segundo plano y estos se devuelven en forma de variable.
En el caso de nuestro formulario de adicin de un cliente a partir de su identificador y contrasea,
debemos modificar la respuesta ya que el formulario no se volver a cargar. Slo debern reenviarse
y tratarse las respuestas de error y xito.
<action name="Indice_Cliente"
class="ejemplo25.ClienteAccion">
<result>/jsp/IndiceCliente.jsp</result>
</action>
<action name="Formulario_Cliente"
class="ejemplo25.ClienteAccion" method="editar">
<interceptor-ref name="paramsPrepareParamsStack"/>
<result>/jsp/FormularioCliente.jsp</result>
</action>
<action name="Listado_Cliente"
class="ejemplo25.ClienteAccion" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo25.ClienteAccion" method="agregar">
<result name="input">/jsp/RespuestaAjax.jsp</result>
<result name="success">/jsp/RespuestaAjax.jsp</result>
</action>
<action name="Modificar_Cliente"
class="ejemplo25.ClienteAccion" method="modificar">
<result name="input">/jsp/RespuestaAjax.jsp</result>
<result name="success">/jsp/RespuestaAjax.jsp</result>
</action>
<action name="Eliminar_Cliente"
class="ejemplo25.ClienteAccion" method="eliminar">
<result name="input">/jsp/RespuestaAjax.jsp</result>
<result name="success">/jsp/RespuestaAjax.jsp</result>
</action>
</package>
</struts>
La vista /IndiceCliente.jsp contiene la declaracin de los archivos JavaScript para el framework JQuery y
su complemento de gestin de formularios. Esta sencilla pgina contiene dos bloques utilizados para
mostrar el contenido del formulario y la lista de clientes.
Cdigo: /jsp/IndiceCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Lista de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
<!-- Utilizar la librera JavaScript JQuery -->
<script src="javascript/jquery.min.js"
type="text/javascript"></script>
<script src="javascript/jquery.form.js"
type="text/javascript"></script>
<script src="javascript/ejemplojform.js"
type="text/javascript"></script>
</head>
<body>
<!-- etiqueta utilizada para mostrar las respuestas Ajax -->
<div id="CuadroRespuestaAjax"></div>
<div id="carta">
<!-- etiqueta utilizada para agregar/modificar un cliente en
Ajax -->
<div id="CuadroFormularioCliente"></div>
Cdigo: /jsp/RespuestaAjax.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se han producido los siguientes errores: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<!-- Mensaje de error durante las acciones -->
<s:if test="errorMessages.size()>0">
<div id="mensaje_error">
<label>Se han producido los siguientes errores de accin:
</label>
<ul><s:actionerror/></ul>
</div>
</s:if>
<!-- Mensaje de confirmacin -->
<s:if test="actionMessages.size()>0">
<div id="mensaje_informacion">
<ul><s:actionmessage/></ul>
</div>
</s:if>
Ahora el tratamiento de la aplicacin se gestiona en el archivo JavaScript utilizado junto con el
framework
JQuery.
El
archivo
JavaScript
ejemplojform.js
utiliza
el
mtodo$(document).ready(function()), que permite realizar acciones una vez cargada la pgina.
Utilizamos este mtodo con el fin de cargar el formulario de registro o modificacin del cliente y
ejecutar la accin que permite realizar listados de los registros.
Este archivo contendr cuatro mtodos que corresponden a las acciones a llevar a cabo:
agregarCliente():
este
mtodo
permite
acudir
de
manera
asncrona
a
accinFormulario_Cliente.action
y
precisar
que
el
formulario
utilizar
complemento ajaxFormpara el envo de la informacin.
la
le
listadoCliente():
este
mtodo
permite
acudir
de
manera
asncrona
a
la
accinListado_Cliente.action y mostrar el contenido en el cuadro CuadroListadoCliente de la
pgina actual.
eliminarCliente (idClienteActual): este mtodo permite acudir de manera asncrona a la
accin Eliminar_Cliente.action y transmitir el cliente relevante. Cuando se ejecuta el resultado,
se desencadena de nuevo el mtodo de realizacin de listados para actualizar la lista.
modificarCliente (idClienteActual): este mtodo permite acudir de manera asncrona a la
accin Formulario_Cliente.action y mostrar el formulario que est siendo modificado asignando
el complemento ajaxForm para convertir el formulario en asncrono. Durante la modificacin, se
actualiza el listado de clientes y se vuelve a mostrar el formulario de adicin.
Cdigo: /javascript/ejemplojsform.js
// accin realizada al inicio
$(document).ready(function() {
// devolver la lista de clientes
listadoCliente();
// devolver el formulario de adicin de clientes
agregarCliente();
});
// devolver el formulario de clientes utilizando Ajax
function agregarCliente()
{
},
beforeSend : function()
{
$("#CuadroListadoCliente").html(<img
src="imagenes/cargacircularpequena.gif" align="absmiddle"
width="16" height="16" style="padding-right:5px;"/>Espere...);
},
success: function(html)
{
// incluir el resultado recibido en la etiqueta
de la pgina actual
$("#CuadroListadoCliente").html(html);
}
});
}
// eliminar el cliente
function eliminarCliente(idClienteActual()
{
//enviar los datos en POST
$.ajax(
{
type: "POST",
url: "Eliminar_Cliente.action",
dataType: "html",
data: "idClienteActual="+idClienteActual,
timeout : 8000,
error: function(){
alert(Desconexin del servidor);
},
beforeSend : function()
{
$("#CuadroListadoCliente").html(<img
src="imagenes/cargacircularpequena.gif" align="absmiddle"
width="16" height="16" style="padding-right:5px;"/>Espere...);
},
success: function(html)
{
// mostrar la respuesta ajax
$(#CuadroRespuestaAjax).html(html);
// eliminacin realizada correctamente
if($
(#CuadroRespuestaAjax).html().indexOf("mensaje_informacion")!=-1)
{
listadoCliente();
}
}
});
}
// modificar el cliente
function modificarCliente(idClienteActual()
{
//enviar los datos en POST
$.ajax(
{
type: "POST",
url: "Formulario_Cliente.action",
dataType: "html",
data: "idClienteActual="+idClienteActual,
timeout : 8000,
error: function(){
alert(Desconexin del servidor);
},
beforeSend : function()
{
$("#CuadroFormularioCliente").html(<img
src="imagenes/cargacircularpequena.gif" align="absmiddle"
width="16" height="16" style="padding-right:5px;"/>Espere...);
},
success: function(html)
{
// incluir el resultado recibido en la etiqueta
de la pgina actual
$("#CuadroFormularioCliente").html(html);
// utilizar el formulario ajaxForm
$(#Formulario_Cliente).ajaxForm({
// precisar el destino de la
respuesta (etiqueta)
target: #CuadroRespuestaAjax,
// funcin desencadenada tras el envo
del formulario
success: function() {
// mostrar las respuestas (errores,
errores de accin, confirmacin)
$(#CuadroRespuestaAjax).fadeIn(slow);
// modificacin realizada correctamente
if($
(#CuadroRespuestaAjax).html().indexOf("mensaje_informacion")!=-1)
{
// recuperar la nueva lista de cliente
listadoCliente();
// volver a mostrar el formulario
de creacin
agregarCliente();
}
}
});
}
});
}
Optimizaciones
El ejemplo anterior es ampliamente funcional. Ahora vamos a mejorarlo con el proyecto ejemplo26,
mediante servicios de tipo Web 2.0 con el lenguaje DHTML, las hojas de estilos y los
complementosJQuery.
Los optimizaciones utilizadas son las siguientes:
Utilizacin de botones dinmicos.
Gestin de cuadros (box) dinmicos para las confirmaciones y mensajes.
Utilizacin del complemento de gestin de componentes o widgets de tipo googletoolkit.
Utilizacin de un calendario dinmico para la gestin de fechas.
Utilizacin de un servicio de autocompletado para las bsquedas.
Gestin de clasificaciones dinmicas.
<a href="JavaScript:confirmarEliminarCliente(${idCliente})"><img
src="imagenes/eliminarcliente.png" alt="Eliminar" title="Eliminar"
border="0"/></a>
La gestin
de los
cuadros dinmicos
casi ha finalizado, slo falta
codificar la
funcinconfirmarEliminarCliente(idClienteActual)
en
el
archivo
JavaScript /javascript/ejemplojform.js para poder mostrar el cuadro de dilogo modal. Este cuadro se
muestra en el centro (position) y dispone de dos botones (Aceptar y Cancelar). El botn de
confirmacin desencadena el mtodoeliminarCliente(idClienteActual).
// funcin que permite mostrar una ventana de confirmacin
antes de eliminar
function confirmarEliminarCliente(idClienteActual)
{
$(function(){
// Configuracin del cuadro
$(#CuadroConfirmacion).dialog({
autoOpen: false,
width: 400,
modal: true,
position: middle,
buttons: {
"Aceptar": function() {
$
(this).dialog("close");
eliminarCliente(idClienteActual;
},
"Cancelar": function() {
$
(this).dialog("close");
}
}
});
});
// Mostrar el cuadro tras un clic en el ratn
$(#CuadroConfirmacion).dialog(open);
}
La pgina principal /jsp/IndiceCliente.jsp se modifica ligeramente para incluir el cuadro de dilogo
oculto al inicio de la aplicacin.
<!-- cuadro utilizado para las confirmaciones -->
<div id="CuadroConfirmacion" title="Gestin de clientes"
style="display:none">
<p><span class="ui-icon ui-icon-alert" style="float:left;
margin:0 7px 20px 0;"></span>¿Desea eliminar el cliente?</p>
</div>
i18n : {
editText : <img
src="./javascript/plugin/easywidgets/imagenes/edit.png" alt="Edit"
width="16" height="16" title="Editar"/>,
closeText : <img
src="./javascript/plugin/easywidgets/imagenes/close.png" alt="Close"
width="16" height="16" title="Cerrar"/>,
collapseText : <img
src="./javascript/plugin/easywidgets/imagenes/collapse.png"
alt="Close" width="16" height="16" title="Cerrar esta herramienta"/>,
cancelEditText : <img src="./javascript/plugin/easywidgets/
images/edit.png" alt="Edit" width="16" height="16" />,
extendText : <img
src="./javascript/plugin/easywidgets/imagenes/extend.png"
alt="Close" width="16" height="16" title="Abrir esta herramienta"/>,
confirmMsg : Cerrar esta herramienta?,
}
});
// Utilizacin del complemento cookie para memorizar las ubicaciones
$.fn.EasyWidgets({
behaviour : {
useCookies : true
}
});
});
El servicio est totalmente operativo y ahora podemos desplazar los cuadros, abrirlos, mostrar el
formulario de edicin, etc. Tambin podemos colocar los cuadros a nuestro gusto, cerrar el
navegador y volver a la pgina para comprobar que las ventanas siguen colocadas correctamente.
Cdigo: /ejemplo27/modelo.ClienteModelo.java
package ejemplo27.modelo;
import java.util.ArrayList;
import java.util.List;
import ejemplo27.javabeans.Cliente;
public class ClienteModelo {
private static List<Cliente> listaClientes;
private static int id=1;
static
{
listaClientes=new ArrayList<Cliente>();
listaClientes.add(new Cliente(id++, "jlafosse", "jerome",
"01/01/1968", "informtico"));
listaClientes.add(new Cliente(id++, "asoto", "amelia",
"02/02/2004", "contable"));
listaClientes.add(new Cliente(id++, "amartn", "alejandro",
"16/05/1954", "repartidor"));
listaClientes.add(new Cliente(id++, "plvarez", "pedro",
"04/05/1992", "msico"));
}
// modificar un cliente de la lista
public static void modificar(Cliente cliente) {
int idCliente=cliente.getIdCliente();
for(int i=0;i<listaClientes.size();i++)
{
Cliente c=listaClientes.get(i);
if(c.getIdCliente()==idCliente)
{
c.setIdentificador(cliente.getIdentificador());
c.setContrasena(cliente.getContrasena());
c.setFechadenacimiento(cliente.getFechadenacimiento());
c.setDescripcion(cliente.getDescripcion());
break;
}
}
}
...
}
El cdigo de la vista responsable de la visualizacin se modifica ligeramente para gestionar la fecha
de nacimiento y la descripcin del cliente.
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<table border="0" id="tabla" cellpadding="0" cellspacing="0">
<tr><td><b>ID</b></td><td><b>Identificador</b></td><td><b>
Contrasea</b></td><td><b>Fecha de
nacimiento</b></td><td><b>Descripción</b></td><td colspan="2"
align="center"><b>Gestin</b></td></tr>
<s:iterator value="listaClientes" status="linea">
<s:if test="#linea.odd"><tr class="linea1"></s:if>
<s:if test="#linea.even"><tr class="linea2"></s:if>
<td><s:property value="idCliente"/></td>
<td><s:property value="identificador"/></td>
<td><s:property value="contrasena"/></td>
<td><s:property value="fechadenacimiento"/></td>
<td><s:property value="descripcion"/></td>
<td align="center"><a href="JavaScript:modificarCliente($
{idCliente})"><img src="imagenes/editarcliente.png" alt="Editar"
title="Editar" border="0"/></a></td>
<td align="center"><a
href="JavaScript:confirmarEliminarCliente(${idCliente})"><img
src="imagenes/eliminarcliente.png" alt="Eliminar" title="Eliminar"
border="0"/></a></td>
</tr>
</s:iterator>
</table>
Ahora podemos pasar a la parte dinmica del formulario del cliente, modificando la pgina
JSP/jsp/FormularioCliente.jsp con el fin de agregar un calendario dinmico gestionado por el
complemento JQuery UI (http://jqueryui.com/demos/datepicker/#default). Para ello, debemos
agregar el cdigo JavaScript que permite aplicar el calendario a un objeto a partir de su atributo idy
configuraremos el idioma en espaol con la ayuda del cdigo JavaScript y de la librera jquery-uii18n.js. El tamao del calendario es gestionado por el estilo css ui-datepicker-div.
#ui-datepicker-div
{
font-size:8%;
}
Cdigo : /jsp/FormularioCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- formulario de adicin o modificacin -->
<s:if test="cliente.idCliente!=0">
<h3>Editar un cliente</h3>
<s:set id="accion">Modificar_Cliente.action</s:set>
</s:if>
<s:else>
<h3>Agregar un cliente</h3>
<s:set id="accion">Agregar_Cliente.action</s:set>
</s:else>
<s:form method="post" action="%{accion}" id="Formulario_Cliente"
name="Formulario_Cliente">
<s:hidden key="cliente.idCliente"/>
<s:textfield name="cliente.identificador" id="cliente.identificador"
<tr>
<td>Buscar: <s:textfield name="busqueda" id="busqueda"
label="Bsqueda" labelposition="left" cssClass="input"/></td>
<td>
<select name="tipobusqueda" id="tipobusqueda"
onchange="cambiarTipoBusqueda();" class="listadesplegable">
<option value="cliente.identificador">Identificador</option>
<option value="cliente.contrasena">Contrasea</option>
<option value="cliente.fechadenacimiento">Fecha de
nacimiento</option>
</select>
</td>
<td><input type="imagen" src="imagenes/buscar.gif"
align="absmiddle"
onclick="JavaScript:listadoCliente(busqueda)"/></td>
</tr>
</table>
</s:form>
<table border="0" id="tabla" cellpadding="0" cellspacing="0">
<tr><td><b>ID</b></td><td><b>Identificador</b></td><td><b>
Contrasea</b></td><td><b>Fecha de
nacimiento</b></td><td><b>Descripción</b></td><td colspan="2"
align="center"><b>Gestin</b></td></tr>
<s:iterator value="listaClientes" status="linea">
<s:if test="#linea.odd"><tr class="linea1"></s:if>
<s:if test="#linea.even"><tr class="linea2"></s:if>
<td><s:property value="idCliente"/></td>
<td><s:property value="identificador"/></td>
<td><s:property value="contrasena"/></td>
<td><s:property value="fechadenacimiento"/></td>
<td><s:property value="descripcion"/></td>
<td align="center"><a href="JavaScript:modificarCliente($
{idCliente})"><img src="imagenes/editarcliente.png" alt="Editar"
title="Editar" border="0"/></a></td>
<td align="center"><a
href="JavaScript:confirmarEliminarCliente(${idCliente})"><img
src="imagenes/eliminarcliente.png" alt="Eliminar" title="Eliminar"
border="0"/></a></td>
</tr>
</s:iterator>
</table>
<script type="text/javascript">
// Gestin de bsquedas
$(function() {
if($("#tipobusqueda") != null)
{
// Desencadenamiento de la accin para inicializar
el autocompletado
cambiarTipoBusqueda();
var busqueda=$(#busqueda).val();
if(busqueda== null || busqueda== )
{
$("#formulariobusqueda").hide();
}
else
{
$("#formulariobusqueda").show();
}
}
});
</script>
La pgina responsable de mostrar los trminos del autocompletado es muy simple y permite mostrar
cada respuesta con la ayuda de una coleccion.
Cdigo: /jsp/AutoComplete.jsp
<%@ page language="java" contentType="text/html; charset=ISO-88591" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<s:iterator value="lista" >
<s:property/>
</s:iterator>
El cdigo JavaScript de gestin del formulario se modifica con el fin de gestionar la visualizacin del
formulario de bsqueda y la confirmacin del envo. La funcin listadoCliente() gestiona a partir de
ahora la bsqueda para el autocompletado.
Cdigo: /javascript/ejemplo27.js
...
// iniciar la bsqueda por tipo de campo
function cambiarTipoBusqueda()
{
var atributo=$(#tipobusqueda).val();
if(atributo!=null)
{
atributo=jQuery.trim(atributo);
if(atributo!=)
{
url="AutoComplete.action?atributo="+atributo;
$("#busqueda").autocomplete(url, {
delay: 400,
width:400,
cacheLength:1,
matchSubset:false,
mustMatch : true,
minChars:1,
autoFill: false
});
}
}
}
// visualizacin del formulario de bsqueda
function mostrarBusqueda()
{
if($("#formulariobusqueda").is(":hidden"))
{
$("#formulariobusqueda").slideDown("fast");
}
else
{
$("#formulariobusqueda").slideUp("fast");
}
}
// devolver la lista de clientes utilizando Ajax
function listadoCliente()
{
//bsqueda del usuario
var busqueda=$("#busqueda").val();
var atributo=$(#tipobusqueda).val();
// no busqueda
if($("#busqueda").html()==null || $
("#tipobusqueda").html()==null)
{
busqueda="";
atributo="";
}
//enviar los datos en POST
$.ajax(
{
type: "POST",
url: "Listado_Cliente.action",
dataType: "html",
data:
"busqueda="+busqueda+"&atributo="+atributo+"&idClienteActual=0",
timeout : 8000,
error: function(){
alert(Desconexion del servidor);
},
beforeSend : function()
{
$("#CuadroListadoCliente").html(<img
src="imagenes/cargacircularpequena.gif" align="absmiddle"
width="16" height="16" style="padding-right:5px;"/>Espere...);
},
success: function(html)
{
// incluir el resultado recibido en la etiqueta de
la pgina actual
$("#CuadroListadoCliente").html(html);
if(busqueda!="")
{
// gestin dinmica de mensajes
//mensajeDinmico(false,true);
}
}
});
}
// realizar listado de todos los clientes
function listadoClienteInicializar()
{
// vaciar los campos y desencadenar la lista
$("#busqueda").val("");
$(#tipobusqueda).val("");
listadoCliente();
}
...
Por ltimo, el modelo ClienteModelo se modifica para gestionar las bsquedas del usuario en la lista
de clientes y se devuelven nicamente los objetos relevantes.
Cdigo: /ejemplo27/modelo/ClienteModelo.java
package ejemplo27.modelo;
import java.util.ArrayList;
import java.util.List;
import ejemplo27.javabeans.Cliente;
public class ClienteModelo {
private static List<Cliente> listaClientes;
private static int id=1;
static
{
listaClientes=new ArrayList<Cliente>();
// bsqueda en contrasea
if(atributo.equals("cliente.contrasena"))
{
if(c.getContrasena().contains(introduccion))
{
listaBusquedas.add(c.getContrasena().trim());
}
}
// buscar en la fecha
else
if(atributo.equals("cliente.fechadenacimiento"))
{
if(c.getFechadenacimiento().contains(introduccion))
{
listaBusquedas.add(c.getFechadenacimiento());
}
}
// bsqueda en el identificador
else
{
if(c.getIdentificador().contains(introduccion))
{
listaBusquedas.add(c.getIdentificador().trim());
}
}
}
return listaBusquedas;
}
}
La aplicacin est operativa y las bsquedas por autocompletado le permiten beneficiarse de un
proyecto ms ergonmico y funcional.
nacimiento</option>
</select>
</td>
<td><input type="imagen" src="imagenes/buscar.gif"
align="absmiddle"
onclick="JavaScript:listadoCliente(busqueda)"/></td>
</tr>
</table>
</s:form>
<br/><div align="left"><a
href="JavaScript:listadoClienteInicializar();"><img
src="imagenes/cliente.png" align="absmiddle" border="0"/> Listado de todos
los clientes</a></div>
<table border="0" id="tablaCliente" cellpadding="0"
cellspacing="0">
<thead><tr>
<th>ID</th>
<th>Identificador</th>
<th>Contraseña</th>
<th>Fecha de nacimiento</th>
<th>Descripcion</th>
<td colspan="2" align="center"><b>Gesti&oacue;n</b></td>
</tr>
</thead>
<tbody>
<s:iterator value="listaClientes" status="linea">
<s:if test="#linea.odd"><tr class="linea1"></s:if>
<s:if test="#linea.even"><tr class="linea2"></s:if>
<td><s:property value="idCliente"/></td>
<td><s:property value="identificador"/></td>
<td><s:property value="contrasena"/></td>
<td><s:property value="fechadenacimiento"/></td>
<td><s:property value="descripcion"/></td>
<td align="center"><a href="JavaScript:modificarCliente($
{idCliente})"><img src="imagenes/editarcliente.png" alt="Editar"
title="Editar" border="0"/></a></td>
<td align="center"><a
href="JavaScript:confirmarEliminarCliente(${idCliente})"><img
src="imagenes/eliminarcliente.png" alt="Eliminar" title="Eliminar"
border="0"/></a></td>
</tr>
</s:iterator>
</tbody>
</table>
<script type="text/javascript">
// Gestin de busquedas
$(function() {
if($("#tipobusqueda") != null)
{
// Desencadenamiento de la accin para inicializar
el autocompletado
cambiarTipoBusqueda();
var busqueda=$(#busqueda).val();
if(busqueda== null || busqueda== )
{
$("#formulariobusqueda").hide();
}
else
{
$("#formulariobusqueda").show();
}
}
// Gestin de clasificaciones
$("#tablaCliente").tablesorter();
});
</script>
En resumen
En este captulo se presenta la configuracin de servicios Web 2.0 con la ayuda del lenguaje
JavaScript y de la tecnologa Ajax. Se detallan los principales servicios utilizados en las aplicaciones
profesionales. Se explican los formularios XHTML sin recarga de pginas detalladamente, as como los
cuadros de dilogo y mensajes. El prrafo siguiente propone la utilizacin de un calendario dinmico
para la gestin de las fechas. Por ltimo, los dos ltimos prrafos le introducen a los mecanismos de
autocompletado y las clasificaciones dinmicas sin necesidad de recargar las pginas.
Velocity
Los motores de plantillas permiten mejorar el cdigo y separar la parte de procesamiento de datos de
la presentacin XHTML. Los motores de plantillas o de presentacin aportan soluciones para la
presentacin mediante etiquetas dedicadas. Un motor permite insertar contenido dinmico y
gestionar la divisin de las partes de las pginas. El objetivo de estos motores es separar el cdigo
de la presentacin. El motor Velocity (http://velocity.apache.org/) del consorcio Apache es un producto
OpenSource incluido con Struts 2 en el paquete struts2-core-2.x.x.jar.
Las aplicaciones utilizan por defecto las pginas JSP para la gestin de las vistas y de la presentacin.
Sin embargo, tambin es posible utilizar el motor Velocity (al igual que FreeMarker) para la
manipulacin de datos. El acceso a los datos y la comprensin de la herramienta son muy similares a
los del lenguaje JSP.
Velocity permite colocar las vistas en la aplicacin y empaquetarlas, al contrario que las pginas JSP.
adems, si queremos implementar una aplicacin como complemento de Struts , Velocity permite incluir
las vistas en el mismo paquete .jar.
Velocity utiliza el signo del dlar ($) para el acceso a los datos. Con Struts se incluye la definicin de
los resultados de tipo velocity en el archivo struts-default.xml y, por lo tanto, se pueden utilizar sin
problemas.
utilizadas
con
Para utilizar Velocity con todas sus funcionalidades, es necesario descargar las siguientes bibliotecas
en formato .jar e instalarlas en la carpeta /WEB-INF/lib de la aplicacin.
velocity-x.jar: la biblioteca principal del motor de plantillas.
velocity-x-dep.jar: la biblioteca con las dependencias.
velocity-tools-x.jar: las herramientas para el motor de plantillas.
commons-digester-x.jar: la biblioteca que permite leer archivos de configuracin en formato
XML.
Para ilustrar el uso del motor de plantillas Velocity, vamos a crear un nuevo proyecto con el
nombreejemplo28 a partir de la aplicacin ejemplo14. El archivo de gestin de la
aplicacin struts.xml se modifica para devolver resultados de tipo velocity.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo28" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo28.ClienteAccion" method="listado">
<result
type="velocity">/velocity/ListadoCliente.vm</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo28.ClienteAccion" method="agregar">
<result name="input"
type="velocity">/velocity/ListadoCliente.vm</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Editar_Cliente"
class="ejemplo28.ClienteAccion" method="editar">
<interceptor-ref
name="paramsPrepareParamsStack"/>
<result name="success"
type="velocity">/velocity/EditarCliente.jsp</result>
</action>
<action name="Modificar_Cliente"
class="ejemplo28.ClienteAccion" method="modificar">
<result name="input"
type="velocity">/velocity/EditarCliente.vm</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Eliminar_Cliente"
class="ejemplo28.ClienteAccion" method="eliminar">
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
Ya no se utilizarn las pginas JSP, en su lugar usaremos archivos de plantillas Velocity en
formato.vm, una sintaxis ms simple que permite gestionar la visualizacin de los mensajes de Struts
(errores de formularios, errores de acciones y confirmaciones).
Cdigo: /velocity/ListadoCliente.vm
<html>
<head>
<title>Listado de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
#sform ("action=Agregar_Cliente")
#stextfield ("name=cliente.identificador"
"id=cliente.identificador" "label=Identificador" "labelposition=top"
"cssClass=input")
#stextfield ("name=cliente.contrasena"
"id=cliente.contrasena" "label=Contrasea" "labelposition=top"
"cssClass=input")
#ssubmit ("value=Agregar un cliente")
#end
<table border="0" id="tabla" cellpadding="0"
cellspacing="0">
<tr><td><b>ID</b></td><td><b>Identificador</b></td><td><b>
Contrasea</b></td><td colspan="2"
align="center"><b>Gestion</b></td></tr>
#foreach( $name in $listaClientes )
<tr>
<td>$name.idCliente</td>
<td>$name.identificador</td>
<td>$name.contrasena</td>
<td align="center"><a href="Editar_Cliente.action?
idClienteActual=$name.idCliente"/><img
src="imagenes/editarcliente.png" alt="Editar" title="Editar"
border="0"/></a></td>
<td align="center"><a href="Eliminar_Cliente.action?
idClienteActual=$name.idCliente"/><img src="imagenes/eliminarcliente.png"
alt="Eliminar" title="Eliminar" border="0"/></a></td>
</tr>
#end
</table>
</div>
</body>
</html>
Cdigo: /velocity/EditarCliente.vm
<html>
<head>
<title>Editar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Editar un cliente</h3>
#sform ("action=Modificar_Cliente")
#stextfield ("name=cliente.identificador"
"id=cliente.identificador" "label=Identificador" "labelposition=top"
"cssClass=input")
#stextfield ("name=cliente.contrasena"
"id=cliente.contrasena" "label=Contrasea" "labelposition=top"
"cssClass=input")
#ssubmit ("value=Modificar un cliente")
#end
</div>
</body>
</html>
FreeMarker
FreeMarker (http://freemarker.org/) al igual que Velocity, es un motor de plantillas que puede
utilizarse con Struts. De hecho, la biblioteca de etiquetas facilitada por Struts est basada en este
motor. Para utilizar FreeMarker con Struts no es necesario disponer ninguna biblioteca adicional ya
que la herramienta est incluida con el framework y la biblioteca freemarker-x.jar. Al igual que Velocity,
FreeMarker permite empaquetar aplicaciones en un archivo con formato .jar.
FreeMarker ofrece los mismos objetos implcitos que los utilizados por Velocity. Las tags de
FreeMarker se pueden utilizar adems de las etiquetas de Struts y quien en la siguiente sintaxis:
<@s.nombre/>. Los archivos de FreeMarker tienen la extensin de formato .ftl y a los tipos de
resultados se les llama freemarker. Vamos a crear un nuevo proyecto con el nombre ejemplo29 a partir
del anterior utilizando FreeMarker como motor de plantillas en lugar de JSP o Velocity. Teniendo en
cuenta que desarrollamos con el modelo de diseo MVC, nicamente se cambiar la visualizacin y su
enrutamiento sin afectar a toda la aplicacin.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo29" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo29.ClienteAccion" method="listado">
<result
type="freemarker">/freemarker/ListadoCliente.ftl</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo29.ClienteAccion" method="agregar">
<result name="input"
type="freemarker">/freemarker/ListadoCliente.ftl</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Editar_Cliente"
class="ejemplo29.ClienteAccion" method="editar">
<interceptor-ref
name="paramsPrepareParamsStack"/>
<result name="success"
type="freemarker">/freemarker/EditarCliente.ftl</result>
</action>
<action name="Modificar_Cliente"
class="ejemplo29.ClienteAccion" method="modificar">
<result name="input"
type="freemarker">/freemarker/EditarCliente.ftl</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
<action name="Eliminar_Cliente"
class="ejemplo29.ClienteAccion" method="eliminar">
<result name="success"
type="redirectAction">Listado_Cliente</result>
</action>
</package>
</struts>
Cdigo: /freemarker/ListadoCliente.ftl
<html>
<head>
<title>Listado de clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<@s.form method="post" action="Agregar_Cliente">
<@s.hidden key="cliente.idCliente"/>
<@s.textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<@s.textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<@s.submit value="Agregar un cliente"/>
</@s.form>
<table border="0" id="tabla" cellpadding="0"
cellspacing="0">
<tr><td><b>ID</b></td><td><b>Identificador</b></td><td><b>
Contrasea</b></td><td colspan="2"
align="center"><b>Gestin</b></td></tr>
<@s.iterator value="listaClientes" status="linea">
<@s.if test="#linea.odd"><tr class="linea1"></@s.if>
<@s.if test="#linea.even"><tr class="linea2"></@s.if>
<td><@s.property value="idCliente"/></td>
<td><@s.property value="identificador"/></td>
<td><@s.property value="contrasena"/></td>
<td align="center"><a href="Editar_Cliente.action?
idClienteActual=${idCliente}"/><img src="imagenes/editarcliente.png"
alt="Editar" title="Editar" border="0"/></a></td>
<td align="center"><a href="Eliminar_Cliente.action?
idClienteActual=${idCliente}"/><img src="imagenes/eliminarcliente.png"
alt="Eliminar" title="Eliminar" border="0"/></a></td>
</tr>
</@s.iterator>
</table>
</div>
</body>
</html>
Cdigo: /freemarker/EditarCliente.ftl
<html>
<head>
<title>Editar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Editar un cliente</h3>
<@s.form method="post" action="Modificar_Cliente">
<@s.hidden key="cliente.idCliente"/>
<@s.textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<@s.textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<@s.submit value="Modificar un cliente"/>
</@s.form>
</div>
</body>
</html>
En resumen
Velocity es un motor de plantillas utilizado para la capa Vista del modelo MVC en lugar de JSP en una
aplicacin de Struts. Esta herramienta se ha explicado a partir de un ejemplo concreto. Struts tambin
incluye y utiliza otro motor de plantillas llamado FreeMarker para su biblioteca de etiquetas. Esta
biblioteca utilizada en una aplicacin web es totalmente empaquetable y convertible al formato .jar.
Presentacin
XML (eXtended Markup Language) deriva del lenguaje SGML (Standard Generalized Markup Language)
desarrollado en los aos 80. Se propuso una versin ms simple de este lenguaje para la
presentacin de documentos Web el HTML (HyperText Markup Language). XML utiliza la simplicidad del
HTML con la flexibilidad de SGML.
En un documento XML, la presentacin y se separa completamente de los datos. La informacin
(contenido) se separa de la apariencia (continente). Por lo tanto, se pueden ofrecer varios tipos de
salidas para un mismo archivo de datos (imagen, archivo HTML, archivo XML, archivo PDF...).
XSL (eXtensible Stylesheet Language) permite gestionar los estilos y el formato de un documento XML,
de igual forma que CSS lo hace para HTML.
Los documentos XML se utilizan para el intercambio de datos o la utilizacin de un estndar en la
estructuracin de la informacin. Los datos XML pueden ser, por lo tanto, manipulados por parte de
los clientes y del servidor. Un documento XML puede transformarse en otro documento XML (cambio
de nombre de las etiquetas, programacin...) o en un documento XHTML, de texto, PDF u otros.
El siguiente esquema presenta la tcnica que permite obtener un documento HTML/XHTML a partir de
un documento XML y de una hoja de estilo XSL y el motor XSLT. Los Datos estn completamente
separados de la Presentacin.
Utilizacin
Utilizaremos varios ejemplos para efectuar transformaciones de XML a XML, de XML a flujo RSS y de
XML a XHTML. Para ello, Struts proporciona un resultado de tipo XSLT con parmetros adicionales
utilizados por la hoja de estilo XSLT.
stylesheetLocation: ruta de la ubicacin de la hoja de estilo.
excudingPattern: lista de los elementos que se excluir a partir de un modelo.
matchingPattern: especificacin de los modelos.
parse: ruta de la hoja interpretada o no con una expresin OGNL.
Las hojas de estilo XSLT estn presentes por defecto en el cach con Struts. Para cambiar esta
configuracin,
es
necesario
modificar
el
parmetro
struts.xslt.nocache
en
el
archivo struts.properties odefault.properties.
El proyecto ejemplo30 permite crear un archivo XML a partir de los datos introducidos por el usuario.
Este archivo XML toma forma a partir de los datos introducidos en un formulario HTML y de una hoja
de estilo XSL. Cada acceso se realiza automticamente a partir del nodo result y de los nodos hijos
(por ejemplo, /result/cliente/identificador del cliente.getIdentificador()).
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo30" namespace="/" extends="strutsdefault">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="XMLXSL" class="ejemplo30.ClienteAccion">
<result name="success" type="xslt">
<param name="stylesheetLocation">
/xsl/Cliente.xsl
</param>
</result>
</action>
</package>
</struts>
Cdigo: /jsp/AgregarCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Agregar un cliente</h3>
<s:form method="post" action="XMLXSL"
id="Formulario_Cliente" name="Formulario_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.fechadenacimiento"
id="cliente_fechadenacimiento" label="Fecha de nacimiento"
labelposition="top" cssClass="input"/>
<s:textarea name="cliente.descripcion" id="cliente_descripcion"
label="Descripcin" labelposition="top" cssClass="textarea"/>
<s:submit value="Confirmar" id="botonconfirmar"/>
</s:form>
</div>
</body>
</html>
Cdigo: ejemplo30.ClienteAccion.java
package ejemplo30;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo30.javabeans.Cliente;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private Cliente cliente;
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
// procesar la informacin
public String execute() {
return SUCCESS;
}
}
Cdigo: /xsl/Cliente.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<cliente>
<identificador>
<xsl:value-of select="/result/cliente/identificador"/>
</identificador>
<contrasena>
<xsl:value-of select="/result/cliente/contrasena"/>
</contrasena>
<fechadenacimiento>
<xsl:value-of select="/result/cliente/fechadenacimiento"/>
</fechadenacimiento>
<descripcion>
<xsl:value-of select="/result/cliente/descripcion"/>
</descripcion>
</cliente>
</xsl:template>
</xsl:stylesheet>
Cdigo: struts.properties
struts.xslt.nocache=true
Cdigo: /xsl/Cliente.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<rss version="2.0">
<channel>
<title>Flujo RSS de clientes</title>
<link>http://localhost:8080/ejemplo31/</link>
<description>Flujo RSS de clientes</description>
<!-- cliente -->
<item>
<title>
<xsl:value-of select="/result/cliente/identificador"/>
</title>
<description>
<xsl:value-of select="/result/cliente/descripcion"/>
</description>
</item>
</channel>
</rss>
</xsl:template>
</xsl:stylesheet>
En resumen
Struts ofrece un medio simple y eficaz para gestionar los resultados de tipo XSLT. La respuesta se
eenviarn en formato XML asociado con una hoja de estilo XSL. Esta arquitectura es la ms utilizada
para la gestin de salidas estndar en diferentes formatos como XML, RSS, XHTML o PDF sin tocar el
cdigo y manipulando nicamente la capa Vista del modelo MVC.
Presentacin
Struts ofrece un sistema de complementos para aumentar las funcionalidades del framework. Un
complemento (plug-in) de Struts es un archivo con formato .jar compuesto de clases de Java, de
plantillas de Velocity o FreeMarker y de un archivo struts-plugin.xml.
Los complementos Struts se facilitan en forma de archivos .jar ubicados en el directorio /WEB-INF/libde
la aplicacin. En el archivo del complemento, un archivo struts-plugin.xml permite definir el
empaquetado de sus resultados. Como recordatorio, Struts carga los archivos de gestin en este
orden:
El archivo struts-default.xml ubicado en la biblioteca struts2-core-2.x.jar.
Todos los archivos llamados struts-plugin.xml implementados en la aplicacin.
El archivo principal de la aplicacin, struts.xml.
Todos los complementos de Struts pueden utilizar y declarar nuevos paquetes, tipos de resultados,
interceptores, acciones o bibliotecas de etiquetas. Existe un gran nmero de complementos ms o
menos tiles en Internet que se pueden utilizar con el framework.
El complemento JFreeChart
JFreeChart (http://www.jfree.org/jfreechart/) es una biblioteca Java OpenSource que permite crear
grficos evolucionados. La biblioteca, que se puede descargar desde el sitio del autor, se compone de
los siguientes archivos que deben copiarse en el directorio /WEB-INF/lib de la aplicacin para su
utilizacin:
jfreechart-x.jar: la biblioteca grfica de JFreeChart.
jcommon-x.jar: la biblioteca dependiente para la creacin de los grficos.
struts2-jfreechart-plugin-2.x.jar: el complemento JFreeChart para Struts.
La biblioteca struts2-jfreechart-plugin-2.x.jar es necesaria para la gestin de los grficos. De
hecho, esta biblioteca contiene el archivo struts-plugin.xml que definen el resultado de tipo
chart. Adems, la versin del complemento debe ser idntica a la versin de Struts.
La creacin de una aplicacin de Struts JFreeChart se realiza siguiendo los siguientes puntos:
Creacin de un paquete de tipo jfreechart-default.
Utilizacin de la clase JFreeChart para crear el grfico.
Definicin de un resultado de tipo chart he inicializacin de los parmetros width y height para
el tamao del grfico.
Definicin de un atributo llamado chart en la clase de accin con su getter para devolver el
objeto que se va a mostrar.
Los grficos se devuelven en forma de imgenes en formato PNG o JPEG. La
aplicacin ejemplo33permite introducir la divisin del tiempo de trabajo del cliente informtico
(porcentaje de tiempo para realizar el anlisis, el desarrollo, las pruebas y el mantenimiento) y
mostrar una grfica circular (pie) para representar la distribucin.
Cdigo struts .xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo33" namespace="/"
extends="jfreechart- default">
<default-action-ref name="Tiempo_Cliente" />
<action name="Tiempo_Cliente">
<result>/jsp/TiempoCliente.jsp</result>
</action>
<action name="Grafica" class="ejemplo33.ClienteAccion">
<result name="success" type="chart">
<param name="value">chart</param>
<param name="type">png</param>
<param name="width">600</param>
<param name="height">400</param>
</result>
</action>
</package>
</struts>
Cdigo : ejemplo33.ClienteAccion.java
package ejemplo33;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.util.Rotation;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo33.javabeans.Cliente;
public class ClienteAccion extends ActionSupport {
// objeto para la grfica
private JFreeChart chart;
// objeto cliente
private Cliente cliente;
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
// generar la grfica a partir de la informacin
public String execute() throws Exception {
// crear la lista de datos que se mostrarn en la grfica
DefaultPieDataset datos=new DefaultPieDataset();
datos.setValue("Analisis",
this.cliente.getPorcentajeAnalisis());
datos.setValue("Desarrollo",
this.cliente.getPorcentajeDesarrollo());
datos.setValue("Pruebas",
this.cliente.getPorcentajePruebas());
datos.setValue("Mantenimiento",
this.cliente.getPorcentajeMantenimiento());
// crear la grfica
chart = ChartFactory.createPieChart3D(
"Distribucin del tiempo de trabajo del cliente", //
ttulo
datos,
// datos que se van a mostrar
true,
// mostrar leyenda
true,
true
);
// crear la grfica circular
PiePlot3D plot=(PiePlot3D) chart.getPlot();
// ngulo de visualizacin
plot.setStartAngle(190);
// rotation
plot.setDirection(Rotation.CLOCKWISE);
// transparencia del esquema
plot.setForegroundAlpha(0.4f);
plot.setNoDataMessage("No hay datos para mostrar");
return SUCCESS;
}
// getter para devolver la imagen
public JFreeChart getChart() {
return chart;
}
}
Cdigo: /jsp/TiempoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Distribución del tiempo del cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Distribución del tiempo del cliente</h3>
<s:form method="post" action="Grafica"
id="Formulario_Cliente" name="Formulario_Cliente">
<s:textfield name="cliente.identificador"
id="cliente.identificador" label="Identificador" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.contrasena"
id="cliente.contrasena" label="Contrasea" labelposition="top"
cssClass="input"/>
<s:textfield name="cliente.porcentajeAnalisis"
id="cliente.porcentajeAnalisis" label="Porcentaje Analisis"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.porcentajeDesarrollo"
id="cliente.porcentajeDesarrollo" label="Porcentaje
Desarrollo" labelposition="top" cssClass="input"/>
<s:textfield name="cliente.porcentajePrueba"
id="cliente.porcentajePrueba" label="Porcentaje Pruebas"
labelposition="top" cssClass="input"/>
<s:textfield name="cliente.porcentajeMantenimiento"
id="cliente.porcentajeMantenimiento" label="Porcentaje Mantenimiento"
labelposition="top" cssClass="input"/>
<s:submit value="Confirmar" id="botonconfirmar"/>
</s:form>
</div>
</body>
</html>
</html>
Cdigo: /javascript/ejemplo33ajax.js
// funcin que permite la visualizacin del grfico en Ajax
function mostrarGrafica()
{
// enlace de la accin con un parmetro aleatorio para
forzar la actualizacin de la imagen y evitar el cach
var action="Grafica.action?"+new Date().getTime();
// insertar la imagen en la etiqueta
var im=new Image();
im.src=action;
document.images["imagen"].src=im.src;
// cambiar la visualizacin cada 2 segundos
setTimeout("mostrarGrafica()",2000);
}
El complemento Tiles
El complemento Tiles permite gestionar la apariencia y la divisin de las aplicaciones Web. Los
proyectos de Internet normalmente utilizan divisiones en forma de encabezados, men, contenido y
pie de pgina. Para realizar esta divisin, los desarrolladores utilizan tablas HTML o an mejor,
definiciones en las hojas de estilo CSS y etiquetas <span/>, <div/>u otras. El proyecto se compone de
estas pginas (fragmentos) llamadas, por ejemplo encabezado.jspf, menu.jspf y piedepagina.jspf. A
continuacin, cada vista del sitio utiliza inclusiones dinmicas a partir de la directiva JSP <%@ include
file=... %> o del tag <jsp:include/>.
El proyecto ejemplo34 pasado en la aplicacin ejemplo08 utiliza una divisin del sitio a partir de
fragmentos de pginas JSP y de la etiqueta <%@ include file=... />.
Cdigo: /jspf/encabezado.jspf
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Gestin de los clientes</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<!-- Mensaje de error -->
<s:if test="errors.size()>0">
<div id="mensaje_error">
<label>Se produjeron los siguientes errores: </label>
<ul><s:fielderror/></ul>
</div>
</s:if>
<div id="encabezado">Gestin de clientes</div>
<div id="menu">
MENÚ<br/>
<a href="/ejemplo34">Mostrar el formulario</a>
</div>
<div id="carta">
Cdigo: /jsp/AgregarCliente.jsp
<%@ include file="../jspf/encabezado.jspf" %>
<h3>Agregar un cliente</h3>
<s:form method="post" action="ConfirmarAgregar_Cliente">
<s:textfield name="identificador" id="identifiant"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="contrasena" id="contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:submit value="Agregar un cliente"/>
</s:form>
<%@ include file="../jspf/piedepagina.jspf" %>
Cdigo: /jsp/MostrarCliente.jsp
<%@ include file="../jspf/encabezado.jspf" %>
<p>
<h4><s:property value="%{getText(cliente.mostrar)}"/></h4>
<s:property value="%{getText(cliente.identificador)}"/>:
<s:property value="identificador"/> <br/>
<s:property value="%{getText(cliente.contrasena)}"/>: <s:property
value="contrasena"/><br/>
</p>
<%@ include file="../jspf/piedepagina.jspf" %>
Cdigo: /jspf/piedepagina.jspf
</div>
<div id="piedepagina">Proyecto ejemplo34 utilización de
fragmentos</div>
</body>
</html>
Cdigo: /jsp/PlantillaPresentacion.jsp
<%@ taglib uri="http://tiles.apache.org/tags-tiles"
prefix="tiles"%>
<html>
<head>
<title><tiles:getAsString name="tituloPagina"/></title>
</head>
<body>
<tiles:insertAttribute name="encabezado"/>
<tiles:insertAttribute name="contenido"/>
<tiles:insertAttribute name="piedepagina"/>
</body>
</html>
2. Definicin de la plantilla
Se utiliza una definicin de plantilla entre la pgina de presentacin de la plantilla y las pginas JSP
del usuario. Por analoga con Java, la pgina de presentacin de la plantilla (layout) se compara a
menudo con una interfaz y la definicin de la plantilla con la clase de usuario de la interfaz, que
proporciona una implementacion de sta.
Arquitectura de Tiles
La pgina de definicin del plantilla se establece en un archivo tiles.xml ubicado en el
directorio/WEB-INF de la aplicacin Struts. El archivo tiles.xml del projecto ejemplo35 se presenta a
continuacin con la ayuda de la etiqueta <definition/>y de los atributos namey template.
Cdigo: /WEB-INF/tiles.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://struts.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
<definition name="AgregarCliente"
template="/jsp/PlantillaPresentacion.jsp">
<put-attribute name="titulopagina" value="Registro de un cliente"/>
<put-attribute name="encabezado" value="/jspf/encabezado.jspf"/>
<put-attribute name="contenido" value="/jsp/AgregarCliente.jsp"/>
<put-attribute name="piedepagina" value="/jspf/piedepagina.jspf"/>
</definition>
<definition name="MostrarCliente"
template="/jsp/PlantillaPresentacion.jsp">
<put-attribute name="titulopagina" value="Visualizacin del cliente"/>
<put-attribute name="encabezado" value="/jspf/encabezado.jspf"/>
<put-attribute name="contenido" value="/jsp/MostrarCliente.jsp"/>
<put-attribute name="piedepagina" value="/jspf/piedepagina.jspf"/>
</definition>
</tiles-definitions>
La etiqueta <definition/> contiene una o varias etiquetas <put-attribute/> utilizadas en
referencia a la pgina de presentacin de la plantilla. Para nuestro proyecto, la pgina de
presentacin utilizada para la plantilla es /jsp/PlantillaPresentacion.jsp.
La definicin AgregarCliente permite declarar una variable llamada titulopagina asociada a la
presentacin de la plantilla y tres pginas para insertar respectivamente el encabezado, el
contenido y el pie de pgina.
Posteriormente, en el archivo de configuracin de la aplicacin struts.xml, se asociaron los
resultados
de
tipo
tiles
con
archivo tiles.xml (AgregarCliente yMostrarCliente).
las
diferentes
definiciones
del
<listener>
<listenerclass>org.apache.struts2.tiles.StrutsTilesListener</listenerclass>
</listener>
Definicin de los resultados de tipo tiles en el archivo de configuracin struts.xmI o
utilizacin del paquete tiles-default.
<result-types>
<result-type name="tiles"
class="org.apache.struts2.views.tiles.TilesResult"/>
</result-types>
Utilizacin de los resultados de tipos tiles en los resultados de las acciones de Struts.
El proyecto ejemplo35 est terminando, el archivo struts.xml contiene la definicin de las acciones y
de los resultados de tipo tiles. Estos ltimos estn relacionados con el archivo de definicin de la
plantilla PlantillaPresentacion.jsp y las definiciones del archivo tiles.xml.
Los
resultados
definidos en
el archivo
struts.xml se
envan
a
la
plantilla
presentacinPlantillaPresentacion.jsp
y
utilizar
los
datos
presentados
en
definiciones AgregarCliente yMostrarCliente del archivo tiles.xml.
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="ejemplo35" 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">
<listener>
<listenerclass>org.apache.struts2.tiles.StrutsTilesListener</listenerclass>
</listener>
<filter>
<filter-name>struts2</filter-name>
<filterclass>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExec
uteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
de
las
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo35" namespace="/" extends="struts-default">
<result-types>
<result-type name="tiles"
class="org.apache.struts2.views.tiles.TilesResult"/>
</result-types>
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente" class="ejemplo35.ClienteAccion">
<result type="tiles">AgregarCliente</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo35.ClienteAccion" method="agregar">
<result name="input" type="tiles">AgregarCliente</result>
<result name="success" type="tiles">MostrarCliente</result>
</action>
</package>
</struts>
Cdigo: /jsp/PlantillaPresentacion.jsp
<%@ taglib uri="http://tiles.apache.org/tags-tiles"
prefix="tiles"%>
<html>
<head>
<title><tiles:getAsString name="titulopagina"/></title>
</head>
<body>
<tiles:insertAttribute name="encabezado"/>
<tiles:insertAttribute name="contenido"/>
<tiles:insertAttribute name="piedepagina"/>
</body>
</html>
Escribir un complemento
La escritura de un complemento no es una tarea muy compleja con el framework Struts. El principio es
el mismo que en la creacin de archivos en formato .jar. Vamos a escribir un complemento que permita
generar directamente una coleccin de objetos en forma de un flujo RSS (lo retomamos del
proyectoejemplo23).
El rbol de la aplicacin utiliza una escritura de paquete completamente validada para poder ser
exportada:
}
public String getEnlace() {
return enlace;
}
public void setEnlace(String enlace) {
this.enlace = enlace;
}
// mtodo ejecutado por el resultado
public void doExecute(String finalLocation,ActionInvocation
invocation) throws Exception
{
// recuperar el objeto response
HttpServletResponse
response=(HttpServletResponse)invocation.getInvocationContext().get
(StrutsStatics.HTTP_RESPONSE);
// recuperar la lista de clientes de la pila
de ejecucin
List<Object>
listaObjects=(List<Object>)invocation.getStack().findValue(this.getColeccionObjeto());
// tipo de respuesta
response.setContentType("application/xml");
PrintWriter out=response.getWriter();
out.println("<?xml version=\"1.0\" encoding=\"ISO8859-1\"?>");
out.println("<rss version=\"2.0\">");
// crear un canal
out.println("<channel>");
out.println("<title>Flujo RSS de los objetos</title>");
out.println("<link>"+this.getEnlace()+"</link>");
out.println("<description>Flujo RSS de
objetos</description>");
// crear los objetos
for(int i=0;i<listaObjects.size();i++)
{
Object o=(Object)listaObjects.get(i);
out.println("<item>");
out.println("<title>"+o+"</title>");
out.println("<link>"+this.getEnlace()
+"</link>");
out.println("<description>"+o+"</description>");
out.println("</item>");
}
out.flush();
out.close();
}
}
La etapa de generacin del archivo (.jar) consiste en crear un directorio independiente, que conserve
el rbol de paquetes, para colocar el archivo completado .class y crear el archivo struts-plugin.xml en
la raz del paquete.
Utilizar el complemento
Ya sea creado el complemento, ahora podemos pasar a su utilizacin copiando la biblioteca pluginstruts2-rss.jar en el directorio /WEB-INF/lib de una aplicacin. El complemento se utiliza en el archivo
de configuracin struts.xml para declarar un paquete que lo utiliza. El archivo de configuracin
comienza por la definicin del paquete que utiliza el complemento (extends="rss-default") y la
definicin del resultado de tipo rss con los enlaces que se realizarn en el flujo RSS y al nombre de la
coleccin de objetos que se debe mostrar.
Cdigo : struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="false" />
<package name="ejemplo36" namespace="/" extends="rss-default">
<default-action-ref name="ListadoRSS_Cliente" />
<action name="ListadoRSS_Cliente"
class="ejemplo36.ClienteAccion" method="listado">
<result type="rss">
<param name="enlace">http://www.google.es</param>
<param name="coleccionObjeto">listaClientes</param>
</result>
</action>
</package>
</struts>
Por ltimo, la clase de accin ClienteAccion.action utiliza la coleccin esttica
nombrelistaClientes y el complemento rss. Ahora la aplicacin es totalmente funcional.
Cdigo: ejemplo36.ClienteAccion.java
package ejemplo36;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo36.javabeans.Cliente;
import ejemplo36.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
private Cliente cliente;
private List<Cliente> listaClientes;
con
el
this.cliente = cliente;
}
public List<Cliente> getListaClientes() {
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {
this.listaClientes = listaClientes;
}
// devolver la lista de clientes despus que la recuperacin
public String listado()
{
listaClientes=ClienteModelo.getListaClientes();
return SUCCESS;
}
}
Cdigo: ejemplo36.javabeans.Cliente.java
package ejemplo36.javabeans;
@SuppressWarnings("serial")
public class Cliente {
private int idCliente;
private String identificador;
private String contrasena;
public Cliente() {
}
public Cliente(int idCliente,String identificador, String
contrasena){
this.idCliente=idCliente;
this.identificador=identificador;
this.contrasena=contrasena;
}
// getter y setter
public String toString() {
String res="Identificador: "+this.getIdentificador()+"
- Contrasea: "+this.getContrasena();
return res;
}
}
Otros complementos
El sitio http://cwiki.apache.org/S2PLUGINS/home.html ofrece una coleccin de distintos complementos
disponibles para el framework Struts. Podemos encontrar complementos para Hibernate, Ajax,
JavaScript, imgenes o incluso la facilitacin del uso de tablas.
La comunidad de complementos de Struts se encuentra actualmente en fase de desarrollo y
prcticamente cada da aparecen nuevos complementos, ms o menos tiles y funcionales,
disponibles en lnea. Un desarrollador ver como se agiliza su trabajo con la ayuda de estos
complementos.
En resumen
El framework Struts ofrece una forma simple y eficaz de crear complementos. En este captulo se han
presentado el mecanismo de complemento de Struts, as como la utilizacin a partir de archivos y del
archivo de configuracin struts.xml. El complemento JFreeChart permite crear grficas complejas con la
ayuda de un resultado de tipo chart. El complemento Tiles se utiliza para la arquitectura de las vistas
y con el fin de facilitar el mantenimiento del conjunto. Por ltimo, los dos ltimos prrafos explican el
desarrollo de complementos personalizados y la utilizacin a partir de archivos fuente y del archivo en
formato .jar.
Presentacin
Como hemos visto desde el comienzo de este libro, la configuracin de las acciones, validaciones o
resultados es una tarea simple con Struts, se realiza a partir de archivos XML. El framework ofrece, sin
embargo, un segundo enfoque llamado configuracin cero o zero configuration. En lugar de utilizar el
archivo struts.xml para especificar las clases y el enrutamiento, las clases se anotan.
Configuracin
Si deseamos utilizar la configuracin cero, es decir sin archivo XML, debemos indicar a Struts que debe
utilizar un paquete o una lista de paquetes como clases de accin. La primera versin de Struts 2
ofreca la posibilidad de utilizar el sistema Zero Config, que permita especificar el paquete que se
anotara en el archivo /WEB-INF/web.xml. Esta tcnica se ha mejorado en beneficio del
complementoCodeBehind, que permite la simplificacin de las declaraciones de paquetes que se
anotarn y el funcionamiento global. Desde la versin 2.1 de Struts, estas 2 tcnicas han pasado a
ser obsoletas en beneficio del complemento Convention.
El complemento Convention (struts2-convention-plugin-2.x.jar) est incluido en el archivo struts2.Xlib.rar, que se puede descargar desde el sitio oficial de Struts 2. Este archivo contiene las bibliotecas
estndar, as como los complementos que pueden utilizarse con la versin indicada del framework.
Este complemento permite realizar anotaciones de acciones, de interceptores, de validaciones o de
tipo de conversiones. A continuacin se resumen las principales funcionalidad es de este
complemento:
Notacin de acciones.
Convencin de nombramiento para los resultados.
Convencin de nombramiento para los accesos a los nombres de clases por URL.
Convencin de nombramiento para los paquetes.
Notacin de interceptores.
Notacin de espacios de nombres.
Notacin de paquetes XWork.
Para utilizar el complemento Convention, es necesario copiar la biblioteca struts2-convention-plugin2.x.jar en el directorio /WEB-INF/lib de la aplicacin. El archivo struts.xml de configuracin de
aplicaciones ya no es obligatorio, la aplicacin utiliza automticamente el complemento Convention si
este est instalado y est presente en la classpath.
En el caso de una distribucin de la aplicacin en forma de paquete con la configuracin cero,
es necesario establecer el parmetro struts.convention.action.disableJarScanning en true.
Utilizacin
Por defecto, el complemento Convention utiliza resultados automticos presentes en el
directorio/WEB-INF/content de la aplicacin. Esta configuracin se puede modificar con la
propiedadstruts.convention.result.path presente en el archivo de propiedades de Struts. El mapping
de las URL se realiza con la ayuda de los nombres. As, la accin que se ejecuta con la URL
http://localhost:8080/ejemplo37/cliente se asociar automticamente a la vista JSP /WEBINF/content/cliente.jsp.
1. Nomenclatura
Para aplicar la configuracin cero, utilizaremos un nuevo proyecto llamado ejemplo37 a partir de la
aplicacin ejemplo14. Por defecto, el complemento Convention busca todas las clases que heredan
de la clase com.opensymphony.xwork2.Actiono que tienen el sufijo Action en paquetes especficos.
Los paquetes utilizados por el complemento Convention deben respetar una convencin de
nomenclatura y se les llama struts, struts2, action o actions.
Cada paquete que contenga uno de estos nombres ser considerado como un paquete del
complemento Convention. A continuacin, el complemento examine si las clases de estos subpaquetes heredan de la clase com.opensymphony.xwork2.Actiono si tienen el sufijo Action.
La llamada de las URL se basa en el rbol de los paquetes:
La clase ejemplo37.acciones.Cliente.javaser llamada por la URL /cliente y el nombre de espacio /.
La clase ejemplo37.actions.paginacion.Cliente.javaser llamada por la URL /paginacion/cliente y el
nombre de espacio /paginacion.
Tambin podemos indicar al complemento que ignore determinados paquetes (para no aplicar las
acciones)
con
el
parmetro
struts.convention.exclude.packages
en
el
archivo
de
configuracinstruts.xml. Asimismo, podemos realizar la accin a la inversa, especificar a Struts en
qu paquete aplicar las acciones con el parmetro struts.convention.action.packages.
2. Notacin de acciones
La utilizacin de anotaciones de acciones permite especificar la URL que ejecutar la accin para la
aplicacin. La anotacin @Action permite declarar una URL y la anotacin @Actions permite asignar
varias URL. La notacin de URL es sensible a maysculas y minsculas, pero el sufijo .action es
utilizado automticamente en las URL. Podemos especificar tantas anotaciones de acciones como
funcionalidades que se van a ejecutar posee la clase.
El proyecto ejemplo37 permite hacer una lista de los clientes a partir de la coleccin del modelo. La
siguiente clase de accin permite realizar el procesamiento de la aplicacin con la ayuda de
anotaciones de acciones.
Cdigo: ejemplo37.actions.ClienteAccion.java
package ejemplo37.actions;
import java.util.List;
import org.apache.struts2.convention.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo37.javabeans.Cliente;
import ejemplo37.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport {
// lista de clientes
private List<Cliente> listaClientes;
3. Anotaciones de resultados
Por defecto, como ya hemos explicado en este captulo, el complemento Convention utiliza el
directorio /WEB-INF/content, as como cada pgina asociada a la accin para realizar el
enrutamiento. La anotacin @Results ofrece 2 tipos de resultados: globales o locales. Los
resultados globales permiten compartir los resultados con todos los mtodos de la clase de accin.
Al igual que las acciones, los resultados se definen en forma de anotaciones. De manera inversa, los
resultados locales permite especificar los resultados accin por accin.
Para lo siguiente utilizamos un nuevo proyecto llamado ejemplo38, adaptado
proyecto ejemplo14para aplicar el principio de la configuracin cero y la gestin de resultados.
del
4. Anotacin de interceptores
Para gestionar los clientes utilizamos el interceptor paramsPrepareParamsStack con la siguiente
declaracin en el archivo de configuracin de la aplicacin struts.xml:
<action name="Editar_Cliente" class="ejemplo14.ClienteAccion"
method="editar">
<interceptor-ref name="paramsPrepareParamsStack"/>
<result name="success">/jsp/EditarCliente.jsp</result>
</action>
La utilizacin del interceptor con la configuracin cero se realiza con la anotacin @InterceptorRef y el
nombre del interceptor que se utilizar antes de la definicin de la clase de accin.
Cdigo: ejemplo38.acciones.ClienteAccion.java
...
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
...
}
La anotacin @InterceptoRefs permite declarar varios interceptores en la misma clase de accin.
@InterceptorRefs({
@InterceptorRef("paramsPrepareParamsStack"),
@InterceptorRef("validation")
})
public class ClienteAccion extends ActionSupport implements
Preparable{
...
}
Tambin es posible colocar las anotaciones de interceptores al nivel de cada declaracin de accin.
Para ello, utilizamos el parmetro de accin llamado interceptorRefs. Del mismo modo, el
atributoparams de la anotacin @InterceptorRef se compone de una tabla de claves que permite
especificar la configuracin del interceptor: {"clave1","valorclave1","clave2","valorclave2"...}.
En nuestro proyecto ejemplo38, debemos utilizar, adems del parmetro paramsPrepareParamsStack,
el interceptor validation para la comprobacin de los datos introducidos del cliente en los formularios
de registro y de edicin. La tabla params contiene tambin un conjunto de propiedades para
especificar que las validaciones se realizan con la ayuda de un archivo XML sin declaracin en la
clase.
interceptorRefs=@InterceptorRef(value="validation",params={"progra
mmatic", "true", "declarative", "false"})
El proyecto ejemplo38 ahora puede ser adaptado a partir del proyecto completo que utilizaba
anteriormente el archivo de configuracin struts.xml.
Cdigo: ejemplo38.acciones.ClienteAccion.java
package ejemplo38.acciones;
import java.util.List;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.InterceptorRef;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;
import org.apache.struts2.convention.annotation.Result;
import ejemplo38.javabeans.Cliente;
import ejemplo38.modelo.ClienteModelo;
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
// lista de clientes
private List<Cliente> listaClientes;
// objeto cliente
private Cliente cliente;
// cliente que se va a modificar
private int idClienteActual;
public void prepare() throws Exception {
// durante la creacin, crear un nuevo objeto vaco
if(idClienteActual==0)
{
cliente=new Cliente();
}
// durante la modificacin, devolver la informacin del objeto
else
{
cliente=ClienteModelo.getCliente(idClienteActual);
}
}
// anotacin del complemento Convention
@Actions({
@Action(value="/",
results={@Result(name="success",
location="/jsp/ListadoCliente.jsp")}
),
@Action(value="/ListadoCliente",
results={@Result(name="success",
location="/jsp/ListadoCliente.jsp")}
)
})
public String listado()
{
listadoCliente=ClienteModelo.getListadoCliente();
return SUCCESS;
}
// modificar un cliente
@Action(value="/ModificarCliente",
results={
@Result(name="success",
location="/ListadoCliente", type="redirect"),
@Result(name="input",
location="/jsp/EditarCliente.jsp")
}
)
public String modificar()
{
ClienteModelo.modificar(cliente);
return SUCCESS;
}
this.listaClientes = listaClientes;
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public int getIdClienteActual() {
return idClienteActual;
}
public void setIdClienteActual(int idClienteActual) {
this.idClienteActual = idClienteActual;
}
}
Cdigo: ejemplo38.acciones.ClienteAccion.java
package ejemplo38.acciones;
...
@ResultPath("/jsp")
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
...
En resumen
El framework Struts ofrece una tcnica llamada configuracin cero o zero configuration que permite no
crear y escribir el archivo de configuracin de la aplicacin struts.xml y que no sea necesario declarar
cada accin, resultado o interceptor. Esta tcnica utiliza para ello el complemento Convention incluido
en el paquete y que se puede descargar desde el sitio del framework. La aplicacin de esta
configuracin cero se basa en las anotaciones Java 5 situadas al principio de las clases o mtodos de
clases. Cada paquete de la aplicacin debe respetar una convencin de nomenclatura para poder
beneficiarse de la configuracin por anotacin simplificada.
Cuando se respeta esta convencin de nomenclatura, los desarrolladores pueden crear anotaciones
para las acciones, los resultados, los interceptores que se van a utilizar, el espacio de nombre de la
aplicacin, las vistas que se van a mostrar y la gestin de las excepciones. El
proyecto ejemplo38ilustra en detalle la aplicacin de esta tcnica a partir de las principales
funcionalidades de una aplicacin de Internet (hacer listados, agregar, modificar, eliminar).
Presentacin
En este captulo se explicar en detalle los distintos interceptores, as como el lenguaje de
manipulacin de expresiones Java Object-Graph Navigation Language OGNL. Este lenguaje de
manipulacin de datos simplifica el acceso a la informacin de los getters y setters de los JavaBeans.
De igual modo, esta biblioteca puede utilizarse en asociacin con las taglibs de JSTL.
Interceptores de Struts
Los interceptores son filtros que permiten realizar tareas para simplificar y mejorar el trabajo de los
desarrolladores. En la mayora de los casos, los interceptores incluidos con Struts son suficientes y es
fundamental entender las ventajas que cada uno de ellos aporta. Estos son los interceptores
incluidos por defecto con el framework.
params
Gestin del mapping entre los parmetros de las consultas y las propiedades de las acciones.
staticParams
Gestin de los parmetros estticos declarados en las definiciones y clases de accin.
prepare
Gestin del acceso a las plantillas de clases.
scope
Gestin del mecanismo de las sesiones.
servletConfig
Gestin del acceso a las clases HttpServletRequesty HttpServletResponse.
validation
Gestin de las validaciones de formularios.
token
Gestin del doble envo o double submit.
tokenSession
Gestin del doble envo o double submit con un parmetro de sesin.
profiling
Gestin de la creacin de perfiles de las acciones.
timer
Gestin del tiempo de ejecucin de las acciones.
roles
Gestin de las funciones de los Realms para la seguridad de las aplicaciones.
modelDriven
Gestin de los modelos de persistencia.
scopedModelDriven
Gestin de los modelos de persistencia como el interceptor anterior, pero con gestin de sesin.
logger
Gestin de las acciones.
execAndWait
Gestin de la carga de pginas en procesamientos ms o menos largos.
debugging
contiene
los
atributos
presentes
en
el
contexto
de
la
session: esta coleccin contiene los atributos presentes en la sesin del usuario.
request: esta coleccin contiene todos los atributos presentes en la consultar actual.
parameters: esta coleccin contiene los parmetros de la consulta actual.
attr: esta coleccin busca los atributos en el siguiente orden: request, sessiony application.
Para leer objetos en la pila
value=nombredelapropiedad/>
referencia a los parmetros de la
deseamos acceder a los objetos
notacin #.
Por ejemplo, las siguientes notaciones permiten leer los parmetros segn el mbito de aplicacin:
<s:property value="client.identificador"/>
<s:property value="#request.cliente[identificador]"/>
<s:property value="#session.contrasena"/>
<s:property value="#application.emailContacto"/>
La notacin de puntos (y por tabla) se utiliza para acceder a los objetos del contexto. Podemos
acceder, por ejemplo, a la propiedad identificador del objeto cliente de las siguientes formas:
<s:property value="cliente.identificador"/>
<s:property value="cliente[identificador]"/>
<s:property value="#request.cliente[identificador]"/>
OGNL tambin permite ejecutar los mtodos de clases para mostrar un procesamiento especfico
mediante el carcter arroba @. Para ello, debemos especificar en la llamada, el nombre
completamente cualificado de la clase seguido del nombre del mtodo.
Por
ejemplo,
para
ejecutar
el
mtodo
esttico
esttica CajaHerramientaspresente en el paquete ejemplo39.
Visualizacion()
de
la
clase
<s:property value="@ejemplo39.CajaHerramientas@Visualizacion"/>
OGNL tambin permite manipular las tablas de valores (Array) mediante el getter asociado y las
distintas propiedades dedicadas. La tabla de pases declarados en la clase de accin se utiliza en la
vista mediante OGNL.
// Lista de pases
private String[] listaPaises;
{
listaPaises=new String[]
"Espaa","Inglaterra","Alemania","Suiza"};
}
Podemos mostrar directamente el contenido de la tabla mediante su nombre, su tamao con el
OGNL permite ejecutar mtodos de la clase de accin. La tcnica es la misma que para el acceso a los
objetos de la clase y de la internacionalizacin.
// mtodo de la clase
public String getVisualizacion() {
return "Mtodo de la clase de accin: "+new
GregorianCalendar().getTime();
}
Podemos mostrar directamente el resultado de la ejecucin del mtodo de accin mediante su
nombre.
Ejecutar un método de clase de acción:
<s:property value="%{getVisualizacion()}"/>
El nuevo proyecto ejemplo39 ilustra detalladamente los ejemplos anteriores y presenta las distintas
manipulaciones de OGNL.
Cdigo: struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//
EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo39" namespace="/" extends="strutsdefault">
<default-action-ref name="OGNL" />
<action name="OGNL" class="ejemplo39.OgnlAccion">
<result>/jsp/Ognl.jsp</result>
</action>
</package>
</struts>
Cdigo: ejemplo39.OgnlAccion.java
package ejemplo39;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
import ejemplo39.javabeans.Cliente;
@SuppressWarnings("serial")
public class OgnlAccion extends ActionSupport implements
SessionAware{
// objeto cliente
private Cliente cliente;
// objeto session
private Map<String,Object> sessionMap;
// tabla de pases
private String[] listaPaises;
{
listaPaises=new String[]
{"Espaa","Inglaterra","Alemania","Suiza"};
}
// Lista de ciudades
private Map<String,String> listaCiudades=new
HashMap<String,String>();
{
listaCiudades.put("MA", "Madrid");
listaCiudades.put("BA", "Barcelona");
listaCiudades.put("VA", "Valencia");
}
// lista de clientes
private List<Cliente> listaClientes=new ArrayList<Cliente>();
{
Cliente c1=new Cliente();
c1.setIdentificador("jlafosse");
c1.setContrasena("jerome");
Cliente c2=new Cliente();
c2.setIdentificador("asoto");
c2.setContrasena("amelia");
listaClientes.add(c1);
listaClientes.add(c2);
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {
this.cliente = cliente;
}
public void setSession(Map<String,Object> map)
{
this.sessionMap=map;
}
public String[] getListaPaises() {
return listaPaises;
}
public Map<String, String> getListaCiudades() {
return listaCiudades;
}
public List<Cliente> getListaClientes() {
return listaClientes;
}
// mtodo de la clase
public String getVisualizacion() {
return "Mtodo de la clase de accin: "+new
GregorianCalendar().getTime();
}
// aadir la informacin del cliente a la sesin
public String execute()
{
// creacin de un objeto cliente
cliente=new Cliente();
cliente.setIdentificador("jlafosse");
cliente.setContrasena("jerome");
// aadir la contrasea a la sesin
sessionMap.put("contrasena", "lafosse");
return SUCCESS;
}
}
Cdigo: /jsp/Ognl.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>OGNL</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<s:debug/>
<div id="carta">
<h4>Información OGNL</h4>
Lectura del cliente en la consulta:
<s:property value="cliente"/><br/>
Lectura del cliente en la consulta:
<s:property value="cliente.identificador"/><br/>
Lectura del cliente en la consulta:
<s:property value="cliente[identificador]"/><br/>
Lectura del cliente en la consulta:
<s:property value="#request.cliente[identificador]"/><br/>
<hr/>
Lectura de la información en la sesión:
<s:property value="#session.contrasena"/><br/>
Lectura de la información en la sesión:
<s:property value="#session[contrasena]"/><br/>
<hr/>
Lectura de la información en el contexto de
la aplicación (web.xml): <s:property
value="#application.emailContacto"/><br/>
<hr/>
Manipulación de tablas (Arrays)<br/>
Lista de países: <s:property value="listaPaises"/><br/>
Lista de países: <s:property value="[0].listaPaises"/>
<br>
Tamaño de la tabla de los países: <s:property
value="listaPaises.length" /><br/>
Acceso al primer índice de la tabla:
<s:property value="listaPaises[0]"/><br/>
<hr/>
Manipulación de listas (Map)<br/>
Lista de ciudades: <s:property
value="listaCiudades"/><br/>
Lista de ciudades: <s:property
value="[0].listaCiudades"/> <br>
Tamaño de la lista de ciudades: <s:property
value="listaCiudades.size"/><br/>
Acceso al primer índice de la tabla:
<s:property value="listaCiudades[BA]"/><br/>
<hr/>
Manipulación de listas (List)<br/>
Lista de clientes: <s:property
value="listaClientes"/><br/>
Lista de clientes: <s:property
value="[0].listaClientes"/> <br>
Tamaño de la lista de clientes: <s:property
value="listaClientes.size"/><br/>
Acceso al primer índice de la tabla:
<s:property value="listaClientes[0].getIdentificador()"/><br/>
¿Lista vacía? : <s:property
value="listaClientes.isEmpty"/><br/>
<hr/>
Ejecutar un método de clase de acción:
<s:property value="%{getVisualizacion()}"/>
<hr/>
Lista creada con OGNL<br/>
<s:select label="Etiqueta de la lista" name="nombre"
list="{valor 1,valor 2,valor 3}"/>
</div>
</body>
</html>
En resumen
En este ltimo captulo se ha presentado una lista detallada de los interceptores utilizados por el
framework Struts. De igual modo, la seccin Object-Graph Navigation Language OGNL explica de
forma extensa el lenguaje de manipulacin OGNL, que permite acceder y modificar los objetos Java.