Structs 2

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 356

Struts 2

El framework de desarrollo de aplicaciones Java EE


Este libro sobre Struts 2 se dirige a los desarrolladores Java que quieren tener un libro de
referencia para la utilizacin del framework de Java EEms popular. El libro est dividido en 23
captulos que explican elfuncionamiento y la ejecucin de proyectos Web a partir
del framework.
Los primeros captulos describen el framework de Java EE de referencia con sus servicios y
su instalacin a travs del modelo de diseo MVC. Los captulos 3 y 4 presentan un ejemplo
concreto de proyecto de Struts 2para gestionar los registros y la depuracin. En el siguiente
captulo, el lector podr conocer en detalle la administracin de las acciones, elmapping,
los formularios y las redirecciones. El captulo 6 presenta de manera extensa la biblioteca de
etiquetas de Struts.
En el resto del libro, el lector aprender a administrar los mensajes y lainternacionalizacin, as
como la validacin de las entradas, los tipos y lasconversiones. Un captulo est dedicado al
modelo de acceso a los datos, a la carga y descarga de datos y a la carga de las pginas.
El desarrollo de los interceptores se detalla ampliamente en el captulo 14, as como la gestin de
los resultados en el captulo 15.
Los siguientes captulos estn dedicados a Ajax Struts y a los motores de plantillas, as como a
la visualizacin de la informacin a travs de XSLT. Los ltimos captulos se refieren a la
utilizacin y el desarrollo decomplementos con Struts, as como a la configuracin cero y el
lenguajeOGNL.
Cada concepto se expone de forma terica y tcnica para permitir a los diseadores que tengan
conocimientos en Java EE utilizar una API que facilita el desarrollo de aplicaciones Web.
Las aplicaciones utilizadas en los captulos proceden de ejemplos concretosy se
pueden descargar en esta pgina.

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

Por qu utilizar un framework?


Los Servlets se definieron en 1998 y dos aos despus, algunas grandes empresas ya haban
apostado por Java para sus aplicaciones Web. Durante varios aos, estas empresas desarrollaron
sus propios proyectos de manera independiente y sin seguir ningn estndar. Hoy en da, todas
estas sociedades tienen en cuenta la importancia de los frameworks. La eleccin del framework de
desarrollo forma parte de la estrategia de una empresa ya que ser determinante para la calidad, la
productividad y la durabilidad de los proyectos.

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.

2. Framework y desarrollo Web


La definicin inicial del API Servlet se queda corta a la hora de abarcar un desarrollo complejo de
aplicaciones totalmente basadas en Servlets. Al principio, las aplicaciones Java se basaban en el
principio del API Common Gateway Interface (CGI) y progresivamente fueron apareciendo los
frameworks de Java para cubrir las carencias o debilidades del API Servlet y JavaServer Pages (JSP).
La eleccin del API tendr un impacto significativo en el rendimiento, el funcionamiento, la calidad y
el mantenimiento de la aplicacin. Del mismo modo, puesto que el framework constituir los
cimientos sobre los que se construir el software, su durabilidad ser tambin fundamental.

Los distintos tipos de framework


Existen varios tipos de herramientas para el desarrollo de aplicaciones. Un framework de tipo
"interno", es decir, desarrollado por la empresa, no es la mejor solucin. Desde los primeros aos de
Java, los equipos de informticos inventaron sus propias herramientas para el desarrollo y las
grandes empresas, en ocasiones, creaban su propio framework. Hay que evitar este tipo de
desarrollos, ya que ninguna empresa podr dedicar suficientes esfuerzos para el mantenimiento y la
evolucin del framework. Adems, los frameworks de cdigo abierto (Open Source) se convierten en
estndares, se prueban y validan a escala mundial a travs de los proyectos realizados.
Los frameworks de editor presentan un riesgo para las empresas desde un punto de vista de
desarrollo. De hecho, siempre tienen un objetivo oculto, que es la fidelizacin de la empresa a las
herramientas del editor.
Los frameworks de cdigo abierto (Open Source) son en la actualidad los ms numerosos y los que
ms xito tienen. Aqu, nos volvemos a encontrar con la calidad del trabajo y la misma dinmica que el
proyecto Apache. Una buena parte de estos proyectos de framework nace del consorcio Apache. Los
frameworks son herramientas complejas independientemente de la calidad del desarrollo y del origen
de los proyectos. No es necesario dominar todos los frameworks existentes, pero s es necesario
saber utilizarlos correctamente. Una vez se ha elegido el framework, es necesario formarse y
constituir una unidad de asistencia para los equipos de desarrollo.

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.

Introduccin a la programacin en Java Entreprise Edition


Las tecnologas Servlets y JavaServer Pages (JSP) son los cimientos del desarrollo en Java EE. El
problema con estas tecnologas es la cantidad de cdigo que es necesario desarrollar para las
comunicaciones HTML/JSP, Servlets y Modelos. De igual modo, sin usar un modelo de diseo de tipo
Modelo Vista Controlador (MVC), mezclar scripts HTML, SQL y Java es una mala idea ya que la
depuracin es entonces ms compleja y lleva ms tiempo. La mezcla de cdigo evita la reutilizacin y
la visibilidad de las estructuras de control, haciendo confusa la presentacin y el acceso a los datos.
Por ltimo, el uso de objetos JavaBeans y de administradores de etiquetas como JSTL (Java Standard
Tag Library) permite un desarrollo simple y consistente, incluso para proyectos complejos. La escritura
de cdigo HTML/XHTML en pginas JSP es muy rpida para el desarrollador. De hecho, los JSP no
reemplazan a los Servlets sino que son ms bien complementarios.
Con un modelo de sitio muy simple realizado completamente en JSP, las pginas se compilan y se
transforman en Servlets. Este es el modelo ms utilizado por los programadores principiantes debido
a su simplicidad y rapidez de aplicacin.
Con un modelo de diseo un poco ms complejo, los desarrolladores usan los Servlets para los
procesamientos y las pginas JSP para la presentacin. Este modelo es ms difcil de aplicar, pero
tambin es el ms natural y el que ofrece un mejor mantenimiento.
Por ltimo, para una arquitectura multinivel de tipo MVC, los Servlets representan el aspecto de
Control, los Modelos el acceso a los datos y los JPS la parte Vista. Este modelo es ms complejo de
desarrollar, pero mucho ms simple de probar, mantener y hacer evolucionar. Del mismo modo, cabe
destacar el concepto de reutilizacin con este tipo de modelo.

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;

una herramienta de depuracin incluida;


una potente biblioteca de etiquetas.

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.

Instalacin del framework Struts 2


El framework Struts 2 est basado en Java EE 5 (Servlets 2.4 y JSP 2.0 como mnimo). Para utilizar los
ejemplos del libro, usamos las ltimas versiones de las herramientas con el JDK 1.6, Tomcat 6.X, los
Servlets 2.5, Struts 2.1.6 y Eclipse/Lomboz 3.4. Puede visitar el sitio oficial de Struts 2 a travs de la
siguiente direccin: http://struts.apache.org/2.x/
La instalacin del API es sencilla, basta con copiar los archivos descargados en las carpetas de una
aplicacin Web Java EE tradicional. Podemos crear un nuevo proyecto y copiar los archivos necesarios
(bibliotecas .jar) o bien instalar el framework utilizando una aplicacin Struts virgen proporcionada con
el framework (http://apache.cict.fr/struts/examples/struts-2.1.6-apps.zip).
Existen varias versiones de proyectos para instalar Struts 2. En la seccin Download del sitio, podemos
encontrar las ltimas versiones de Struts con archivos en formato .zip. Las versiones struts-versionall.zip incluyen todas las bibliotecas, archivos fuente y ejemplos de aplicacin. La versin utilizada en
este manual es struts-2.1.6-all.zip de 90 MB. La versin struts-version-lib.zip contiene nicamente las
bibliotecas en formato .jar necesarias para el funcionamiento de Struts 2.
El framework Struts V2 est compuesto de varios archivos. Las bibliotecas Java (.jar) contienen todas
las clases utilizadas por el framework:
commons-fileupload.jar (biblioteca de gestin de la carga en Java).
commons-logging.jar (biblioteca de logging/registros).
commons-io-version.jar (biblioteca de gestin de las entradas/salidas).
freemarker-version.jar (biblioteca utilizada para la presentacin y el motor de plantillas).
ognl-version.jar (biblioteca utilizada para la manipulacin de objetos Java).
junit-version.jar (biblioteca del framework de gestin de las pruebas unitarias).
struts2-core-version.jar (biblioteca completa de Struts 2, es la biblioteca principal).
xwork-version.jar (biblioteca de XWork con las dependencias).
La biblioteca nativa del framework es struts2-core-version.jar mientras que las bibliotecas commonslas
proporciona la fundacin Apache y el proyecto Commons.
Existen dos maneras de instalar el framework Struts:
Copiar las bibliotecas .jar en el directorio /WEB-INF/lib de una nueva aplicacin.
Utilizar una aplicacin de Struts vaca, incluida en el paquete, que permite instalar el
framework. Esta aplicacin vaca lleva el nombre de struts2-blank-version.war para indicar que
est en blanco.
Para instalar esta aplicacin, es necesario:
Copiar el archivo struts-blank-version.war en una carpeta (por ejemplo, instalacionstruts2).
Descomprimir su contenido.
Abrir Eclipse y hacer clic en Archivo - Nuevo - Proyecto - Java - Proyecto Tomcat o Proyecto
Web.
Dar un nombre a su proyecto (por ejemplo, instalacionstruts2) y seleccionar la carpeta
anterior.

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.

La arquitectura MVC separa la lgica empresarial de la visualizacin. En este modelo, un componente


se encarga de recibir las consultas (Servlets), otro procesa los datos (Clases) y un tercero administra
la visualizacin (JSP). Si la interfaz entre estos tres componentes est claramente definida, ser ms
fcil modificar un componente sin afectar a los otros dos.
En una aplicacin Web evolucionada, la lgica MVC es la siguiente:
El cliente emite consultas al servidor. Cada accin precisa corresponde a un Servlet que redirige las
consultas a una pgina JSP adecuada, realiza un procesamiento o accede a los datos y, en este caso,
inicia otro programa que se encargar de responder a la solicitud del usuario actual.
El siguiente esquema presenta una estructura de tipo MCV con la utilizacin de Servlets y pginas
JSP.

Arquitectura MVC, Servlets y JSP


Los Servlets, que tienen una funcin de controlador en una aplicacin MVC, deben disponer de un
medio para transmitir las consultas a los componentes encargados de la visualizacin. Este medio lo
proporciona el objeto RequestDispatcher. Este componente permite trasladar una consulta de un
componente a otro.
Obtenemos un objeto RequestDispatcher con el mtodo getServletContext(). Desde este objeto, es
posible
obtener
un
RequestDispatcher
con
la
ayuda
de
los
mtodos
siguientes:getNamedDispatcher(nombre)
o
getRequestDispatcher(ruta).
El
mtodo getRequestDispatcher(...)funciona con una ruta que comienza por la barra y que es relativo
al contexto de la aplicacin. El mtodo getNamedDispatcher(...) corresponde a un subelemento
<servlet-name>
de
un
elemento<servlet-mapping>
del
descriptor
de
implementacin web.xml.
Los componentes son muy numerosos, pero tambin muy simples. Sus especificaciones hacen que
puedan desarrollarse por parte de especialistas: los Servlets y EJB por desarrolladores Java, los JSP
por desarrolladores y Webdesigners, los accesos a los datos por especialistas de SQL... Esta divisin
permite tambin un mantenimiento ms sencillo del sistema. As, podremos cambiar fcilmente la
identidad grfica utilizando las vistas sin tocar el modelo y el controlador.
En el modelo de diseo MVC, tenemos un Servlet o un filtro que representa el controlador del modelo.
Struts 1 utiliza un Servlet, mientras que Struts 2 utiliza un filtro. Para el modelo utilizamos los POJO
(Plain Old Java Object) que son simples objetos en oposicin a los EJB.
El acrnimo POJO se utiliza para hacer referencia a la simplicidad de la utilizacin de un objeto
Java en comparacin con la dificultad de la utilizacin de un componente EJB (Enterprise Java
Bean). Los JavaBeans (no confundir con los EJB) son componentes lgicos simples reutilizables y
manipulables. Para que sea una clase JavaBean, deber respetar ciertas convenciones de nombre
para respetar la utilizacin, la reutilizacin, la sustitucin y la conexin JavaBean. La clase debe ser
serializable (para guardar y leer). La clase debe tener un constructor por defecto (sin argumento).
Las propiedades de los mtodos deben ser accesibles a travs de los mtodos (descriptores de
acceso). La nica diferencia real entre un POJO y un JavaBean es la posibilidad de los JavaBeans de
gestionar eventos.

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.

Primer proyecto MVC


Vamos a comenzar con un ejemplo simple de formulario de registro de un nuevo cliente (identificador
y contrasea) y la visualizacin de los datos que ha introducido. La aplicacin se llamar ejemplo01. El
usuario introducir un identificador y una contrasea, su informacin se mostrar en otra pgina
despus de la creacin del usuario, sin persistencia de un objeto cliente.
La aplicacin se compone de los siguientes elementos:
Una clase llamada Cliente, que es un JavaBean.
Un Servlet controlador llamado ServletControlador.
Dos pginas JSP para las respectivas visualizaciones del formulario y de los datos
introducidos.
Una hoja de estilos llamada estilos.css para dar formato a las visualizaciones.

rbol del proyecto ejemplo01


El archivo fuente Cliente.java es una simple clase POJO.
Cdigo: ejemplo01.Cliente.java
package ejemplo01;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Cliente implements Serializable {
private String identificador;
private String contrasena;
public Cliente()
{
}
public String getIdentificador() {
return identificador;
}
public void setIdentificador(String identificador) {
this.identificador= identificador;
}
public String getContrasena() {

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&ntilde;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&oacute;n sobre el cliente:</h4>
Identificador: ${cliente.identificador}<br/>
Contrase&ntilde;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

acciones Agregar_cliente.action yMostrar_cliente.action.


Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>ServletControlador</servlet-name>
<servlet-class>ejemplo01.ServletControlador</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletControlador</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
El modelo *.action permite no escuchar todos los URI (*), lo cual sera intil para los recursos
llamados estticos como los archivos JavaScript (.js), las imgenes u hojas de estilos CSS
(.css). Por lo tanto, nuestro controlador general ignorar estos archivos.
Podemos
probar
esta
aplicacin
http://localhost:8080/ejemplo01/Agregar_Cliente.action

con

la

siguiente

Formulario de registro de un cliente ejemplo01


Cuando el usuario confirma la creacin de un cliente, se ejecutar la siguiente URL:
http://localhost:8080/ejemplo01/ConfirmarAgregar_cliente.action

URL:

Visualizacin de la informacin del cliente ejemplo01

Proyecto MVC con filtro


La anterior aplicacin reposa sobre un Servlet de gestin que permite efectuar las acciones
adaptadas en funcin de la solicitud del internauta. Los filtros permiten dar a una aplicacin una
estructura modular. Permiten agrupar diferentes tareas que pueden ser imprescindibles para procesar
las consultas. La principal funcin de un Servlet es la de recibir las consultas y responder a los clientes
afectados.
Por contra, a menudo es necesario realizar una funcin idntica para cada Servlet en relacin con las
consultas y las respuestas HTTP. Queremos enrutar o transportar un parmetro que se encuentra en
todas las consultas sin estar obligado a escribir el cdigo para cada Servlet... La interfaz Filter que
viene incluida con el API Servlet 2.3 permite resolver este tipo de problema.
Los filtros permiten procesar:
las consultas procedentes de los clientes antes de que sean procesadas por los Servlets;
las respuestas procedentes de los Servlets antes de que no sean devueltas a los clientes.
Podemos, por ejemplo:
descifrar las consultas enviadas a los Servlets, procesar los datos con los Servlets y cifrar las
respuestas para los clientes;
gestionar la autenticacin de los clientes;
convertir los formatos de imgenes, aplicar transformaciones XSLT a los datos XML u otros.
Para utilizar un filtro es necesario realizar dos operaciones. La primera consiste en escribir una clase
que implemente la interfaz Filter. La segunda consiste en modificar el descriptor de implementacin
(archivo web.xml) de la aplicacin para indicar al contenedor que utilice el filtro.
Cuando se ha creado un filtro, el contenedor llama a su mtodo init(...). En este mtodo, podemos
acceder a los parmetros de inicializacin con la interfaz FilterConfig. Durante el procesamiento de la
consulta, el contenedor llama al mtodo doFilter(...). Por ltimo, antes de destruir el filtro, el
contenedor llama su mtodo destroy(...).
Cuando el filtro llama al mtodo chain.doFilter(), se ejecuta el siguiente filtro de la cadena. El
cdigo situado antes de chain.doFilter() se ejecuta antes del procesamiento del Servlet. Cualquier
modificacin que deba realizarse en el filtro antes de la ejecucin de la consulta debe efectuarse
antes de esta llamada. El cdigo situado despus de esta llamada se ejecuta despus del
procesamiento del Servlet. Tambin es posible encadenar los filtros y utilizar un filtro para un
procesamiento especfico.
El siguiente esquema presenta el funcionamiento de un filtro antes y despus del procesamiento por
el Servlet invocado.

Estructura de un filtro Java EE


El descriptor de implementacin web.xml permite indicar el o los filtros que se ejecutarn para cada
Servlet o URL. El primer elemento <filter>permite declarar la clase asociada al filtro. Este elemento
debe colocarse al principio del archivo de configuracin despus de la declaracin de variables
globales al contexto (<context-param>).
En nuestro caso, decidimos un filtro asociado a la clase FiltreJournalisationque permite gestionar
el acceso a las pginas del sitio.
...
<!-- definicin del filtro -->
<filter>
<filter-name>filtroregistro</filter-name>
<filter-class>cajaherramientas.FiltroRegistro</filter-class>
</filter>
...
El segundo elemento necesario es <filter-mapping>. Permite, como para los Servlets, administrar el
mapping (las relaciones) entre un nombre y un Servlet o una URL. Por ejemplo, aqu el filtro se aplica
nicamente al Servlet servletautenticacion.
...
<filter-mapping>
<filter-name>filtroregistro</filter-name>
<servlet-name>servletautenticacion</servlet-name>
</filter-mapping>
...
Con la utilizacin de un filtro, podremos ofrecer a cada recurso (dinmicos y estticos) de nuestra
aplicacin un nico controlador (un Servlet). Con el anterior ejemplo de descriptor de
implementacinweb.xml, slo los URI con el sufijo .action sern procesados por nuestro Servlet. No
gestionaremos los recursos estticos, como por ejemplo las imgenes, archivos JavaScript u hojas de
estilos.
<servlet>
<servlet-name>ServletControlador</servlet-name>
<servlet-class>ejemplo01.ServletControlador</servlet-class>
</servlet>

<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);
}
}

public void destroy()


{
this.filterConfig=null;
}
}
El cdigo del controlador es idntico desde el punto de vista algortmico. El internauta puede
visualizar el formulario de creacin y despus de la confirmacin, visualizar sus datos a travs de un
objeto cliente. El cdigo del descriptor de implementacin se modifica para declarar un filtro a la
escucha de las consultas.
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>FiltroControlador</filter-name>
<filter-class>ejemplo01.FiltroControlador</filter-class>
</filter>
<filter-mapping>
<filter-name>FiltroControlador</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Podemos
utilizar
esta
aplicacin
con
la
siguiente
http://localhost:8080/ejemplo01-2/Agregar_cliente.action

URL

para

agregar

un

cliente:

Consola y registros del filtro

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.

Funcionamiento general de Struts 2


Struts utiliza un filtro como en el caso de la aplicacin ejemplo01-2. Este filtro debe estar declarado en
el descriptor de implementacin de nuestra aplicacin web.xml. Este filtro se define en la
claseorg.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.
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">
<display-name>Struts Blank</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Con Struts, el mtodo de accin de la clase se ejecuta despus de que todas las propiedades hayan
sido procesadas y afectadas. Por su parte, un mtodo de accin devuelve una cadena de caracteres
de tipo String. Esta cadena indica a Struts donde debe redirigirse al controlador. Por ejemplo, la
cadena success indica a Struts que regrese a la pgina en caso de que el procesamiento haya sido
correcto y la cadena error determina la pgina que se mostrar en caso de error.
En la mayora de los casos, Struts redirige al usuario a una vista JSP con la ayuda de la
interfazRequestDispatcher de Java. En general, las vistas devueltas estn en formato JSP, pero
tambin pueden ser modelos de Velocity o FreeMarker. Adems, Struts tambin puede devolver un
flujo multimedia de tipo imagen, por ejemplo.
Para probar y analizar los URI, Struts utiliza el archivo de configuracin llamado struts.xml. Este
archivo de configuracin debe colocarse en el directorio /WEB-INF/src (o /WEB-INF/classes despus de
la compilacin) para que funcione por defecto. Todas las acciones se declararn en el archivo de
enrutamiento y a cada declaracin de accin le corresponde un nombre de URI. Este archivo de
configuracin struts.xml se lee al iniciar la aplicacin. Podemos, para simplificar y para evitar volver a
cargar el administrador, declarar la aplicacin en modo de desarrollo. Con esta modalidad de diseo,
el archivo se volver a cargar cada vez que haya un cambio en la aplicacin. Con esta etiqueta, no es
necesario recargar el contenedor.
Cdigo: struts.xml
...
<constant name="struts.devMode" value="true" />
...
Cada
declaracin
de accin
esta
asociada
a
una clase
completamente
cualificada
(nombrepaquete.nombreclase). En caso contrario, podemos definir una accin de Struts por defecto.
Una clase de accin deber tener al menos un tipo de cadena de caracteres, pero tambin puede
tener varios (confirmacin, error, consulta, lista...).
A la hora de acceder a un recurso o de ejecutar una accin, Struts utiliza este proceso de llamada:

Proceso de ejecucin de Struts


1) El cliente enva consultas desde URL adaptadas. Tambin puede enviar parmetros dentro de la
URL o mediante un formulario.
2) Struts consulta su archivo de configuracin struts.xml para encontrar la configuracin de la accin.
3) Se ejecuta cada interceptor asociado a la accin. Uno de estos interceptores es el encargado de
asignar automticamente los valores recibidos en la consulta con las propiedades de la clase de
accin, en funcin de los nombres (por ejemplo, identificador, contrasena).
4) Struts ejecuta el mtodo en de accin asociada a la clase.
5) Se devuelve el resultado adaptado al usuario solicitante.
Los interceptores son se ponen en marcha despus de que se ejecute el mtodo de la accin,
lo que permite a estos mismos interceptores ejecutar las operaciones sobre los parmetros
despus del procesamiento del mtodo de accin.

Los interceptores de Struts 2


Un interceptor de Struts es un filtro que puede efectuar distintos procesamientos sobre una accin. El
cdigo presente en un interceptor es modulable y puede aadirse o suprimirse directamente en el
archivo de configuracin struts.xml. Tambin es posible aadir cdigo especifico a una aplicacin sin
recompilar el framework principal.
En este punto del libro, es necesario comprender que un interceptor es un filtro que tiene una funcin
especfica y permite, por ejemplo, administrar las cookies, los parmetros HTTP, la depuracin, la
carga de archivos (upload) o incluso los alias de las acciones.

El archivo de configuracin struts.xml


Una aplicacin de Struts tiene un archivo de configuracin struts.xml el formato
predeterminado default.properties, pero tambin puede tener otros archivos de configuracin.

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">

La etiqueta <package/> debe,


en la mayora de los casos,
extender el paquete strutsdefault
definido
en
el
archivo struts-default.xml. En
este caso, todas las acciones
pueden
utilizar
los
interceptores declarados en el
archivo struts-default.xml. Los
desarrolladores pueden crear

...
</package>
...
</struts>

sus
propios
paquetes
heredando de este paquete
predeterminado que pone a
disposicin la mayora de los
interceptores tiles para la

programacin de aplicaciones de Internet.

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.

<action name="ConfirmarAgregar_Cliente" class="ejemplo04.Cliente"


method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/Mostrar.jsp</result>
</action>

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.

<result name="success" type="dispatcher">/jsp/mostrarcliente.jsp


</result>
<result>/jsp/mostrarcliente.jsp</result>

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

ejemplo de la utilizacin de los interceptores anteriores:


<package name="nombredelpaquete" extends="struts-default">
<interceptors>
<interceptor name="confirmacion" class="..."/>
<interceptor name="alias" class="..."/>
</interceptors>
<action name="nombreaccion" class="nombrepaquete.nombreclase">
<interceptor-ref name="confirmacion"/>
<interceptor-ref name="alias"/>
<result>/jsp/mostrarcliente.jsp</result>
</action>
</package>

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>

Para hacer referencia a un


grupo de interceptores, basta
con
agregar
la
etiqueta <interceptor-ref/> y
el atributo namedentro de una
accin.

<action name="nombreaccion" class="nombrepaquete.nombreclase">


<interceptor-ref name="interceptorGlobal"/>
<result>/jsp/mostrarcliente.jsp</result>
</action>

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.

Los archivos de propiedades struts.properties y


default.properties
Para administrar la configuracin de Struts, podemos utilizar las etiquetas <constant/>presentes en
el archivo de configuracin struts.xml como se ha indicado anteriormente, o bien utilizar un
archivostruts.properties.
Para ello, debemos crear un archivo struts.properties en la carpeta /WEB-INF/src (o /WEB-INF/classes)
de la aplicacin y sobrecargar los valores para las parejas claves/valores (principio de los archivos de
propiedades a Java). Estos valores por defecto se presentan en el archivo default.properties presente
en el paquete struts2-core-version.jar.
Para volver a tomar el ejemplo, podemos
archivostruts.properties:

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

struts.objectTypeDeterminer=notiger: este parmetro permite administrar la implementacin de la


clasecom.opensymphony.xwork2.util.ObjectTypeDeterminer.
struts.multipart.parser=jakarta: este parmetro permite especificar el analizador utilizado por las
consultas multi-parte (upload).
struts.multipart.saveDir: este parmetro permite especificar la carpeta por defecto para la carga de
archivos.
struts.multipart.maxSize=2097152: este parmetro permite especificar el tamao mximo para los
archivos que se cargan.
struts.custom.properties=application,org/apache/struts2/extension/custom:
especificar la lista de archivos de propiedades que deben cargarse.

este

parmetro

permite

struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper: este parmetro permite


precisar cmo se asignan las URL a las acciones. El valor por defecto de este parmetro es:
org.apache.struts2.dispatcher.mapper.DefaultActionMapper.
struts.action.extension=action: este parmetro permite administrar por una lista separada por comas,
la lista de extensiones para las acciones (por ejemplo, .action, .do).
struts.serve.static=true: este parmetro indica si Struts debe administrar los contenidos estticos
presentes en los archivos .jar.
struts.serve.static.browserCache=true: este parmetro indica si el filtro utilizado por Struts debe o no
almacenar en el cach del navegador del cliente, los contenidos estticos (imgenes, css,
javascript...).
struts.enable.DynamicMethodInvocation=true: este parmetro indica si la invocacin de mtodos
dinmicos es posible con Struts. Las invocaciones dinmicas consisten en crear URI de la siguiente
forma: agregar!Cliente.action.

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

struts.freemarker.templatesCache=false: este parmetro permite especificar si el motor de FreeMarker


debe utilizar el cach o no.
struts.freemarker.beanwrapperCache=false: este parmetro permite especificar si el cache de los beans
debe ser utilizado uno con FreeMarker.
struts.freemarker.wrapper.altMap=true: este parmetro permite precisar si el mapping est autorizado
con FreeMarker.

struts.freemarker.mru.max.strong.size=100: este parmetro permite configurar el tamao mximo para


el cach de FreeMarker.
struts.xslt.nocache=false: este parmetro permite especificar si los resultados XSLT deben utilizar el
cach.
struts.mapper.alwaysSelectFullNamespace=false: este parmetro indica si Struts debe utilizar o no el
nombre de espacio antes de la ltima barra.

El archivo de propiedades de la aplicacin web.xml


Para la administracin de la configuracin de Struts, podemos utilizar las etiquetas <constant/> y el
archivo struts.properties. Existe tambin una ltima manera de configurar Struts, por medio del archivo
de descripcin de la aplicacin web.xml.
Para ello, debemos utilizar las constantes de inicializacin del filtro con la siguiente sintaxis:
Cdigo: /WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
<init-param>
<param-name>struts.devMode</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>

El archivo de configuracin struts-default.xml


En el archivo struts-default.xml presente en el archivo struts2-core-version.jar, estn definidos los
resultados predeterminados y los interceptores. Por este motivo, podemos utilizar este archivo por
defecto sin volverlo a escribir en nuestro archivo struts.xml para que sea ms pequeo y legible.
La estructura del archivo struts-default.xml es la siguiente:
<?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>
<bean class="com.opensymphony.xwork2.ObjectFactory"
name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory"
name="struts"
class="org.apache.struts2.impl.StrutsObjectFactory" />
...
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain"
class="com.opensymphony.xwork2.ActionChainResult"/>
...
</result-types>
<interceptors>
<interceptor name="alias"
class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
...
</interceptors>
<default-interceptor-ref name="defaultStack"/>
</package>
</struts>

Nuestra primera aplicacin con Struts


Vamos a retomar nuestra aplicacin de administracin de clientes con el identificador y la contrasea
para adaptarla a Struts.

rbol del proyecto ejemplo02


El descriptor de implementacin web.xml contiene nicamente la declaracin de la utilizacin del
framework Struts.
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">
<display-name>Struts Blank</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>

<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;
}

public String getContrasena() {


return contrasena;
}
public void setContrasena(String contrasena) {
this.contrasena= contrasena;
}
public String execute()
{
System.out.println("En el mtodo de la accin");
return "success";
}
}
Gracias a Struts, ya no hay necesidad de administrar las recuperaciones de los parmetros en la
consulta (request.getParameter() o request.getAttribute()), ni de administrar los reenvos de los de
parmetros en la respuesta (request.setAttribute()). El simple hecho de utilizar los descriptores de
acceso de cada propiedad permite administrar los parmetros de manera global.
La pgina JSP /jsp/AgregarCliente.jsp tiene la siguiente estructura:
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&ntilde;a:</td>
<td><input type="text" name="contrasena"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
value="Agregar un cliente"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
Por ltimo, el archivo JSP /jsp/MostrarCliente.jsp tiene la siguiente estructura:
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>
<div id="carta">

<p>
<h4>Informaci&oacute;n sobre el cliente:</h4>
Identificador: <s:property value="identificador"/><br/>
Contrase&ntilde;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

Visualizacin del cliente de ejemplo02


Nuestra primera aplicacin con Struts ya esta operativa. Permite administrar dos atributos presentes
en un formulario. La simple declaracin de los descriptores de acceso permite administrar toda la
cadena de desarrollo (consulta/respuesta). Para probarlo, podemos simplemente suprimir el
settersetContrasena() en la clase Clientey comprobar el resultado.

Visualizacin del cliente sin el setter de la contrasea

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

rbol y archivo log4j


A continuacin, es necesario copiar el archivo de Log4J en formato .jar, log4j-version.jar en el directorio
de bibliotecas de la aplicacin /WEB-INF/lib. Ahora podemos volver a ejecutar el proyecto y comprobar
el registro en la consola Java.

Registro de la consola Java con Log4J


Podemos mostrar un nuevo formulario de registro de un cliente y confirmar la accin para visualizar el
seguimiento sin registro.

Registro de la consola Java sin Log4J

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.

Etiqueta de depuracin <s:debug/>

Visualizacin de los registros de la depuracin


En Struts, la depuracin se realiza mediante el interceptor debugging. Este interceptor est presente
en la configuracin predeterminada de Struts. Podemos ejecutar este interceptor directamente a
partir de una URL utilizando el parmetro debug=xml o debug=console. El parmetro debug=xmlpermite
visualizar un rbol XML que contiene la lista de valores de registro y los objetos.
A continuacin, se presenta un ejemplo con la ejecucin de la siguiente URL:
http://localhost:8080/exemple02/ConfirmarAgregar_Cliente.accion?debug=xml

Visualizacin del rbol de depuracin XML


Por ltimo, el seguimiento en modo consola permite visualizar una ventana con la informacin sobre la
aplicacin en curso. En esta consola, podemos introducir expresiones de tipo Object-Graph Navigation
Language (OGNL) para visualizar los parmetros de la pgina.

Consola de depuracin OGNL

Esta consola no funciona actualmente con el navegador Internet Explorer, pero s con Firefox.

Administracin de la creacin de perfiles (Profiling)


Struts incorpora por defecto una herramienta de administracin de la creacin de perfiles para
nuestras aplicaciones. La creacin de perfiles permite dar informacin precisa sobre el curso de una
accin. La creacin de perfiles permite, por ejemplo, mejorar el cdigo o encontrar una parte del
cdigo particularmente lenta durante su ejecucin. Struts proporciona un seguimiento sobre el tiempo
de ejecucin de las diferentes secciones, de sus filtros, interceptores y resultados. Cada uno de estos
servicios
utiliza
la
clase
UtilTimeStack
que
se
encuentra
en
el
paquetecom.opensymphony.xwork2.util.profiling. Por defecto, no se mostrarn los registros. Por contra,
si se ha activado el seguimiento para una accin, por ejemplo, el resultado se mostrar en modo de
registro en la consola Java o en el archivo de registro del servidor Java EE (catalina.out para Tomcat).
Para activar la creacin de perfiles con Struts, existen varias tcnicas:
1) Ejecutar una consulta con el parmetro profiling=true o profiling=yes en la URL de la aplicacin.
http://localhost:8080/ejemplo02/ConfirmarAgregar_Cliente.accion?profiling=yes
Para
detener
la
creacin
de
perfiles
de
una
aplicacin,
basta
con
pasar
del
parmetro profiling=no oprofiling=false http://localhost:8080/ejemplo02/ConfirmarAgregar_Cliente.accion?
profiling=no
2) Activar el mtodo UtilTimerStack.setActive(true)en la funcin que se va a perfilar.
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import java.io.Serializable;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
@SuppressWarnings("serial")
public class Cliente implements Serializable {
...
public String execute()
{
UtilTimerStack.setActive(true);
System.out.println("En el mtodo de la accin");
return "success";
}
}
3) Activar la constante del sistema UtilTimerStack.ACTIVATE_PROPERTY.
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import java.io.Serializable;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
@SuppressWarnings("serial")
public class Cliente implements Serializable {
...
public String execute()
{

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

mostrarse cuando se ha activado el seguimiento y no se ha prohibido por Log4J y su archivo de


propiedades.
A continuacin, se presenta un ejemplo de registro con la siguiente URL:
http://localhost:8080/ejemplo02/ConfirmarAgregar_Cliente.accion?profiling=true
INFO: [500ms] - Handling request from Dispatcher
[0ms] - create DefaultActionProxy:
[0ms] - actionCreate: ConfirmarAgregar_Cliente
[453ms] - invoke:
[453ms] - interceptor: exception
[453ms] - invoke:
[453ms] - interceptor: alias
[453ms] - invoke:
[453ms] - interceptor: servletConfig
[453ms] - invoke:
[453ms] - interceptor: i18n
[453ms] - invoke:
[453ms] - interceptor: prepare
[453ms] - invoke:
[453ms] - interceptor: staticParams
[453ms] - invoke:
[453ms] - interceptor: actionMappingParams
[453ms] - invoke:
[453ms] - interceptor: params
[453ms] - invoke:
[453ms] - interceptor: conversionError
[453ms] - invoke:
[453ms] - interceptor: validation
[125ms] - invoke:
[125ms] - interceptor: workflow
[125ms] - invoke:
[0ms] - invokeAction: ConfirmarAgregar_Cliente
[125ms] - executeResult: success
Podemos igualmente crear perfiles de las actividades especficas con los mtodos push()y pop()de la
clase esttica UtilTimerStack. El siguiente es un ejemplo para realizar el seguimiento nicamente de
los accesos a una base de datos.
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 {
...
public String execute()
{
String activite="database access";
UtilTimerStack.push(activite);
try
{
System.out.println("En el mtodo de la
accin y el acceso a la base de datos");
}
catch(Exception e)
{
UtilTimerStack.pop(activite);
}
finally
{

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");
}

Administracin de los recursos


Las clases de accin permiten el acceso a los recursos externos como los Servlets de Java. Los recursos externos son los siguientes:
la clase ServletContext;
la clase HttpSession;
la clase HttpServletRequest;
la clase HttpServletResponse.
Struts ofrece la posibilidad de acceder a estos recursos utilizando la herencia de clases o la implementacin de interfaces.

1. Acceso a los recursos por clase


Existen dos clases que permiten acceder a los recursos, com.opensymphony.xwork2.actionContext yorg.apache.struts2.ServletActionContext. La clase
esttica ServletActionContextofrece tres mtodos estticos getRequest(), getResponse()y getServletContext()para acceder a los recursos.
Un punto muy importante de la utilizacin de la clase esttica ServletActionContextes su mbito de aplicacin. De hecho, es imposible utilizar esta clase en
un constructor de una clase de accin. En el constructor, la clase de accin todava no conoce la clase esttica ya que esta ltima an no se le ha sido
pasada.
A continuacin, se muestra un pequeo ejemplo de utilizacin de la clase estticaServletActionContexten un mtodo de accin.
Cdigo: ejemplo02.Cliente.java
package ejemplo02;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
...
public String execute()
{
HttpServletRequest
request=ServletActionContext.getRequest();
HttpSession session=request.getSession();
if(session.getAttribute("cliente")==null)
{
System.out.println("Ningn cliente en la sesin");
}
else
{
System.out.println("Un cliente en la sesin");
}
ServletContext
context=ServletActionContext.getServletContext();
System.out.println("Un atributo en el contexto de
la aplicacin: "+context.getInitParameter("email"));

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>

Acceso a los recursos

2. Acceso a los recursos por interfaz

El framework proporciona cuatro interfaces para acceder a los recursos externos:


org.apache.struts2.util.ServletContextAware,
org.apache.struts2.interceptor.ServletRequestAware,
org.apache.struts2.interceptor.ServletResponseAware,
org.apache.struts2.interceptor.SessionAware.
La interfaz ServletContextAware posee un mtodo llamado setServletContext() que permite acceder al objeto ServletContext en la clase de accin. Cuando se
ejecuta una accin, el framework comprueba si la interfaz se ha implementado y si una funcin sobrecarga la declaracin. En el interior de este mtodo, debemos
asignar una variable servletContext para recuperar el objeto.
La interfaz ServletRequestAware posee un mtodo llamado setServletRequest()que permite acceder al objeto HttpServletRequest en la clase de accin. Cuando se
ejecuta una accin, el framework comprueba si la interfaz se ha implementado y si una funcin sobrecarga la declaracin. En el interior de este mtodo, debemos
asignar una variable request para recuperar el objeto.
La interfaz ServletResponseAware posee un mtodo llamado setServletResponse()que permite acceder al objeto HttpServletResponse en la clase de accin. Cuando
se ejecuta una accin, el framework comprueba si la interfaz se ha implementado y si una funcin sobrecarga la declaracin. En el interior de este mtodo, debemos
asignar una variable response para recuperar el objeto.
La interfaz SessionAware posee un mtodo llamado setSession()que permite acceder al objetoHttpSession en la clase de accin. Cuando se ejecuta una accin, el
framework comprueba si la interfaz se ha implementado y si una funcin sobrecarga la declaracin. En el interior de este mtodo, debemos asignar una
variable session para recuperar la coleccin de atributos presentes en la sesin.
Struts transmite una instancia de la clase org.apache.struts2.dispatcher.SessionMapcomo parmetro. Esta clase hereda de la clase AbstractMapque a su vez
implementa la clase Map. La claseSessionMap ofrece varios mtodos para poder manipular los objetos de tipo HttpSession. Los mtodos propuestos por la
clase SessionMapson los siguientes:
invalidate(): este mtodo permite eliminar la sesin.
clear(): este mtodo permite borrar todos los atributos del objeto HttpSession.
get(cle): este mtodo devuelve un atributo determinado por la clave configurada. Este mtodo devuelve null si el objeto no existe o no se encuentra.
put(clave, valor): este mtodo permite guardar un atributo de la sesin. Si el objetoHttpSession es null, se crear un nuevo objeto HttpSession.
remove(clave): este mtodo permite suprimir un atributo especfico de la sesin.
El siguiente ejemplo permite aplicar el acceso a los recursos externos utilizando las interfaces que ofrece Struts. El ejemplo sigue el mismo principio que el
proyecto ejemplo02. El archivo de administracin de la aplicacin struts.xml es el siguiente:
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="ejemplo03" namespace="/" extends="strutsdefault">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo03.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
<action name="Eliminar_Cliente" class="ejemplo03.Cliente"
method="eliminar">
<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>

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&ntilde;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>

La accin Agregar_Cliente permite mostrar nicamente el


formulario JSP de registro de un nuevo cliente. La
accin ConfirmarAgregar_Cliente permite agregar la informacin
del cliente a la sesin si no se ha dejado ningn campo vaco.
De lo contrario, el internauta ser redirigido a la pgina de
introduccin
de
datos.
Por
ltimo,
la
accin Eliminar_Cliente permite eliminar la informacin del
cliente de la sesin. Las expresiones de JSP Expression
Language ${identificador} y ${contrasena}presentes en el
atributo value de las etiquetas <input/> permiten conservar
los datos introducidos por el usuario.
La siguiente pgina permite visualizar los valores de los
atributos
presentes
en
la
sesin,
que
sonIdentificadorsesion y contrasenasesion. El enlace propuesto
ejecuta el mtodo eliminar() de la clase de accin para
eliminar la sesin.
Por ltimo, la clase Cliente permite administrar los atributos
del usuario en la sesin durante una autenticacin. La
clase Cliente accede a la sesin del usuario ya que se
implementa la interfazSessionAware y se sobrecarga del
mtodo setSession(Map map). La accin agregar() permite
registrar los parmetros del cliente en la sesin y el
mtodo eliminar()permite borrar esta misma informacin de
la sesin. Tambin se utiliza una comprobacin de los datos
introducidos (no vacos) en esta clase.
Podemos probar la aplicacin ejemplo03 con este enlace
haciendo pruebas con campos vacos y campos completados.
Por ltimo podemos eliminar los atributos de la sesin
utilizando un enlace adaptado.
http://localhost:8080/ejemplo03/Agregar_Cliente.action

</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&oacute;n sobre el cliente:</h4>
Identificador: <s:property
value="#session.identificadorsesion"/><br/>
Contrase&ntilde;a: <s:property
value="#session.contrasenasesion"/><br/>
</p>
<a href="Eliminar_Cliente.action">Eliminar el cliente</a>
</div>
</body>
</html>

Visualizacin de la informacin del cliente ejemplo03

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&oacute;n sobre el cliente:</h4>
Identificador: <s:property
value="#session.identificadorsession"/><br/>
Contrase&ntilde;a: <s:property
value="#session.contrasenasession"/><br/>
P&aacute;gina: <s:property value="pagina"/><br/>
</p>
<a href="Eliminar_Cliente.action">Eliminar el cliente</a>
</div>
</body>
</html>

Uso de los parmetros de accin de ejemplo03

Administracin dinmica del mapping


Un proyecto final contiene varias declaraciones de acciones y puede limitar la legibilidad y la facilidad
de mantenimiento del archivo de configuracin struts.xml. En la primera versin de Struts, el archivo
de configuracin poda contener muchas lneas y, en ocasiones, declaraciones prcticamente idnticas
(por ejemplo, administracin de los artculos, clientes, categoras...).
Para ofrecer soluciones a estos problemas, Struts ofrece ahora declaraciones de acciones en forma de
expresiones regulares o modelos llamados wildcard.
Podemos utilizar un nuevo proyecto llamado ejemplo04 y agregar una declaracin dinmica para el
formulario de creacin con el fin de mostrar cualquier prefijo introducido antes de la
frase_Cliente.action.
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="*_Cliente">
<result>/jsp/AgregarCliente.jsp</result>
</action>
...
</struts>
El carcter * permite capturar las URL compuestas de cualquier cadena de caracteres. As, podemos
ejecutar la siguiente URL http://localhost: 8080/ejemplo04/Crear_Cliente.action para mostrar el
formulario de creacin. La parte de la URL que se captura por el carcter * est disponible con el
trmino {1}. Si utilizamos varios caracteres de escape, existen tantos parmetros como caracteres de
escape ({1}, {2}, ).
As, podemos reducir considerablemente el cdigo de nuestro ejemplo utilizando los escapes y la
invocacin dinmica de mtodos.
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="*_Cliente" class="ejemplo04.Cliente"
method="{1}">
<result name="input">/jsp/cliente/Agregar.jsp</result>
<result name="success">/jsp/cliente/{1}.jsp</result>
</action>
</package>
</struts>
As,
la
URL
http://localhost:8080/ejemplo04/Agregar_Cliente.action
llamada*_Cliente y el mtodo Agregar()de la clase ejemplo04.Cliente.

ejecutar

la

accin

De igual modo, la URL http://localhost:8080/ejemplo04/ConfirmarAgregar_Cliente.action ejecutar la

accin llamada *_Cliente y el mtodo ConfirmarAgregar() de la clase ejemplo04.Cliente. Cada


mtodo de la clase Cliente devuelve, en caso de xito, a las pginas JSP del mismo nombre.
Retomamos ahora las vistas JSP /jsp/cliente/Agregar.jsp y /jsp/cliente/ConfirmarAgregar.jsp.

rbol del proyecto ejemplo04


Este ejemplo, aunque es muy til, no representa un verdadero proyecto de plataforma Web. Si
estudiamos por ejemplo una configuracin de un sistema de administracin de clientes, deberemos
declarar las siguientes acciones:
Consultar un cliente.
Mostrar el formulario de creacin de un cliente.
Validar la creacin de un cliente.
Mostrar el formulario de modificacin de un cliente.
Validar la modificacin de un cliente.
Eliminar un cliente.
Sin la utilizacin de invocaciones dinmicas, este es un ejemplo del archivo de configuracin para la
administracin de los clientes.
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>
<package name="backoffice" namespace="/admin" extends="strutsdefault">
<action name="listado_Cliente" class="ejemplo.Cliente"
method="listado">
<result>/vistas/administradores/Cliente/listadoCliente.jsp</result>
</action>
<action name="editar_Cliente" class="ejemplo.Cliente"
method="editar">
<result>/vistas/administradores/Cliente/editarCliente.jsp</result>
</action>

<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

Clases de accin genricas


En este ejemplo todo es dinmico, la llamada de clases con el parmetro {2}, la llamada de mtodos
de clases con el parmetro {1} y la invocacin de pginas JSP con la combinacin de dos parmetros.
El parmetro {0} contiene el URI recibido por la accin.

Invocacin dinmica de mtodos


Existe con Struts un carcter especial que puede utilizarse para la invocacin dinmica de mtodos. A
este carcter ! se le conoce como notacin bang.
Por defecto, la invocacin dinmica de mtodos est
propiedadesdefault.properties. La declaracin es la siguiente:

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>

rbol de ejemplo04 con invocacin dinmica

Formulario de registro con invocacin dinmica

Visualizacin del formulario con invocacin dinmica

Administracin de los resultados


Las clases de accin de Struts devuelven cadenas de caracteres en relacin con el procesamiento de
la accin. Por ejemplo, si la accin contiene los resultados success, input y error, la declaracin de la
accin en el archivo struts.xml debe tener tres resultados con los valores de los
atributos namesiguientes:
<action name="miaccion" class="nombrepaquete.nombreclase">
<result name="success">...</result>
<result name="input">...</result>
<param name="error">...</param>
</action>
El resultado se representa por medio de la etiqueta <result/>y de sus atributos, que son nameytype.
El atributo namepermite dar un nombre al resultado que corresponda al valor return de la accin. El
valor por defecto de este atributo es success. El atributo typepermite definir el tipo de resultado. El
valor por defecto de este atributo es dispatcher (redireccin simple).
Por ejemplo, las siguientes declaraciones son idnticas:
<result name="success" type="dispatcher">/jsp/MostrarCliente.jsp
</result>
<result name="success">/jsp/MostrarCliente.jsp</result>
<result>/jsp/MostrarCliente.jsp</result>
El tipo dispatcher, correspondiente a una redireccin a una pgina JSP en la mayora de los casos, es
el tipo ms utilizado. La lista propuesta a continuacin presenta todos los tipos de resultados
posibles que podemos indicar en el atributo typede la etiqueta <result/>.
Adems de estos distintos tipos de resultados, el desarrollador puede utilizar complementos
con resultados especficos o incluso desarrollar sus propios complementos con resultados
adaptados. En el captulo Complementos de Struts abordaremos la creacin de complementos con
Struts.
dispatcher: este tipo permite redirigir la accin a una pgina JSP de resultado.
redirect: este tipo permite realizar una redireccin completa a otra URL. La diferencia entre
este tipo y dispatcher reside en la forma de redireccin. Con dispatcher no se cambia la URL del
navegador y se conservan los parmetros presentes en la consulta. Con redirect se cambia la
URL en el navegador y se pierden los parmetros presentes en la consulta.
redirectAction: este tipo permite realizar una redireccin a otra accin.
chain: este tipo permite utilizar el encadenamiento de acciones (varias acciones enlazadas).
freemarker: este tipo permite utilizar el motor de plantillas FreeMarker.
velocity: este tipo permite utilizar el motor de plantillas Velocity.
httpheader: este tipo permite devolver encabezados HTTP especficos al navegador.
stream: este tipo permite devolver un resultado en forma de flujo InputStream al navegador
(imgenes, vdeos).
xslt: este tipo permite utilizar resultados XML y hojas de estilo XSLT.
plaintext: este tipo permite devolver el resultado en formato de texto para visualizar el cdigo
fuente de una pgina.

1. Redireccin con parmetros


El tipo dispatcher es el ms utilizado en los resultados. Este tipo permitir realizar una redireccin a
un recurso como una pgina JSP (o una pgina HTML/XHTML) sin perder los parmetros. Este
parmetro debe apuntar a un recurso interno del proyecto, pero no permite realizar una redireccin
a un recurso externo o una URL absoluta (por ejemplo, www.gdawj.com).
<action name="Eliminar_Cliente" class="ejemplo03.Cliente"
method="eliminar">
<result name="success"
type="dispatcher">/jsp/MostrarCliente.jsp</result>
</action>

2. Redireccin sin parmetros


El tipo redirect permite realizar una redireccin a un recurso interno o externo, pero con prdida de
parmetros. La principal ventaja de utilizar este tipo de resultado es que se puede redirigir al
usuario a un recurso externo. Este tipo de redireccin tambin es ms rpida. Por ltimo, este tipo
de redireccin se utiliza a menudo para administrar el doble envo (double submit). De hecho, con
una redireccin simple, cuando un usuario aade, por ejemplo, un producto a su compra, si por
cualquier motivo actualiza su navegador, se agregar un segundo artculo. La redireccin sin
parmetros permite eliminar los parmetros de la consulta y dirigir al usuario a otra URL.
Tambin podemos transmitir parmetros a una redireccin utilizando las propiedades. La
sintaxis${propiedad} permite transmitir los datos presentes a la clase de accin con sus descriptores
de acceso.
<result name="success" type="redirect">/jsp/MostrarCliente.jsp?
identificador=${identificador}</result>
Si tomamos un nuevo proyecto llamado ejemplo04, realizaremos una primera redireccin al motor de
bsqueda Google y una segunda a una accin del sitio. Para realizar una redireccin a un recurso
externo es necesario utilizar el tipo redirect. El archivo de configuracin de la aplicacin struts.xml es
el siguiente:
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="redirect">/jsp/MostrarCliente.jsp?identificador=$
{identificador}</result>
</action>
<action name="Google">

<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&oacute;n sobre el cliente:</h4>
Identificador: <s:property value="identificador"/>
<%=request.getParameter("identificador") %><br/>
Contrase&ntilde;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:

Formulario de registro de un nuevo cliente ejemplo04

Visualizacin de la informacin del cliente ejemplo04

3. Redireccin a una accin


El tipo redirectAction se utiliza para realizar una redireccin a otra accin de manera similar al
tiporedirect. El nombre de la accin se determina en la etiqueta <result/>sin extensin (.action). La
siguiente redireccin permite redirigir el proyecto ejemplo04 a otra accin con prdida de
parmetros.
La confirmacin del registro del cliente redirige al usuario a la accin Mostrar_Cliente.

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>

Formulario de registro de un cliente ejemplo04

Visualizacin del cliente con redireccin ejemplo04

No es necesario incluir la extensin de la accin (.action), el sufijo no es necesario. Esta


tcnica permite utilizar nombres de extensiones a su eleccin (.action, .do...).
Tambin podemos realizar una redireccin siguiendo el mismo principio anterior. Se agregarn los
parmetros a la consulta y se transmitirn en la URL, lo cual reduce las posibilidades de escritura.
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="ejemplo04.Cliente" method="agregar">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="success" type="redirectAction">
<param name="actionName">Mostrar_Cliente</param>
<param name="identificador">${identificador}</param>
</result>
</action>
...
</struts>

Redireccin con parmetros

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>

<action name="Mostrar_Cliente" class="ejemplo04.Cliente">


<result name="success">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>

Visualizacin de los parmetros con redireccin encadenada

En general, no es muy recomendable usar redirecciones encadenadas. Si una accin debe


llamar a otra accin, esto se realiza con una redireccin como respuesta a la primera accin
o bien se puede llamar al cdigo de la segunda accin en la primera.

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.

Tipos de respuestas HTTP

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>

Visualizacin en modo plainText de ejemplo04

Administracin de las excepciones


La administracin de las excepciones es una fase fundamental de un proyecto. Las pruebas permiten
poner de relieve los fallos de los programas, pero la administracin de las excepciones proporciona
seguridad de procesamiento al cdigo. Cada ejecucin de excepcin estar asociada a un error 500
HTTP del lado del servidor.
Para administrar las excepciones, Struts ofrece la etiqueta <exception-mapping/> que permite
capturar las excepciones no procesadas en las acciones. Esta etiqueta contiene dos
atributos,exception, para especificar el tipo de excepcin que se va a capturar y result, que permite
especificar el resultado que se ejecutar en caso de producirse una excepcin. Esta accin de
resultado puede especificarse de manera local en la accin o de manera global con la
etiqueta <globals-results/>.
Si
retomamos
nuestro
proyecto
ejemplo04,
podemos
agregar
voluntariamente
el
procesamientoConfirmarAgregar.action de una conversin de cadena de caracteres en entero para
ejecutar una excepcin.
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()
{
}
...
// agregar la informacin del cliente en la sesin
public String agregar()
{
// forzar la ejecucin de una excepcin
int exception=Integer.parseInt(this.contrasena);
// verificar los datos introducidos, en caso de error
devolver a la pgina de introduccin de datos
if(this.identificador.equals("") ||
this.contrasena.equals(""))
{
return "input";
}
// ningn error
else
{
return "success";
}
}
}

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>

Administracin de las excepciones

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&oacute;n<br/>P&oacute;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&oacute;n<br/> P&oacute;nganse en
contacto con el webmaster</h3>
<s:property value="exceptionStack"/>
<s:property value="exception.message"/>
</div>
</body>
</html>

Los atributos exceptionStack y exception.message

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.

Las etiquetas de formulario


Estas etiquetas XHTML se utilizan en las pginas JSP para mostrar datos. La biblioteca de etiquetas de Struts se facilita con el archivo struts2-core-x.jar. Para utilizar
esta librera en nuestras pginas JSP, debemos incluir la siguiente declaracin en la parte superior de cada pgina y aadir el prefijo<s:nombreetiqueta/>a nuestras
etiquetas:
<%@ taglib prefix="s" uri="/struts-tags" %>
Las etiquetas de Struts pueden mostrar contenido esttico o contenido dinmico con la ayuda de una expresin OGNL (Object-Graph Navigation Language). Las
etiquetas de Struts se declaran en el paquete org.apache.struts2.components y se basan en declaraciones JSTL corrientes. En funcin del tipo de atributo utilizado
(visualizacin, lectura, presentacin, etc.), existen diversos parmetros, como la clase de CSS utilizada para la presentacin, el nombre, el ttulo o las acciones
JavaScript asociadas.

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>

Las etiquetas de Struts cuentan con numerosos parmetros


utilizados habitualmente para la presentacin, los estilos CSS
que se van a aplicar o las acciones JavaScript asociadas. La
etiqueta<s:form/>es traducida automticamente por el motor
y transformada en tabla. Struts realiza automticamente la
presentacin. La alineacin y el estilo son gestionados por los
atributos de cada etiqueta. Podemos mostrar el cdigo fuente
de esta pgina con el fin de observar el cdigo XHTML
generado por Java.

Cdigo fuente generado: /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 id="ConfirmarAgregar_Cliente" name="ConfirmarAgregar_Cliente"
action="/ejemplo05/ConfirmarAgregar_Cliente.action" method="post">
<table class="wwFormTable">
<tr>
<td class="tdLabel"><label
for="ConfirmarAgregar_Cliente_identificador"
class="label">Identificador:</label></td>
<td><input type="text" name="identificador" value=""
id="ConfirmarAgregar_Cliente_identificador"/></td>
</tr>
<tr>
<td class="tdLabel"><label
for="ConfirmarAgregar_Cliente_contrasena" class="label">
Contrasena:</label></td>
<td><input type="text" name="contrasena" value=""
id="ConfirmarAgregar_Cliente_contrasena"/></td>
</tr>
<tr>
<td colspan="2"><div align="right"><input type="submit"
id="ConfirmarAgregar_Cliente_0" value="Agregar el cliente"/>
</div>
</td>
</tr>
</table>
</form>
</div>
</body>
</html>

2. Los temas de presentacin y el atributo theme


Como hemos indicado anteriormente, por defecto Struts utiliza un tema de presentacin que permite generar una tabla XHTML para mostrar el contenido.
Resumiendo, la etiqueta <s:form/> se transforma en <form><table/></form>. Esta prctica presentacin permite desarrollos rpidos y adaptados a las partes
administrativas de los sitios Web pero no obligatoriamente a las partes pblicas. En realidad, existe tendencia a utilizar presentaciones optimizadas con
etiquetas <span/>y<div/>, as como a aplicar hojas de estilo de forma masiva.
Con Struts, las presentaciones, denominadas tambin plantillas, se generan con FreeMarker, que analizaremos en el captulo reservado a esta herramienta. Los
temas propuestos por FreeMarker son los siguientes:
xhtml: es el tema predeterminado. Este modelo permite mostrarlos formularios en forma de tabla XHTML.
simple: este tema permite traducir todas las etiquetas de Struts de la forma ms simple, es decir, sin formato para la visualizacin. Con este tema, las
etiquetas se traducirn sin modificacin.
css_xhmtl: este tema permite traducir las etiquetas con un modelo adaptado a las hojas de estilo CSS, es decir, con las etiquetas <span/>y <div/>.
ajax: este tema permite facilitar un modelo para la utilizacin de Ajax.
Los temas pueden utilizarse en el nivel ms alto de la jerarqua, en la etiqueta <s:form/>, o a nivel local en cada etiqueta XHTML (por ejemplo: <s:textfield/>). Por
defecto se utiliza el tema xhtml, por lo que no es necesario especificarlo. Si deseamos cambiar el tipo, podemos utilizar el atributotheme de cada etiqueta de
presentacin.

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>

Formulario de registro de un cliente con el tema simple

Formulario de adicin de un cliente con el tema css_xhtml


Por supuesto, podemos definir nuestros propios temas con
FreeMarker. Tambin podemos utilizar un tema para toda la
pgina haciendo uso de un atributo denominado themecon el
alcance page,request, session o application. Adems, es posible
asignar
un
tema
a
todo
el
proyecto
con
la
propiedad struts.ui.theme presente en el archivo de
propiedades struts.properties.

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>

Cdigo fuente generado: /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 id="ConfirmarAgregar_Cliente" name="ConfirmarAgregar_Cliente"
action="/ejemplo05/ConfirmarAgregar_Cliente.action" method="post">
<div id="wwgrp_ConfirmarAgregar_Cliente_identificador" class="wwgrp">
<div id="wwlbl_ConfirmarAgregar_Cliente_identificador" class="wwlbl">
<label for="ConfirmarAgregar_Cliente_identificador" class="label">
Identificador:
</label></div> <br /><div
id="wwctrl_ConfirmarAgregar_Cliente_identificador" class="wwctrl">
<input type="text" name="identificador" value=""
id="ConfirmarAgregar_Cliente_identificador"/></div> </div>
<div id="wwgrp_ConfirmarAgregar_Cliente_contrasena" class="wwgrp">
<div id="wwlbl_ConfirmarAgregar_Cliente_contrasena" class="wwlbl">
<label for="ConfirmarAgregar_Cliente_contrasena" class="label">
Contrasena:
</label></div> <br /><div
id="wwctrl_ConfirmarAgregar_Cliente_contrasena" class="wwctrl">
<input type="text" name="contrasena" value=""
id="ConfirmarAgregar_Cliente_contrasena"/></div> </div>
<div align="right" id="wwctrl_ConfirmarAgregar_Cliente_0"><input
type="submit" id="ConfirmarAgregar_Cliente_0" value="Agregar el
cliente"/>
</div>
</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" %>

Utilizacin de la etiqueta <s:submit/>

<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>

Utilizacin de la etiqueta <s:textarea/>

10. La etiqueta <s:checkbox/>


La etiqueta <s:checkbox/> permite crear una casilla de verificacin HTML Esta etiqueta de Struts permite gestionar de manera dinmica el estado de la casilla
(seleccionada o no). La respuesta enviada a la clase de accin es false si la casilla no est seleccionada, y true en caso de estarlo. Las etiquetas checkbox van
acompaadas de una etiqueta HTML hidden.
Si la etiqueta no est seleccionada, se presenta la etiqueta sin el parmetro checkbox. Todos los atributos deben contener sus descriptores de acceso en la clase
de accin para poder funcionar.
Cdigo: ejemplo05.Cliente.java
package ejemplo05;

Tambin es posible utilizar colecciones Java para gestionar


listas de casillas de verificacin. As, podemos crear una lista
con los objetos que se mostrarn en cada casilla de
verificacin con el texto y el estado asociado.

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>

11. La etiqueta <s:select/>


La etiqueta <s:select/>permite crear una lista desplegable HTML Este tipo de componente es bastante difcil de gestionar en la programacin clsica cuando se
realiza el posicionamiento del tem adecuado en la lista. El atributo utilizado por Struts para gestionar los valores transmitidos se denomina list. Este atributo se
utiliza igualmente para los botones radio, que tambin representan las opciones.
El tipo de este atributo list puede ser String, una enumeracin java.util.Enumeration, un elemento de iteracin java.util.Iterator, una coleccin java.util.Map o
HashMap, ArrayList... Podemos utilizar el ejemplo anterior con una coleccin.
Cdigo: ejemplo05.Cliente.java
package ejemplo05;
import java.util.HashMap;
import java.util.Map;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private boolean Boletin;
private Map<Integer,String> listaProfesiones=new
HashMap<Integer,String>();
private int profesion;

public Cliente()
{
}
public int getProfesion() {
return profesion;
}
public void setProfesion(int profesion) {
this.profesion = profesion;

La etiqueta <s:select/> contiene el atributo label que


corresponde al texto que se mostrar en la pgina HTML. El
atributo listpermite crear el vnculo con la informacin que se
debe mostrar en la lista. Por ltimo, el atributo name permite
nombrar la lista desplegable. El atributo list est asociado
con
los
descriptores
de
acceso
presentes
en
la
clase Client permitiendo crear el vnculo con los valores
automticamente. El atributo name est asociado con los
descriptores de acceso de todo el tipo presentes en la
clase Client, permitiendo crear el vnculo con la eleccin del
cliente.
De esta forma, ante la introduccin de datos o errores en los
formularios, la visualizacin se posicionar automticamente
en el tem adecuado de la lista desplegable.
Vamos
a
utilizar
un
segundo
ejemplo
con
la
clase Profesion asociada al cliente y una lista desplegable
denominada
profesion
asociada
a
una
coleccin listaProfesiones de tipo Lista. La direccin para
acceder
a
la
aplicacin
es
la
siguiente:
http://localhost:8080/ejemplo05/Agregar_Cliente.action

Utilizacin de la etiqueta <s:select/>

}
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 de gestin de las profesiones


class Profesion
{
private int idProfesion;
private String nombre;
public Profesion(int idProfesion, String nombre)
{
this.idProfesion=idProfesion;
this.nombre=nombre;
}
public int getIdProfesion() {
return idProfesion;
}
public void setIdProfesion(int idProfesion) {
this.idProfesion=idProfesion;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre=nombre;
}
}

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>

12. La etiqueta <s:optgroup/>


La etiqueta <s:optgroup/>permite, junto con la etiqueta <s:select/>, crear una lista desplegable con grupos de datos en formato HTML. Este tipo de componente,
al igual que las listas simples, es bastante difcil de gestionar en la programacin clsica con el fin de recolocar la lista en el tem adecuado cuando se realiza una

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;

Utilizacin de la etiqueta <s:optgroup/>

La gestin de la etiqueta <s:optgroup/> no requiere la


utilizacin del setter asociado.

@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";
}
}
}

// Clase de gestin de las profesiones


class Profesion
{
private int idProfesion;
private String nombre;
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>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>

13. La etiqueta <s:radio/>


La etiqueta <s:radio/>permite utilizar botones radio HTML. Los botones radio con Struts recogen el principio de uso de las listas. Cada botn radio se representa
en una lista y la eleccin nica es gestionada por el framework con el fin de aligerar el trabajo del desarrollador. El principio de utilizacin es idntico al de las listas,

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";
}
}
}

// Clase de gestin de las profesiones


class Profesion
{
private int idProfesion;
private String nombre;
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>Agregar un cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>

Utilizacin de la etiqueta <s:radio/>

</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>

14. La etiqueta <s:checkboxlist/>


La etiqueta <s:checkboxlist/>permite crear una casilla de verificacin HTML. Estos componentes utilizan tablas de cadenas de caracteres o una coleccin de tipos
primitivos. Cuando se valida una casilla de verificacin, la propiedad asociada en la clase de accin se inicializa, por lo que es necesario implementar una tabla de
nmeros enteros para gestionar la eleccin del usuario.
Vamos a continuar nuestro ejemplo con casillas de verificacin para la gestin de las comidas del cliente.
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 int[] comida;
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();
private List<Comida> listaComida=new ArrayList<Comida>();
public Cliente()
{
}
// getter y setter
public List<Comida> getListaComida() {
listaComida.add(new Comida(1, "Comida"));
listaComida.add(new Comida(2, "Cena"));
return listaComida;
}
public void setListaComida(List<Comida> listaComida) {
this.listaComida = listaComida;
}
public int[] getComida() {
return comida;
}
public void setComida(int comida[]) {
this.comida = comida;
}
// 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 de gestin de las profesiones
class Profesion
{
private int idProfesion;
private String nombre;
public Profesion(int idProfesion, String nombre)
{
this.idProfesion=idProfesion;
this.nombre=nombre;
}
// getter y setter

Utilizacin de la etiqueta <s:checkboxlist/>

}
//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>

15. Otras etiquetas de los formularios de Struts


Struts propone otras etiquetas para facilitar la configuracin de componentes HTML. Volvemos a encontrar la etiqueta <s:combobox/> que propone una
etiqueta <input/>asociada a una lista desplegable <select/>, esta etiqueta permite enviar el texto y el valor de la etiqueta <option/>de la lista desplegable. La
etiqueta <s:updownselect/>permite crear listas mltiples con la posibilidad de clasificar los elementos. La etiqueta <s:optiontransferselect/> permite crear un
objeto complejo con una lista principal y una lista de destino con el fin de desplazar los tems de la primera lista a la segunda. Por ltimo, la
etiqueta <s:doubleselect/>permite crear dos listas desplegables HTML asociadas. Las elecciones en la primera lista provocan modificaciones en la segunda lista.
Todas estas etiquetas, aunque en ocasiones son tiles, no se utilizan con frecuencia en el desarrollo de Internet. Por experiencia, es preferible desarrollar
diferentes herramientas en DHTML con bibliotecas JavaScript o Ajax (como JQuery o Prototype).

Las etiquetas de control


Las etiquetas de Struts estudiadas anteriormente permiten gestionar la presentacin y visualizacin
de los datos en las vistas. No obstante, el framework facilita tambin un conjunto de etiquetas para la
gestin de los accesos a los datos, las condicionales o los bucles de programacin, as como para la
manipulacin de las propiedades de la aplicacin.

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>

Utilizacin de la etiqueta <s:property/>

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

<s:action var="objeto" name="miAccionAEjecutar"/>

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&aacute;metro a
una acci&oacute;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&oacute;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"/>

Fecha: <s:property value="#fecha"/><br/>


Fecha ES: <s:date name="#fecha" var="formato_es"
format="dd/MM/yyyy"/><s:property value="formato_es"/><br/>
Fecha EN: <s:date name="#fecha" var="formato_en"
format="yyyy-MM-dd"/><s:property value="formato_en"/><br/>
</div>
</body>
</html>

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>

Utilizacin de la etiqueta <s:set/>

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&aacute;metro a
una acci&oacute;n</s:a>

10. Las etiquetas <s:if/>, <s:else/> y <s:elseif/>


Las etiquetas <s:if/>, <s:else/> y <s:elseif/> se utilizan para realizar pruebas condicionales.
Estas etiquetas permiten comprobar si un atributo es nulo.
<s:if test="#profesion.id==null">
Sin profesion
</s:if>
<s:else>
Profesion <s:property value="#profesion.id"/>
</s:else>

11. La etiqueta <s:iterator/>


La etiqueta <s:iterator/>permite recorrer una tabla o una coleccin Java. Retomaremos el ejemplo
de las profesiones para mostrar los valores.
Cdigo: ejemplo06.Profesion.java
package ejemplo06;
public class Profesion {
private int id;
private String nombre;
public Profesion()
{
}
public Profesion(int id, String nombre)
{
this.id=id;
this.nombre=nombre;
}
// getter y setter
}

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 {

private String identificador;


private String contrasena;
private String emailWebmaster="[email protected]";
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 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: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>

Utilizacin de la etiqueta <s:iterator/>


La etiqueta <s:iterator/>permite realizar bucles en los valores con el fin de simular el recorrido de
los mismos. Este segundo ejemplo de la vista JSP permite mostrar una lista numerada.
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>
<ul>
<s:iterator value="{1,2,3,4,5,6,7,8,9}" status="status">
<li><s:property/> - <s:property
value="#status.index"/> - <s:property value="#status.count"/>
- <s:property value="#status.even"/> - <s:property
value="#status.odd"/> - <s:property value="#status.last"/>
</li>
</s:iterator>
</ul>
</div>
</body>
</html>

Utilizacin de la etiqueta <s:iterator/>

12. La etiqueta <s:append/>


La etiqueta <s:append/>permite concatenar elementos de iteracin para las listas de datos. Esta
etiqueta se utiliza principalmente para crear una nueva lista a partir de otras dos.
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>
<br/>
<s:set var="lista1" value="{informtico,formador}"/>
<s:set var="lista2" value="{administrador
de red,programador}"/>
<s:append var="listacompleta">
<s:param value="lista1"/>
<s:param value="lista2"/>
</s:append>
<s:iterator value="#listacompleta">
<s:property/><br/>
</s:iterator>
</div>
</body>
</html>

Utilizacin de la etiqueta <s:append/>

13. La etiqueta <s:sort/>


La etiqueta <s:sort/>se utiliza para clasificar los componentes de un elemento de iteracin de una
coleccin.
Cdigo: ejemplo06.Cliente.java
package ejemplo06;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private String emailWebmaster="[email protected]";
private int profesion;
private List<Profesion> listaProfesiones=new
ArrayList<Profesion>();

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.

Acceso a las propiedades


El acceso a los datos presentes en los archivos de propiedades mediante los mtodos dedicados
como getText(...)se realiza en el orden siguiente:
A un archivo de propiedades que tenga el mismo nombre que la clase de accin y est
presente en el directorio /WEB-INF/clases al mismo nivel que la clase de accin.
Por
ejemplo:
la
clase
propiedadesCliente.properties.

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

A un archivo de propiedades presente en el paquete predeterminado del proyecto. Por


ejemplo: paquete ejemplo07, /ejemplo07/recursos.properties.
A un archivo de propiedades presente en un paquete secundario de la aplicacin. Por
ejemplo: ejemplo07.recursos.
Para mostrar los datos presentes en el archivo de propiedades, podemos utilizar la
etiqueta<s:property/> o el atributo label de las etiquetas asociadas al mtodo getText(...): %
{getText(clave)}. Vamos a utilizar de nuevo el ejemplo anterior de gestin de las cuentas clientes
con el fin de manipular los mensajes del archivo de propiedades.
El nuevo proyecto utilizado est disponible en la siguiente direccin:
http://localhost:8080/ejemplo07/Agregar_Cliente.action

rbol del proyecto ejemplo07

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

Agregar un cliente ejemplo07


Podemos observar que en nuestro ejemplo, el acceso a los datos se realiza en el
archivo/ejemplo07/package.properties presente en el paquete predeterminado y al que por tanto
podrn acceder todas las clases de dicho paquete. Este archivo est compuesto de parejas
clave/valor en formato de texto. En las vistas, el acceso a dichos datos se realiza de dos formas, con
la etiqueta<s:property/>y con el atributo label:
<s:property value="%{getText(cliente.agregar)}"/>
<s:textfield name="identificador" id="identificador" label="%
{getText(cliente.identificador)}"/>
Esta aplicacin, aunque monolinge, permite gestionar de forma sencilla los mensajes simplemente
editando los archivos de propiedades estticos sin compilar de nuevo los archivos de origen.

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

Visualizacin de la informacin del cliente ejemplo07


Ahora vamos a utilizar un segundo archivo de propiedades package_en.properties para gestionar la
visualizacin en ingls. Para ello, simplemente copiamos/pegamos el archivo con las traducciones
asociadas.
Cdigo: /ejemplo07/package_en.properties
cliente.agregar=Add a customer
cliente.identificador=Login
cliente.contrasena=Password
cliente.mostrar=Show a customer
cliente.bienvenida=Hello {0}
Antes de comprobar sus caractersticas, configuraremos nuestro navegador en ingls. Para ello, en
Firefox utilizamos el men Herramientas - Opciones - Contenido - Idiomas - Seleccionar y el
idioma:Ingls. Para que estos cambios tengan efecto, es necesario cerrar el navegador y abrir una
nueva ventana.

Modificacin del idioma predeterminado del navegador


Ahora podemos abrir de nuevo el navegador e iniciar la pgina del formulario del cliente. As, el sitio
utilizar automticamente las traducciones presentes en el archivo asociado a la configuracin
regional _en (package_en.properties).

Formulario de cliente ingls ejemplo07

El principio es idntico independientemente del nmero de idiomas utilizados. Tendremos

tantos archivos de propiedades con el sufijo correspondiente a la configuracin regional adaptada


como idiomas utilizados en la aplicacin.
La etiqueta <s:i18n/>permite cargar de manera dinmica recursos o bundles. Esta tcnica se utiliza
principalmente para asociar una clave a un objeto complejo dinmico que no sea una cadena de
caracteres. La etiqueta <s:i18n/> contiene un atributo name que permite especificar el bundle que
desea que se utilice.
Para ilustrar nuestro ejemplo, vamos a definir una clase denominada MisRecursos.javaque devolver
un contenido de tipo complejo adaptado al tipo de autenticacin. As, si el usuario conectado lo hace
en espaol, la fecha de conexin se mostrar automticamente con el formato dd/mm/aaaa, y si lo
hace en ingls, la fecha de conexin se mostrar con el formato aaaa-mm-dd.
Las clases de utilizacin de los
clasejava.util.ListResourceBundle.

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

>: <s:property value="contrasena"/><br/>


<s:i18n name="ejemplo07.MisRecursos">
<s:text name="cliente.bienvenida">
<s:param name="identificador"><s:property
value="identificador"/></s:param>
</s:text>
</s:i18n>
</p>
</div>
</body>
</html>

Visualizacin del cliente ejemplo07


Ahora, si deseamos utilizar un mensaje de autenticacin adaptado para el ingls (configuracin
regional en), debemos crear una clase con el mismo nombre pero con el sufijo de la configuracin
regional (MisRecursos_en.java).
Cdigo: ejemplo07.MisRecursos_en.java
package ejemplo07;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ListResourceBundle;
public class MisRecursos_en extends ListResourceBundle{
// objeto fecha y presentacin
Date fecha=new Date();
SimpleDateFormat fechadia=new SimpleDateFormat("yyyy-mm-dd");
// devolver el contenido adaptado
public Object[][] getContents()
{
return traducciones;
}
// contenido dinmico (clave/valor)
Object[][] traducciones= {
{"cliente.bienvenida","Hello {0} - connection:
"+fechadia.format(fecha)}
};

Visualizar el cliente ejemplo07

Seleccin dinmica de archivos


Los archivos de propiedades utilizados hasta ahora se cargan en funcin de la configuracin regional
del navegador y por tanto de la seleccin del idioma. Hemos utilizado las propiedades del navegador
para realizar la eleccin del idioma. No obstante, esta manipulacin requiere que se cargue de nuevo
el navegador. En una aplicacin profesional, los idiomas deben poder modificarse de manera dinmica,
por ejemplo a travs de pequeas banderas que representen los idiomas. Para ello, la
claseActionSupportpropone una solucin de internacionalizacin.
Para poder cambiar de manera dinmica el idioma de la aplicacin, Struts facilita el
parmetrorequest_locale. Vamos a utilizar este parmetro para cambiar el idioma a travs de un
vnculo HTML. Estos vnculos se situarn en la primera accin y en la vista asoaciada AgregarCliente.jsp.
Por supuesto, podramos utilizar cualquier pgina JSP o JSPF para gestionar los idiomas (por
ejemplo:encabezado.jspf).
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">
<s:url action="Agregar_Cliente" id="urlIdiomaES">
<s:param name="request_locale">es</s:param>
</s:url>
<s:url action="Agregar_Cliente" id="urlIdiomaEN">
<s:param name="request_locale">en</s:param>
</s:url>
<ul>
<li><s:a href="%
{urlIdiomaES}">Espaol</s:a></li>
<li><s:a href="%{urlIdiomaEN}">Ingls</s:a></li>
</ul>
<br/>
<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>
La primera parte del cdigo permite nicamente crear vnculos HTML con los parmetros.
Puede sustituirse por las lneas siguientes: <ul><li><a href="Agregar_Cliente.action?
request_locale=es">Espa&ntilde;ol</a></li><li><a
href="Agregar_Cliente.action?
request_locale=en">Ingl&eacute;s</a></li></ul>

Gestin de idiomas con la etiqueta <s:i18n/>

Acceso a los recursos de las clases


En ocasiones, durante el desarrollo necesitamos acceder a los recursos de los archivos de
propiedades de las clases de una aplicacin para gestionar los mensajes de error de confirmacin.
Como hemos comentado anteriormente, todas las clases herederas de la clase ActionSupportpueden
utilizar los archivos de propiedades. El acceso a las parejas de datos clave/valor se realiza a travs
de la funcin getText(...)estudiada con anterioridad.
Vamos a modificar nuestra clase Cliente.javacon el fin de agregar un mensaje dinmico posterior a
la autenticacin del cliente. Para ello, utilizaremos una variable de clase denominada mensaje as
como su getter asociado para acceder a su valor en las vistas JSP.
Cdigo: ejemplo07.Cliente.java
package ejemplo07;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Cliente extends ActionSupport {
private String identificador;
private String contrasena;
private String mensaje;
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
{
// mensaje dinmico presente en el archivo
de propiedades
this.mensaje=getText("cliente.inicio");
return SUCCESS;
}
}
}

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>

<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:property value="mensaje"/>
</p>
</div>
</body>
</html>

Visualizacin de la informacin del cliente con acceso a los mensajes

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.

rbol del proyecto ejemplo08

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.

Formulario de adicin de cliente y validacin de datos introducidos


Ahora,
podemos
mejorar
la
visualizacin
utilizando
la
etiqueta
propuesta
por
Struts <s:fielderror/>con el fin de mostrar los mensajes de error en las vistas adaptadas. De esta
forma, la etiqueta se aade con un estilo CSS adaptado para la presentacin.
Cdigo: /css/estilos.css
#mensaje_error
{
margin-top:16px;
margin-bottom:0px;
margin-left:40px;
padding-bottom:2px;
padding-top:2px;
padding-left:20px;

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>

Formulario de adicin de cliente y hoja de estilos CSS

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>

Formulario de cliente y validacin de la longitud de los campos

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>

Formulario de cliente y validacin del e-mail

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>

Formulario de cliente y gestin de un espacio de validacin

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 {

private String identificador;


private String contrasena;
private int edad;
// 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="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>

Formulario de cliente y validacin de la edad

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.

Por ejemplo, la definicin del identificador presente


ConfirmarAgregar_Cliente-validation.xml es la siguiente:

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/>.

Aplicacin de un ejemplo completo


En el ejemplo anterior, hemos utilizado una clase de accin ClienteAccion, as como todos sus
descriptores de acceso, y en ocasiones una clase interna como Profesion. Esta tcnica, aunque
totalmente eficaz, no es demasiado rpida y requiere muchas lneas de cdigo en la clase de accin.
Vamos a utilizar un nuevo proyecto ejemplo09 con una clase de accin ClienteAccion y dos
JavaBeans, Cliente.java y Profesion.java. En la clase de accin ClienteAccion, no se declarar cada
propiedad en conformidad con sus descriptores de acceso sino el propio objeto cliente. Por tanto slo
habr un getter y un setter para trabajar con el objeto cliente y no existirn descriptores de acceso
para cada propiedad. Adems, las vistas y archivos de validacin se adaptarn en consecuencia.
El archivo de propiedades tambin se utiliza para la gestin de los mensajes y los idiomas. El primer
archivo package.properties contiene los mensajes informativos, as como los textos asociados a las
validaciones. El segundo archivo package_en.properties es idntico al primero pero contiene las
traducciones de los mensajes. Este archivo de propiedades es avanzado y utiliza mtodos de Struts
con el fin de mostrar de manera dinmica el nombre de los campos concernidos por una validacin con
el mtodo getText(...). Por ejemplo, la lnea siguiente permite mostrar el mensaje de error durante
una validacin estableciendo el vnculo con el nombre del campo de forma dinmica.
cliente.identificador=Identificador
campoobligatorio=El campo ${getText(fieldName)} es obligatorio
En caso de error en el campo cliente.identificador, el mtodo ${getText(fieldName)} sustituir el
campo asociado y mostrar el mensaje siguiente: El campo Identificador es obligatorio.
La aplicacin permite agregar un nuevo cliente a partir de un identificador, una contrasena y la
eleccin de la profesion en una lista dinmica. La clase de accin devuelve esta lista y el mecanismo
de descriptor de acceso permite establecer el vnculo entre las diferentes propiedades de la
aplicacin. Por ltimo, la pgina JSP /jsp/MostrarCliente.jsp permite mostrar los datos introducidos por
el cliente en la interfaz a travs del acceso al objeto cliente.

rbol del proyecto ejemplo09

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="ejemplo09" namespace="/" extends="struts-default">
<default-action-ref name="Agregar_Cliente" />
<action name="Agregar_Cliente"
class="ejemplo09.ClienteAccion">
<result>/jsp/AgregarCliente.jsp</result>
</action>
<action name="ConfirmarAgregar_Cliente"
class="ejemplo09.ClienteAccion">
<result name="input">/jsp/AgregarCliente.jsp</result>
<result name="SUCCESS">/jsp/MostrarCliente.jsp</result>
</action>
</package>
</struts>

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;
}

public void setListaProfesiones(List<Profesion>


listaProfesiones) {
this.listaProfesiones = 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&eacute;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>

Formulario de cliente con validaciones

Gestin de mensajes de error y SUCCESS


Los ejemplos presentados en las validaciones y la internacionalizacin utilizan colecciones para
mostrar los mensajes de error durante las validaciones. Struts gestiona estos mensajes de manera
dinmica y stos permiten mostrar textos independientes o insertados en los archivos de
propiedades.
En Struts, existen tres tipos de mensajes asociados a colecciones de datos:
actionErrors contiene la lista de mensajes de error correspondiente al tratamiento de las
acciones en asociacin con la variable errorMessages.
fieldErrors contiene la lista de mensajes de error correspondiente al tratamiento de las
validaciones de los campos de formularios en asociacin con la variable errors.
actionMessages contiene la lista de mensajes de confirmacin correspondiente al tratamiento.
As, podemos gestionar estos tres tipos de mensajes con los archivos de validacin XML, pero tambin
en forma de software, con los mtodos de programacin.
Para agregar un error de accin actionErrors, se debe agregar el mtodo siguiente a las clases de
accin:
addActionError("Texto para justificar el error");
Ahora, es necesario utilizar la etiqueta de Struts adaptada para mostrar los errores de accin en la
vista:
<s:actionerror/>
Para agregar un error de validacin fieldErrors, se debe agregar el mtodo siguiente a las clases de
accin:
addFieldError("campo", "Texto para justificar el error de
validacin");
Ahora, es necesario utilizar la etiqueta de Struts adaptada para mostrar los errores de accin en la
vista:
<s:fielderror/>
Para agregar un mensaje de confirmacin actionMessages, se debe agregar el mtodo siguiente a las
clases de accin:
addActionMessage("Texto para justificar el SUCCESS");
Ahora, es necesario utilizar la etiqueta de Struts adaptada para mostrar los mensajes de confirmacin
en la vista:
<s:actionmessage/>
A continuacin, mejoraremos nuestro proyecto ejemplo09 para comprobar si la creacin del cliente se
corresponde con el identificador jlafosse. De no ser as, se agregar un mensaje de error dinmico,
adems del mensaje del campo cliente.identificador, o si no se mostrar un mensaje de SUCCESS. Las
vistas se modifican para mostrar los mensajes adaptados.
La clase de accin ClienteAccioncuenta con un nuevo mtodo verificar()configurado en el archivo
de configuracin struts.xml.
Cdigo: struts.xml
...
<action name="ConfirmarAgregar_Cliente"
class="ejemplo09.ClienteAccion" method="verificar">

<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&ntilde;ol</a></li>
<li><a href="Agregar_Cliente.action?
request_locale=en">Ingl&eacute;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

cliente.profesion.idProfesion=Profesin del cliente


cliente.mostrar=Mostrar un cliente
error=Se han producido los errores siguientes:
erroraccion=Se han producido los errores de accin siguientes:
campoobligatorio=El campo ${getText(fieldName)} es obligatorio
campominimo=El campo ${getText(fieldName)} debe tener entre $
{getText(minLength)} y ${getText(maxLength)} caracteres
campologin=El campo no est asociado a una cuenta
erroraccion.login=El campo no est asociado a una cuenta correcta
para la aplicacin
correcto=El formulario se ha completado correctamente

Formulario de cliente y validaciones avanzadas multilinges

Visualizacin del cliente tras la validacin

Los mensajes de error se muestran a travs de la etiqueta <s:fielderror/> en el cuadro


situado en la parte superior de la pgina, pero tambin aparecen automticamente junto a
cada campo del formulario. Esta presentacin es gestionada por el tema predeterminado de la
pgina xhtml.
En nuestro formulario, el tema no se ha precisado, por lo que se utiliza el tema predeterminado,xhtml.
Con este modo de visualizacin en forma de tabla HTML, los mensajes de error se asocian
automticamente a los campos. Para desactivar este servicio, podemos cambiar el tema y utilizar el
tema simple, que no aplica ningn tipo de formato. Podemos precisar este tema en la
etiqueta<s:form/>de nuestra pgina JSP AgregarCliente.jsp.
<s:form method="post" action="ConfirmarAgregar_Cliente"
theme="simple">

Formulario de cliente y tema simple

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.

1. La interfaz Validator y las clases ValidatorSupport y FieldValidatorSupport


Para poder sobrecargar el mtodo validate(Object objetoavalidar), es ms sencillo heredar de la
clase ValidatorSupport o FieldValidatorSupport que implementar la interfaz Validator. La
claseValidatorSupport se utiliza para definir validaciones complejas (de tipo accin) y la
claseFieldValidator se hereda para las validaciones de los campos de formularios. Si nuestro
validador debe utilizar parmetros, el principio es idntico al de las clases de accin. En realidad,
basta con declarar la variable en la clase validador y definir su getter y setter.
La clase ValidatorSupportincluye varios mtodos para la gestin de las validaciones:
getFieldValue(String nombre, Object objeto): permite devolver el valor de un campo
especificado.
addActionError(Object actionError): permite agregar un error de tipo accin.
addFieldError(String nombre, Object objeto): permite agregar un error de campo.

2. Declarar los validadores


Los validadores se declaran de manera estndar en el archivo default.xml presente en la clase
siguiente: com.opensymphony.xwork2.validator.validatorsque se encuentra en el archivoxwork.jar.
Durante la creacin de nuevos validadores, es necesario declarar los mismos en un
archivo validators.xml presente en el directorio /WEB-INF/clases o /WEB-INF/src de la aplicacin.
A
continuacin
se
muestra
el
contenido
archivocom.opensymphony.xwork2.validator.validators.default.xml incluido con Struts:
<?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-config1.0.dtd">
<!-- START SNIPPET: validators-default -->
<validators>
<validator name="required"
class="com.opensymphony.xwork2.validator.validators.RequiredField
Validator"/>
<validator name="requiredstring"
class="com.opensymphony.xwork2.validator.validators.RequiredString
Validator"/>
<validator name="int"
class="com.opensymphony.xwork2.validator.validators.IntRangeField
Validator"/>

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.

rbol del proyecto ejemplo10

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

// mtodo para agregar un mensaje de confirmacin


public String verificar()
{
addActionMessage(getText("correcto"));
return SUCCESS;
}
}

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

Formulario de cliente multilinge con validaciones personales

Visualizacin del cliente tras la validacin

4. Validacin en las clases de accin


El prrafo anterior explica la aplicacin de una validacin XML para los campos utilizados de manera
declaratoria. Struts incluye tambin validaciones de software en forma de cdigo Java, que puede
integrarse en las clases de accin. La interfaz com.opensymphony.xwork2.Validateable puede
implementarse en las clases de accin para facilitar una validacin por programacin. Esta interfaz
requiere una sola escritura de mtodo denominada validate(). El mtodo validate()contiene toda
la lgica de validacin de las propiedades de los JavaBeans.
Si una clase de accin implementa la interfaz Validateable, el mtodo validate() se ejecutar
automticamente. Si nuestra clase de accin es heredera de la clase ActionSupport, no tenemos
necesidad de implementar la interfaz Validateable.
Vamos a utilizar esta tcnica para imponer el identificador adurn durante la creacin de una cuenta
de cliente. De lo contrario, se agregar un mensaje de tipo fieldError.
Struts ofrece una programacin sencilla y permite combinar las tcnicas de validacin. En
nuestro ejemplo, hemos utilizado validaciones XML (bundle validation) por definicin, un
validador especfico y por ltimo una validacin de programacin, con el mtodo validate()en la
clase de accin.

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"

Formulario de cliente con validacin personal del identificador

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.

Administracin de las conversiones


Struts utiliza un interceptor llamado params, responsable de realizar el mapping entre los campos de
formularios y sus descriptores de acceso respectivos. Gracias a este interceptor, las cadenas de
caracteres recibidos por el protocolo HTTP se convierten automticamente al tipo definido en la clase
de accin, como el nuestro ejemplo anterior sobre la administracin de la cuenta del cliente. El ID de
la profesin del cliente se recibe en forma de una cadena de caracteres, pero se convierte
automticamente en un entero.
Struts lleva a cabo conversiones entre los distintos tipos y un problema de conversin (por ejemplo,
de
caracter
a
entero)
no
tiene
por
qu
detener
el
framework.
La
interfazcom.opensymphony.xwork2.ValidationAware es la encargada de administrar las conversiones de
tipos. Si nuestra clase de accin no implementa esta interfaz, se pueden producir excepciones.
Los mensajes de error de conversin se administran a travs del archivo de propiedades. La
introduccin de un valor incorrecto a nivel de tipo generar un mensaje como el siguiente: Invalid field
value for field fieldName.
Ya nos hemos encontrado con este tipo de mensaje en el proyecto ejemplo08 cuando hemos
introducido un carcter para la edad del cliente. Podemos modificar las visualizaciones relativas a los
tipos sobrecargando estos mensajes por defecto en nuestro archivo de propiedades.
En nuestro caso, podemos utilizar esta definicin en el archivo package.properties.
Invalid.fieldvalue.NombreDelCampo=Error de tipo en un campo
del formulario
El parmetro NombreDelCampo corresponde al nombre del campo utilizado en los formularios y las
validaciones.
Podemos retomar nuestra aplicacin ejemplo08 y utilizarla para nuestro nuevo proyecto ejemplo11.

rbol del ejemplo11

Cdigo: ejemplo11.package.properties
invalid.fieldvalue.edad=El campo edad debe ser un nmero entero para
poder confirmar el formulario

Formulario de registro de cliente y administracin de los tipos

El archivo de validacin que contiene los mensajes (ejemplo11.package.properties) tambin


puede ser nombrado segn el nombre de la clase ubicarse en el mismo entorno que ste
dentro del rbol del proyecto. En nuestro ejemplo, habramos podido utilizar el archivo
ejemplo11.ClientAction.properties.

Administracin de los tipos


Como hemos explicado en el prrafo anterior, Struts proporciona conversores de tipos simples que
pueden utilizarse en gran medida en la mayora de los proyectos. Sin embargo, no es posible realizar
conversiones de tipos complejos con el sistema que se incluye por defecto. Para ello, debemos crear
nuestros propios conversores de tipos. Un conversor de tipos debe implementar la
interfazognl.TypeConverter o heredar de la clase DefaultTypeConvertero StrutsTypeConverter.
Esta interfaz tiene una nica firma de mtodo llamada convertValue(...), que permite administrar la
conversin al tipo adaptado. La implementacin de este mtodo permite recuperar la propiedad
afectado por el tipo, as como la clase de la propiedad.

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.

rbol del proyecto ejemplo12


Esta aplicacin contiene un nuevo campo para la fecha de nacimiento del cliente. El archivo de
propiedades ClienteAccion-conversion.properties permite realizar el enlace entre el campo que se va a
verificar y la clase encargada de realizar esta operacin.
Cdigo: ejemplo12.ClienteAccion-conversion.properties
cliente.fechaNacimiento=ejemplo12.conversor.FechaNacimiento
Conversor
La clase que permite administrar la conversin de tipo es simple, recuperar los datos introducidos y
realiza una conversin un modelo especificado (en espaol en este caso). Si la conversin es correcta,
devolver el resultado en el objeto afectado, en caso contrario saldr del conversor sin bloquear la
aplicacin.

Los datos introducidos correctamente sern procesados y reenviados al formulario mientras


que los datos introducidos incorrectamente no sern reenviados al formulario.

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&ntilde;ol</a></li>
<li><a href="Agregar_Cliente.action?
request_locale=en">Inge&eacute;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>

Formulario de registro de un cliente ejemplo12

Visualizacin de la cuenta del cliente

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:

rbol del proyecto ejemplo13


El archivo de configuracin de la aplicacin struts.xml cuenta con dos acciones, una para la creacin
de
una
nueva
cuenta
(Agregar_Cliente.action)
y otra
para
mostrar la
informacin
(Listado_Cliente.action). La accin Listado_Cliente.action desencadena el mtodo listado()de la clase
de accin y la accin Agregar_Cliente.action est asociada al mtodo agregar() de la clase de
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>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
<package name="ejemplo13" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo13.ClienteAccion" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"

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.

Administracin de los clientes

La utilizacin del tipo redirectAction en el resultado de la adicin <result name="success"


type="redirectAction">Listado_Cliente</result> permite redirigir totalmente el formulario a la
accin Listado_Cliente.action para evitar as creaciones dobles en caso de actualizacin de la
pgina (F5) o de hacer doble clic en el botn de adicin. Asimismo, dado que este tipo es una
redireccin real, se perdern todos los parmetros y se limpiarn los datos introducidos por el
usuario (identificador y contrasea).
Ahora, si intentamos crear una cuenta con un error de introduccin (por ejemplo: sin introducir la
contrasea), observaremos que se conservan los datos pero no se muestra la lista de clientes. Es
algo totalmente normal puesto que la accin listado() de la clase de accin que devuelve la lista desde
el modelo no se ejecuta en caso de error en la introduccin de datos. Por tanto, los datos ya no se
muestran. El interceptador Preparable permite evitar este problema.

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.

rbol del proyecto ejemplo14


Comenzaremos modificando el archivo de declaracin de las acciones 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="ejemplo14" namespace="/" extends="strutsdefault">
<default-action-ref name="Listado_Cliente" />
<action name="Listado_Cliente"
class="ejemplo14.ClienteAccion" method="listado">
<result>/jsp/ListadoCliente.jsp</result>
</action>
<action name="Agregar_Cliente"
class="ejemplo14.ClienteAccion" method="agregar">
<result name="input">/jsp/ListadoCliente.jsp</result>
<result name="success"
type="redirectAction">Listado_Cliente</result>

</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;
}

// mostrar el formulario en edicin


public String editar()
{
return SUCCESS;
}
// modificar un cliente
public String modificar()
{
ClienteModelo.modificar(cliente);
return SUCCESS;
}
// eliminar un cliente a partir del parmetro recibido llamado
idCliente
public String eliminar()
{
ClienteModelo.eliminar(idClienteActual);
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"/>

<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: /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.

Administracin completa de los clientes

Acceso y manipulacin de datos


En los ejemplos anteriores, los datos se almacenan en una coleccin dinmica Java que contiene las
cuentas de los clientes. Durante la utilizacin de proyectos de gran envergadura, las bases de datos
se usan para garantizar la persistencia de los datos. Los modelos objeto-relacional y relacional-objeto
se utilizarn para gestionar la persistencia de los datos en Java.

Transformaciones de objeto en relacional y relacional en objeto


Las transformaciones objeto-relacional (objetos Java a base de datos) y relacional-objeto (base de
datos a objeto Java) se denominan mapping. Actualmente existe un gran nmero de herramientas,
ms o menos funcionales, para llevar a cabo estos servicios. Los ms conocidos son la librera
OpenSource Hibernate y Java Persistence API (JPA). Sin necesidad de utilizar una herramienta de
mapping, es totalmente posible llevar a cabo este servicio incluyendo diversas lneas de programacin
a nivel del software. El mecanismo implementado por el desarrollador utiliza el modelo o pattern Data
Access Object (DAO), suficiente para la mayora de aplicaciones profesionales.
El modelo o capa de persistencia, ofrece un conjunto de mtodos para consultar, agregar, modificar,
eliminar o realizar un listado de los objetos, imgenes de los valores de la base de datos.

1. El modelo Data Access Object DAO


Este modelo facilita un conjunto de reglas o consignas que deben seguirse para configurar un
sistema de persistencia de objetos. Con este modelo de diseo, cada clase utilizada en el sistema
debe poseer su propia clase modelo para gestionar su persistencia. Por ejemplo, tendremos una
clase JavaBean Cliente.java y una clase ClienteModeloDAO.java. La clase modelo contiene los
mtodos de modificacin del estado del objeto (agregar, modificar y eliminar) y de consulta
(consultar, realizar listado).
El modelo DAO de base se utiliza para cada clase de accin de Struts que debe llevar a cabo una
operacin en la base de datos. El modelo DAO prev tambin la configuracin de una clase principal
(o varias) para gestionar los mtodos comunes y la declaracin de las conexiones al SGBD. La
interfaz DAOes implementada por al clase DAO principal para indicar que se debe sobrecargar, y por
tanto utilizar, el mtodo getConnection(). La clase ModeloDAO se sita en la parte superior de la
jerarqua e implementa la anterior interfaz DAO. Esta clase permite la implementacin del
mtodogetConnection()que ser utilizado por las clases secundarias, as como por el setter, para la
transmisin de la conexin.

Diagrama UML del pattern DAO


Vamos a utilizar un nuevo proyecto ejemplo15 basado en el ejemplo anterior para hacer uso de la
parte modelo DAO. La base de datos utilizada se denomina struts2.

rbol del proyecto ejemplo15


La interfaz DAO requiere una sola escritura del mtodo getConnection()para la conexin al SGBD.

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);

Estructura MySQL de la base de datos struts2


Podemos empezar por comprobar nuestra conexin al SGBD iniciando la aplicacin y verificando si el
listener gestiona correctamente el pool.

Seguimiento de la carga del pool de conexin en la consola


La
clase de
accin
de Struts
prcticamente
no se
modifica, utiliza
dinmicoModeloClienteDAO en vez del modelo de la clase esttica del ejemplo anterior.
Cdigo: ejemplo15.ClienteAccion.java
package ejemplo15;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import ejemplo15.javabeans.Cliente;
import ejemplo15.modelos.ModeloClienteDAO;
@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 {
ModeloClienteDAO ModeloClienteDAO=new
ModeloClienteDAO();
// en creacin, crear un nuevo objeto vaco
if(idClienteActual==0)
{
cliente=new Cliente();
}
// en modificacin, devolver la informacin del objeto
else
{
cliente=modeloClienteDAO.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;

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;
}

// eliminar un cliente en la base


public int eliminarCliente(int idCliente)
{
// Variables
PreparedStatement consulta=null;
String consultaString=null;
int codigoError=0;
try
{
// Apertura de una conexin
conexion=super.getConnection();
// Eliminar el cliente

consultaString="DELETE FROM cliente WHERE


idCliente=?";
consulta=conexion.prepareStatement(consultaString);
consulta.setInt(1,idCliente);
// Ejecucin de la consulta
codigoError=consulta.executeUpdate();
}
catch(Exception e)
{
codigoError=0;
System.out.println("Error en la consulta
de la clase ModeloClienteDAO funcin eliminarCliente");
}
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 eliminarCliente");
}
}
// Devolver el cdigo de error
return codigoError;
}

// modificar un cliente en la base


public int modificarCliente(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="UPDATE cliente set
identificador=?,contrasena=? WHERE idCliente=?";

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;
}

// Realizar el mapping relacional hacia objeto


public Cliente mapperCliente(ResultSet resultado)
{
// Variables
Cliente cliente=new Cliente();
try
{
if (resultado.getString("idCliente")==null)
{
cliente.setIdCliente(0);
}
else

{
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;
}

public String getImagenFileName() {


return imagenFileName;
}
public void setImagenFileName(String imagenFileName) {
this.imagenFileName = imagenFileName;
}
public String getImagenContentType() {
return imagenContentType;
}
public void setImagenContentType(String imagenContentType) {
this.imagenContentType = imagenContentType;
}
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 en el modelo
public String agregar() throws Exception
{
// ubicar el nombre del archivo en el cliente
if(this.imagenFileName!=null)
{
cliente.setImagen(this.imagenFileName);
// 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);
ClienteModelo.agregar(this.cliente);
}

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>&nbsp;</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.

Carga de la imagen del cliente


Podemos observar que el archivo de validacin ClienteAccion-Agregar_Cliente-validacin.xml no es
gestionado por el interceptor fileUpload y que, en consecuencia, las validaciones ya no se
implementan. Si deseamos agregar la validacin de los campos y la utilizacin del resultado de
tipoinput, debemos agregar el interceptor validationWorkflowStack presente en el archivo strutsdefault.xmly que contiene otros interceptores.
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>

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.

Gestin de los errores de carga de archivos


Para sobrecargar los mensajes mostrados en el seguimiento de los errores, podemos crear un archivo
denominado struts-mensajes.properties que se encuentre en el paquete de la clase implicada.
Cdigo: /WEB-INF/src/struts-mensajes.properties
struts.mensajes.error.uploading=Error durante la carga del archivo
: {0}
struts.mensajes.error.content.type.not.allowed=Error. Tipo de
archivo no permitido
struts.mensajes.error.file.too.large=Error. El archivo
es demasiado grande
Con un archivo de tamao inferior a 100 Kb pero de un formato inadecuado (por ejemplo: un archivo
de texto), el mensajes reenviado ser el siguiente:

Visualizacin de mensajes de error adaptados


La verificacin del tipo de contenido se ha realizado correctamente, pero los mensajes de error siguen
siendo los definidos por defecto en Struts. Para que se utilice nuestro archivo strutsmensaje.properties, debemos sobrecargar el archivo de propiedades de Struts creando un
archivostruts.properties en el directorio /WEB-INF/src de la aplicacin e indicando a travs del
parmetrostruts.custom.i18n.resources el archivo que debe utilizarse para los mensajes.
Aprovecharemos adems para definir el tamao mximo del archivo que se va a cargar en este
archivo de configuracin.
Cdigo: /WEB-INF/src/struts.properties
struts.multipart.parser=org.apache.struts2.dispatcher.multipart.Ja
kartaMultiPartRequest
struts.multipart.maxSize=102400
struts.custom.i18n.resources=struts-mensajes
En caso de gestin del sitio en varios idiomas, ser necesario crear tantos archivos
struts.mensaje.properties como idiomas, aadindole a estos el sufijo de su configuracin
regional (por ejemplo: struts-mensaje_en.properties).

Gestin de mensajes de error por idioma

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">&nbsp;</div></div>
<div id="progress-text">&nbsp;</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.

rbol del proyecto ejemplo17

Carga asncrona de archivos en Ajax

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>

rbol del proyecto ejemplo18

Descarga de un archivo en bytes

Descarga dinmica de archivos


La aplicacin anterior permite mostrar cmo forzar las descargas o devolver flujos de bytes
directamente accesibles por el navegador. Vamos a desarrollar una nueva aplicacin, ejemplo19, que
permita descargar los archivos de origen del proyecto o mostrar el cdigo en forma de contenido
textual en el navegador.
El archivo de configuracin de la aplicacin struts.xml contiene la accin Mostrar_Archivo.action, que
permite descargar los archivos en forma de documentos de texto (contentType=text/plain). Esta
declaracin
est
asociada
al
mtodo
inputStreamMostrar().
La
segunda
accin,
denominadaDescargar_Mostrar.action, est asociada al mtodo de la accin inputStreamDescargar().
Este mtodo, ms complejo que el anterior, permite precisar de forma dinmica el nombre del archivo
que se va a descargar (atributo contentDisposition) en funcin del nombre recibido para beneficiarse
de un sistema genrico y verificar que ste existe.
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="ejemplo19" namespace="/" extends="strutsdefault">
<default-action-ref name="Gestionar_Archivo" />
<action name="Gestionar_Archivo">
<result>/jsp/ArchivoCliente.jsp</result>
</action>
<action name="Mostrar_Archivo"
class="ejemplo19.DescargarAccion">
<result name="success" type="stream">
<param name="inputName">inputStreamMostrar</
param>
<param name="contentType">text/plain</param>
</result>
</action>
<action name="Descargar_Archivo"
class="ejemplo19.DescargarAccion">
<result name="success" type="stream">
<param
name="inputName">inputStreamDescargar</param>
</result>
</action>
</package>
</struts>

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&oacute;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>

Visualizacin y descarga dinmica de archivos

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&aacute;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

El segundo ejemplo21 utiliza el mtodo getComplete()y la propiedad complete en la clase de accin


para mostrar el progreso de la carga al usuario. La clase de accin Cargador.action, as como la vista
JSP /jsp/Cargador.jsp se modifican para procesar el parmetro complete.
El parmetro complete se incrementa en 10 cada segundo, sabiendo el tiempo de espera es de 10
segundos, el tiempo de carga ser entonces correcto y preciso. Por supuesto, esta cifra deber
ajustarse y no es muy precisa, pero permite indicar el progreso al usuario.
Cdigo: ejemplo21.Cargador.action
package ejemplo21;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class CargadorAccion extends ActionSupport {
private int complete=0;
public String execute() {
System.out.println("En la accin");
try
{
Thread.sleep(10000);
}
catch(Exception e)
{
System.out.println(e);
}
return SUCCESS;
}
public int getComplete() {
complete+=10;
return complete;
}
}

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&aacute;gina
<s:property value="complete"/>% completado
</div>
</body>
</html>

Carga de archivos y progreso

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().

Funcionamiento de los filtros Java

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&oacute;n</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Autenticaci&oacute;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&aacute; protegido, est&aacute;
autenticado;
<br/><a href="Desconectar.action">Desconectarse</a>
</div>
</body>
</html>

Formulario de autenticacin del cliente

Acceso protegido por un interceptor de Struts personalizado

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>

Gestin de los clientes en formato RSS

Gestin del resultado en formato RSS

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

Aplicacin del testigo


Para aplicar la gestin del testigo y del interceptor TokenInterceptor, utilizaremos el ejemplo de
gestin de clientes ejemplo14 en un nuevo proyecto llamado ejemplo24. El mtodo agregar()permite
crear un nuevo cliente y se modificar para abordar el problema del doble envo. Para ello, crearemos
un thread de espera de varios segundos.
Cdigo: ejemplo24.ClienteAccion.java
package ejemplo24;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import ejemplo24.javabeans.Cliente;
import ejemplo24.modelo.ClienteModelo;
@SuppressWarnings("serial")
public class ClienteAccion extends ActionSupport implements
Preparable, ModelDriven{
...
// agregar el cliente al modelo
public String agregar()
{
try
{
Thread.sleep(4000);
}
catch(Exception e)
{
}
ClienteModelo.agregar(cliente);
return SUCCESS;
}
...
}
El thread de espera permite simular una carga larga de la pgina cuando se aade el cliente. A
continuacin, podemos visualizar el proyecto y crear una nueva cuenta. Si el tiempo de carga de la
pgina es largo, podemos hacer clic en el botn Agregar (Agregar un cliente) varias veces
consecutivas. Entonces observamos las inserciones mltiples a la lista.

Formulario de registro del cliente

Visualizacin mltiple de las cuentas de los clientes

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.

Gestin del doble clic con el mensaje por defecto


El mensaje de error asociado a la gestin de testigos es la clave struts.messages.invalid.token que se
encuentra en el archivo struts-messages.properties incluido con Struts. La clase de gestin del
interceptor es org.apache.struts2.interceptor.TokenInterceptor. Para sobrecargar del mensaje mostrado
en el error de la accin, es necesario crear un archivo llamado TokenInterceptor.properties que se
encuentra en el directorio /WEB-INF/classes/org/apache/struts2/interceptor.
Una 2 tcnica consiste en crear, como en los ejemplos anteriores del libro, un
archivostruts.properties
en
la
carpeta
/WEB-INF
de
la
aplicacin
con
el
parmetro struts.custom.i18n.resourcesy un segundo archivo llamado struts-messages.properties con
la clave y su valor.
Cdigo: struts.properties
struts.custom.i18n.resources=struts-messages

Cdigo: struts-messages.properties
struts.messages.invalid.token=Ha enviado el formulario dos
veces. Slo se validar un nico envo.

Formulario de cliente con gestin del doble clic multilinge

rbol del proyecto ejemplo24

El interceptor tokenSession es similar al interceptor token, la diferencia reside en la forma de


guardar los datos al validar el formulario. El interceptor tokenSession guarda los datos del
formulario en la sesin del usuario.

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.

Instalacin del framework JavaScript


Desde hace aos, se vienen desarrollando numerosos framework especializados en JavaScript
especficos que permiten ofrecer un conjunto de servicios Web 2.0. Podemos citar por
ejemploPrototype (http://www.prototypejs.org/), as como Scriptaculous (http://script.aculo.us/)
basado en Prototype y que ofrece nuevas funcionalidades Ajax y DHTML. La librera ms potente, pero
tambin la ms pesada y compleja, es actualmente, sin ninguna duda, ExtJs (http://extjs.com/). Por
ltimo, la ms utilizada y recomendada por los diseadores de Struts es JQuery (http://jquery.com/).
Esta librera, muy ligera en una versin comprimida (alrededor de 20 Kb), permite manipular
documentos HTML y XHTML. Asimismo, ofrece un sencillo sistema de acceso a las etiquetas de los
documentos mediante notacin de puntos. Este framework propone cientos de complementos para
gestionar la visualizacin, los formularios, Ajax, las validaciones e incluso el uso del ratn.
La configuracin de este framework es muy simple y est compuesta por tres etapas:
Descarga de la librera JavaScript y de manera opcional de los complementos necesarios.
Instalacin de los
ejemplo:/jscripts).

archivos .js descargados

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.

Gestin del formulario de adicin de clientes en Ajax


En nuestro ejemplo vamos a utilizar el framework JQuery, as como un conjunto de complementos,
para llevar a cabo las principales operaciones tiles de una aplicacin Ajax Web 2.0. Para ello,
comenzaremos un nuevo proyecto, ejemplo25, que nos permitir gestionar los clientes y la
libreraJQueryForm Plug-in (http://malsup.com/jquery/form/) para enviar el formulario en Ajax sin
necesidad de volver a cargar la pgina.
La configuracin se modifica desde el punto de vista de la estructura. En realidad, las respuestas a los
errores de validacin <result name="input"> y de xito <result name="success"> siempre deben
volver a la misma pgina de visualizacin de mensajes (errores, errores de accin y xito). El resto de
visualizaciones se gestionan en Ajax para evitar volver a cargar las pginas. El archivo de gestin de
las acciones struts.xml cuenta con las declaraciones para la realizacin de listados, la adicin, la
modificacin y la eliminacin. Estas acciones se realizan en segundo plano y nicamente devuelven el
mensaje adaptado en funcin de la accin (validacin de campos, error, xito). La
accinFormulario_Cliente.action permite volver al archivo de gestin del cliente aadido o modificado
dependiendo de la presencia o ausencia del objeto cliente. Esta accin utiliza el
interceptorparamsPrepareParamsStack para gestionar la transmisin de parmetros. Finalmente, la
ltima accinIndice_Cliente.action permite mostrar la pgina principal de gestin.
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="exemple25" namespace="/" extends="strutsdefault">
<default-action-ref name="Indice_Cliente" />

<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>

<!-- etiqueta utilizada para mostrar la lista de clientes en


Ajax -->
<div id="CuadroListadoCliente"></div>
</div>
</body>
</html>
La pgina /jsp/FormularioCliente.jsp es muy sencilla y permite mostrar el formulario del cliente que est
siendo creado o modificado de acuerdo con el parmetro recibido.
Cdigo: /jsp/FormularioCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- formulario de registro 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"
label="Identificador" labelposition="top" cssClass="input"/>
<s:textfield name="cliente.contrasena" id="cliente.contrasena"
label="Contrasea" labelposition="top" cssClass="input"/>
<s:submit value="Confirmar"/>
</s:form>
La pgina /jsp/ListadoCliente.jsp retoma el cdigo de visualizacin de los datos 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 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="JavaScript:modificarCliente($
{idCliente})"><img src="imagenes/editarcliente.png" alt="Editar"
title="Editar" border="0"/></a></td>
<td align="center"><a href="JavaScript:eliminarCliente($
{idCliente})"><img src="imagenes/eliminarcliente.png" alt="Eliminar"
title="Eliminar" border="0"/></a></td>
</tr>
</s:iterator>
</table>
Por ltimo, la pgina /jsp/RespuestaAjax.jsp, utilizada por las acciones, permite simplemente mostrar
los mensajes recibidos en Ajax.

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()
{

//enviar los datos en POST


$.ajax(
{
type: "POST",
url: "Formulario_Cliente.action",
dataType: "html",
timeout : 8000,
error: function(){
alert(Desconexion 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);
// adicin realizada correctamente
if($
(#CuadroRespuestaAjax).html().indexOf("mensaje_informacion")!=-1)
{
// recuperar la nueva lista de cliente
listadoCliente();
// vaciar los campos del formulario
$
(#Formulario_Cliente).resetForm();
$
(#Formulario_Cliente).clearForm();
}
}
});
}
});
}
// devolver la lista de clientes utilizando Ajax
function listadoCliente()
{
//enviar los datos en POST
$.ajax(
{
type: "POST",
url: "Listado_Cliente.action",
dataType: "html",
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);
}
});
}
// 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();
}
}
});
}
});
}

rbol del proyecto ejemplo25

Gestin de clientes en Ajax

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.

1. Utilizacin de botones dinmicos


Vamos a agregar un nuevo estilo con un icono para el botn de confirmacin con el fin de mostrar la
imagen de carga en el botn al igual que en los programas informticos.
#botonconfirmar
{
background-image: url(../imagenes/confirmar.png);
background-repeat:no-repeat;
background-position:5% 65%;
height:20px;
padding-left:20px;
width:120px;
height:25px;
color:#333333;
font-family: tahoma, verdana, arial, sans-serif;
font-size:11px;
font-weight:normal;
margin-left:17px;
background-color:#ebebeb;
border:1px solid;
border-top-color:#999999;
border-left-color:#999999;
border-right-color:#666666;
border-bottom-color:#666666;
}
A continuacin, agregaremos las lneas JQuery siguientes, que permiten cambiar la imagen de fondo
del botn, desactivar el mismo y mostrarlo de nuevo cuando se produce la respuesta.
// utilizar el formulario ajaxForm
$(#Formulario_Cliente).ajaxForm({
...
// funcin desencadenada antes del envo del formulario
beforeSubmit : function() {
$("#botonconfirmar").css("backgroundimage","url(./imagenes/cargacircularpequena.gif)");
$("#botonconfirmar").attr("disabled","disabled");
},
// funcin desencadenada tras el envo del formulario
success: function() {

// volver a colocar el botn


$("#botonconfirmar").css("backgroundimage","url(./imagenes/confirmar.png)");
$("#botonconfirmar").removeAttr("disabled");
...

Utilizacin de botones dinmicos

2. Gestin de cuadros (box) dinmicos para las confirmaciones y mensajes


Vamos a agregar un nuevo complemento JQuery denominado JQueryUI (http://jqueryui.com/). Este
complemento permite gestionar calendarios, barras de carga, tablas, etc. La librera se copia en el
directorio /javascript/jqueryui con el directorio /css para las hojas de estilos y el directorio /js para los
archivos JavaScript. Vamos a utilizar la librera para crear cuadros de dilogo dinmicos Web 2.0 con
botones, con el fin de gestionar el redimensionamiento de los cuadros, el desplazamiento de estos y
la gestin de la colocacin en primer plano.
A continuacin pasamos a la configuracin de las libreras en la pgina /jsp/IndiceCliente.jsp:
<!-- Utilizar la biblioteca JavaScript JQueryUI -->
<style type="text/css">@import url(javascript/plugin/jqueryui/css/
smoothness/jquery-ui-1.7.1.custom.css);</style>
<style type="text/css">@import url(javascript/plugin/jqueryui/css/
css.css);</style>
<script type="text/javascript" src="javascript/plugin/jqueryui/js/
jquery-ui-1.7.1.custom.min.js"></script>
<script type="text/javascript"
src="javascript/ejemplojqueryui.js"></script>
La librera est configurada y ahora podemos modificar los vnculos para la eliminacin de clientes
con el fin de desencadenar una nueva funcin JavaScript de confirmacin avanzada.

<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>&iquest;Desea eliminar el cliente?</p>
</div>

Cuadro de dilogo Web 2.0


Para la gestin de los cuadros de mensaje (errores, errores de accin y xito), vamos a utilizar un
cdigo JavaScript basado en un Timer (thread), que mostrar u ocultar los cuadros de acuerdo con
las necesidades.
// tiempo de espera de los mensajes en milisegundos
var tiempoespera=3000;
// funcin que permite mostrar u ocultar los mensajes durante
un determinado tiempo
function mensajeDinamico(cache,animation)
{
if($("#mensaje_error")!=null || $
("#mensaje_informacion")!=null)
{
if(cache)
{
$("#mensaje_error").effect("explode",{},500);
$("#mensaje_informacion").effect("explode",{},500);
}
else
{
$("#mensaje_error").show("slow");
$("#mensaje_informacion").show("slow");
}
if(animation) setTimeout("mensajeDinamico("+!
cache+",false)",tiempoespera);
}
}
Por tanto, este mtodo utiliza un tiempo de espera de 3 segundos (configurable) para ocultar los
mensajes
con
un
efecto
de
animacin.
A
continuacin
se
acude
a
la
funcinmensajeDinamico(cache,animation) en cada mtodo que devuelve un valor.
...
// 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();
}
// gestin dinmica de los mensajes
mensajeDinamico(false,true);
}
...

3. Utilizacin del complemento Widget


Los componentes o widgets se utilizan cada vez ms en las aplicaciones de Internet. As,
encontramos cuadros que se pueden ampliar, desplazar, cerrar y cuya utilizacin es similar a la de
los cuadros en los programas informticos. El complemento JQuery easywidgets permite crear
componentes avanzados. Este complemento utiliza una hoja de estilos especfica, as como un
archivo JavaScript jquery.easywidgets.js e imgenes, para los cuadros.

rbol JavaScript del complemento JQuery easywidgets


A continuacin se realiza la gestin del complemento con cdigo JavaScript ubicado en nuestro
archivo /javascript/ejemplo26.js. Para crear un widget, debemos utilizar las etiquetas <div/>con las
clases especficas y utilizar un nombre nico con el atributo idpara cada widget con el fin de poder
hacer referencia a los mismos. El cuadro debe estar asociado con la clase CSS widget-place, y el
cuadro secundario con la clase widget y con las diferentes propiedades que deben gestionarse
(desplazable, editable, eliminable...). El bloque del ttulo est asociado con el estilo widget-header,
el cuadro secundario cuenta con el estilo widget-editboxy el contenido principal est vinculado con
el estilo widget-content.
En resumen, la estructura de un widget (componente) es la siguiente:

Estructura de las etiquetas y estilos CSS del complemento easywidget


Vamos a modificar nuestra pgina principal insertando el ttulo en la etiqueta widget-header, el
formulario de registro en la parte widget-editbox y la tabla de lista en la seccin widget-content.
Adems, agregaremos un segundo widget de ayuda para mostrar el alcance de los desplazamientos
de los cuadros. Para ocultar o mostrar todos los cuadros se insertan dos vnculos. A continuacin se
indica el nuevo cdigo de la pgina JSP.
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>
<!-- archivo de gestin -->
<script src="javascript/ejemplo26.js"
type="text/javascript"></script>
<!-- Utilizar la librera JavaScript JQueryUI -->
<style type="text/css">@import url(javascript/plugin/jqueryui/css/
smoothness/jquery-ui-1.7.1.custom.css);</style>
<style type="text/css">@import url(javascript/plugin/jqueryui/css/
css.css);</style>
<script type="text/javascript" src="javascript/plugin/jqueryui/js/
jquery-ui-1.7.1.custom.min.js"></script>
<script type="text/javascript"
src="javascript/ejemplojqueryui.js"></script>
<!-- Utilizar la librera JavaScript easywidgets -->
<style type="text/css">@import
url(javascript/plugin/easywidgets/css/jquery.easywidgets.css);</style>
<style type="text/css">@import
url(javascript/plugin/easywidgets/css/example.css);</style>
<script type="text/javascript" src="javascript/plugin/easywidgets/
js/jquery.easywidgets.js"></script>
<script type="text/javascript" src="javascript/plugin/easywidgets/
js/jquery-ui-min.js"></script>
</head>
<body>

<!-- etiqueta utilizada para mostrar las respuestas Ajax -->


<div id="CuadroRespuestaAjax"></div>
<div align="left" style="margin-left:20px">
<ul>
<li><a onClick="$.fn.ShowEasyWidgets(); return false"
href="#" title="Mostrar widgets">Mostrar
widgets</a></li>
<li><a onClick="$.fn.HideEasyWidgets(); return false"
href="#" title="Ocultar widgets">Ocultar widgets</a></li>
</ul>
</div>
<!-- Widget 1 -->
<div class="widget-place" id="widget-place-1">
<div class="widget movable collapsable removable closeconfirm
editable collapse" id="identificarwidget-1">
<div class="widget-header"><strong>Gesti&oacute;n de
clientes</strong></div>
<div class="widget-editbox">
<!-- etiqueta utilizada para agregar/modificar un cliente en
Ajax -->
<div id="CuadroFormularioCliente"></div>
</div>
<div class="widget-content">
<!-- etiqueta utilizada para mostrar la lista de clientes
en Ajax -->
<div id="CuadroListadoCliente"></div>
</div>
</div>
</div>
<!-- Widget 2 -->
<div class="widget-place" id="widget-place-2">
<div class="widget movable collapsable removable closeconfirm
collapse" id="identificarwidget-2">
<div class="widget-header"><strong>Ayuda</strong></div>
<div class="widget-content">
La aplicaci&oacute;n de gesti&oacute;n de los clientes,
de tipo Web 2.0, se gestiona totalmente en Ajax.
</div>
</div>
</div>
<!-- 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>&iquest;Desea eliminar el cliente?</p>
</div>
</body>
</html>
La gestin de los widgets se realiza con la ayuda del archivo JavaScript ejemplo26.js. Agregaremos
el bloque de internacionalizacin i18n de gestin de textos e imgenes mostrados para la
manipulacin de los cuadros. Asimismo, utilizamos el servicio de gestin de cookies, que permite
conservar la ubicacin de nuestros widgets (la ubicacin de los cuadros) en la siguiente utilizacin.
// Gestin de widgets
$(function(){
// Gestin de la internacionalizacin para los cuadros
$.fn.EasyWidgets({

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.

Utilizacin de widgets para los formularios

4. Utilizacin de herramientas dinmicas


Para esta parte del proyecto de Struts 2 en Ajax, vamos a crear un nuevo proyecto, ejemplo27, a
partir del anterior y a agregar dos atributos a la clase JavaBean Clientecon el fin de gestionar la
fecha de nacimiento del cliente y un campo de descripcin para sus datos (cv, competencias...).
Estas dos nuevas propiedades se asociarn a continuacin con los servicios dinmicos DHTML para
mostrar un calendario para la fecha de nacimiento. El modelo se modifica ligeramente para la
inicializacin y la modificacin de los objetos del cliente.
Cdigo: /ejemplo27/.javabeans.Cliente.java
package ejemplo27.javabeans;
@SuppressWarnings("serial")
public class Cliente {
private int idCliente;
private String identificador;
private String contrasena;

private String fechadenacimiento;


private String descripcion;
public Cliente() {
}
public Cliente(int idCliente,String identificador, String
contrasena, String fechadenacimiento, String descripcion){
this.idCliente=idCliente;
this.identificador=identificador;
this.contrasena=contrasena;
this.fechadenacimiento=fechadenacimiento;
this.descripcion=descripcion;
}
// getter y setter
...
}

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&oacute;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"

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>
<script type="text/javascript" src="javascript/plugin/jqueryui/js/
i18n/jquery-ui-i18n.js"></script>
<script type="text/javascript">
$(function() {
// mostrar los meses y aos
$("#cliente_fechadenacimiento").datepicker({
changeMonth: true,
changeYear: true
}
);
// cambiar el idioma a espaol
$
("#cliente_fechadenacimiento").datepicker($.datepicker.regional[es]
);
$("#cliente_fechadenacimiento").datepicker(option,
$.extend({showMonthAfterYear: false},
$.datepicker.regional[es]));
});
</script>
La notacin de puntos (cliente.fechadenacimiento) no est permitida para acceder a un
objeto con el framework JQuery. En el ejemplo utilizamos el guin bajo (_).

Utilizacin de un calendario dinmico

5. Utilizacin de un servicio de autocompletado para las bsquedas


La gestin del autocompletado o proposicin en las bsquedas es un servicio habitual en las
aplicaciones de Internet Web 2.0. Para ejemplificar este servicio, vamos a utilizar de nuevo un
complemento JQuery denominado autocomplete con sus archivos JavaScript y su hoja de estilos.
La configuracin comienza por la instalacin de los archivos en el directorio /javascript/plugin de la
aplicacin ejemplo27. La vista especializada de la visualizacin de la lista de clientes se modifica con
el fin de gestionar el formulario de bsqueda.
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- Formulario de bsqueda -->
<div id="buscar" align="left">
<a href="javascript:mostrarBusqueda();"><img
src="imagenes/lupabusqueda.png" title="Hacer clic para mostrar
el formulario de bsqueda" alt="Buscar" align="absmiddle"
border="0"/></a>
</div>
<s:form method="post" id="formulariobusqueda"
name="formulariobusqueda" theme="simple">
<table cellspacing="4" cellpadding="0" id="tabla" width="440px"
border="0">

<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&oacute;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>();

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"));
}

// devolver la lista de clientes


public static List<Cliente> getListaClientes(String busqueda,
String atributo) {
// lista para las bsquedas
List<Cliente> listaClientesBusqueda=new ArrayList<Cliente>();
// bsqueda usuario
if(busqueda!=null && !busqueda.equalsIgnoreCase(""))
{
// examinar cada cliente
for(Cliente c : listaClientes)
{
// bsqueda en contrasea
if(atributo.equals("cliente.contrasena"))
{
if(c.getContrasena().contains(busqueda))
{
listaClientesBusqueda.add(c);
}
}
// bsqueda en la fecha
else
if(atributo.equals("cliente.fechadenacimiento"))
{
if(c.getFechadenacimiento().contains(busqueda))
{
listaClientesBusqueda.add(c);
}
}
// bsqueda en el identificador
else
{
if(c.getIdentificador().contains(busqueda))
{
listaClientesBusqueda.add(c);
}
}
}
// devolver la nueva lista de clientes
return listaClientesBusqueda;
}
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());
c.setFechadenacimiento(cliente.getFechadenacimiento());
c.setDescripcion(cliente.getDescripcion());
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;
}

// realizar una bsqueda en las listas


public static ArrayList<String> buscar(String introduccion,
String atributo) {
// lista de bsquedas
ArrayList<String> listaBusquedas=new
ArrayList<String>();
// examinar cada cliente
for(Cliente c : listaClientes)
{

// 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.

rbol del proyecto ejemplo27

Utilizacin del autocompletado para las bsquedas

6. Gestin de clasificaciones dinmicas


El servicio termina con el uso del complemento JQuery tablesorter (http://tablesorter.com/docs/), que
permite clasificar las columnas de una tabla. Este complemento se instala en el
directorio/javascript/plugin/tablesorter. Para activar las clasificaciones, las tablas deben contar con un
identificador nico (parmetro id), as como con un formato a partir de etiquetas <thead/>y<tbody/>.
Nuestra pgina de visualizacin se modifica ligeramente para respetar esta estructura y colocar en
JavaScript las clasificaciones de la tabla de clientes.
Cdigo: /jsp/ListadoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- Formulario de bsqueda -->
<div id="buscar" align="left">
<a href="javascript:mostrarBusqueda();"><img
src="imagenes/lupabusqueda.png" title="Hacer clic para mostrar el
formulario de busqueda" alt="Buscar" align="absmiddle"
border="0"/></a>
</div>
<s:form method="post" id="formulariobusqueda"
name="formulariobusqueda" theme="simple">
<table cellspacing="4" cellpadding="0" class="tabla" width="440px"
border="0">
<tr>
<td>Buscar: <s:textfield name="busqueda" id="busqueda"
label="Busqueda" 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">Contrase&ntilde;a</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>
<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&ntilde;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>

Utilizacin del complemento JavaScript de gestin de clasificaciones

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.

Cmo usar Velocity


Al igual que JSP, Velocity proporciona objetos implcitos que pueden utilizarse sin creacin previa.
response: el objeto HttpServletResponse.
res: el alias del objeto response.
request: el objeto HttpServletRequest.
req: el alias del objeto request.
session: el objeto HttpSession.
application: el objeto ServletContext.
base: la ruta del contexto de la aplicacin (path).
action: el objeto action.
stack: el valor de la pila de ejecucin.
Velocity tambin ofrece varias etiquetas o tags. Estas etiquetas se parecen a los tags de Struts, pero
la sintaxis es diferente y se basa en la utilizacin de caracteres #nombre.
Cuando las pginas utilizan nicamente tags de Velocity, la etiqueta de inclusin de la
biblioteca de Struts no es obligatoria <%@ taglib prefix="s" uri="/struts-tags" %>. No es
posible empaquetar proyectos con pginas JSP, ya que las pginas JSP necesitan un motor Java
para ejecutarse.
Esta
es
una
lista
no
exhaustiva
de
etiquetas
Velocity: #if, #else, #elseif, #end, #foreach,#include, #stextfield, #sform.

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>

Gestionar los clientes con el motor de plantillas Velocity

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>

rbol del proyecto ejemplo29

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.

Tcnica de transformacin de XML-XSLT a HTML

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

rbol del proyecto ejemplo30

Formulario de registro de una cuenta de cliente

Visualizacin de la informacin del cliente en formato XML


El segundo proyecto ejemplo31 permite mostrar el resultado en forma de un flujo RSS modificando
nicamente la parte de la VISTA, es decir, la hoja de estilo XSL.

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>

Visualizacin de la informacin del cliente en formato RSS


El ltimo proyecto ejemplo32 permite mostrar en formato XHTML la informacin introducida por el
usuario con la ayuda de una hoja de estilo XSL. Observamos las ventajas de el procesamiento y la
presentacin.
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:output method="html" indent="yes" encoding="ISO-8859-1"/>
<xsl:template match="/">
<html>
<head>
<title>Cliente </title>

<!-- hojas de estilo -->


<link rel="stylesheet" type="text/css"
href="css/estilos.css" title="predeterminado" />
</head>
<body>
<table border="0" id="tabla" cellpadding="0"
cellspacing="4">
<tr><td><b>Identificador</b></td><td><b>Contrasea</b></
td><td><b>Fecha de nacimiento</b></td><td><b>Descripcin</b></td></tr>
<tr>
<td><xsl:value-of
select="/result/cliente/identificador"/></td>
<td><xsl:value-of
select="/result/cliente/contrasena"/></td>
<td><xsl:value-of
select="/result/cliente/fechadenacimiento"/></td>
<td><xsl:value-of
select="/result/cliente/descripcion"/></td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Visualizacin de la cuenta del cliente con una hoja XSLT

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&oacute;n del tiempo del cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
</head>
<body>
<div id="carta">
<h3>Distribuci&oacute;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>

rbol del proyecto ejemplo33

Formulario de introduccin de valores del cliente

Visualizacin de la grfica JFreeChart


Tambin podemos insertar directamente una grfica en una imagen de un documento XHTML con la
ayuda del siguiente cdigo: <img src="Grafica.action"/>. El nuevo ejemplo33ajax permite mostrar
dinmicamente grficas de forma aleatoria con la ayuda de la etiqueta XHML <img/> y de cdigo
JavaScript.
Cdigo: /jsp/TiempoCliente.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Distribuci&oacute;n del tiempo del cliente</title>
<style type="text/css">@import url(css/estilos.css);</style>
<!-- archivo de gestin -->
<script src="javascript/ejemplo33ajax.js"
type="text/javascript"></script>
</head>
<body>
<div id="carta">
<img src="" id="imagen"/>
<script language="JavaScript">
// accin efectuada despus de la carga
window.load=mostrarGrafica();
</script>
</div>
</body>

</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&Uacute;<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&oacute;n de
fragmentos</div>
</body>
</html>

rbol del proyecto ejemplo34

Visualizacin del formulario del cliente con la ayuda de fragmentos


Esta tcnica, muy utilizada en la mayora de las aplicaciones Web, tambin es muy limitada para
proyectos de envergadura. De hecho, si la presentacin cambia (el nombre de los fragmentos) o la
presentacin, debemos modificar cada pgina del usuario. Para evitarlo, el complemento Tiles
proporciona una biblioteca de etiquetas que permite definir la presentacin para todas las pginas de
usuario del sitio. Un cambio en la definicin de las pginas conlleva una modificacin instantnea de
todas las pginas de usuario. Para ello, Tiles se basa en una definicin de pginas en formato JSP o
XML, que permite determinar la estructura del sitio.
Tiles es un componente desarrollado en su origen por Struts 1. Sin embargo, gracias a su
popularidad, Tiles pas a ser un proyecto de Apache por completo http://tiles.apache.org. El
complemento de Struts, Tiles se incluye con las bibliotecas de Struts struts-2.1.X-lib.zip (tiles-core2.X.jar, tiles-api-2.X.jar, tiles-jsp-2.x.jar y struts2-tiles-plugin-2.1.X.jar). Para que el complemento
Tiles funcione correctamente con Struts 2, debern instalarse tambin las siguientes bibliotecas
dependientes: commons-beanutils-1.X.jar, commons-collections-3.X.jar y commons-digester-1.X.jar.

El complemento Tiles se basa en dos componentes: la plantilla y la definicin. La plantilla de las


pginas se define a partir de una pgina JSP. Cada pgina JSP utilizada por la plantilla de divisin
ser declarada en la pgina. Ahora, si queremos cambiar toda la plantilla de presentacin de la
aplicacin, basta con cambiar una sola pgina JSP de definicin para aplicar los cambios. Para aplicar
este complemento, vamos a definir un nuevo proyecto con el nombre ejemplo35 a partir del anterior.

1. Pgina del formato de la plantilla


La siguiente pgina JSP permite definir la plantilla de presentacin del sitio. Al principio del archivo
encontramos la inclusin de la biblioteca de etiquetas y la utilizacin de dos etiquetas:
<tiles:getAsString name="titulopagina"/>: permite devolver una cadena de caracteres
definida en el archivo de configuracin.
<tiles:insertAttribute name="encabezado"/>: permite incluir una pgina en la definicin de
la plantilla de presentacin.
La etiqueta <tiles:/>posee el atributo role, que permite especificar la funcin del usuario
necesaria para la ejecucin de la pgina.

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

3. Aplicacin del complemento Tiles


La aplicacin del complemento Tiles se realiza en cuatro pasos:
Copia de los archivos tiles-core-2.X.jar, tiles-api-2.X.jar, tiles-jsp-2.x.jar y struts2-tiles-plugin2.1.X.jar en el directorio /WEB-INF/lib de la aplicacin.
Definicin del listener asociado a Tiles en el archivo de configuracin /WEB-INF/web.xml.

<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>

Utilizacin del complemento Tiles para la arquitectura de las pginas

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:

rbol del proyecto ejemplo36


El paquete com.pluginstruts2.rss contiene nuestra clase rssResult.java que permite generar una
coleccin de objetos en forma de flu jo RSS. Esta clase utiliza dos parmetros, coleccionObjeto para
especificar el nombre de la coleccin que se va a mostrar y enlace para realizar los enlaces de cada
elemento del flujo rss.
Cdigo : /com/pluginstruts2/rss/rssResult.java
package com.pluginstruts2.rss;
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;
@SuppressWarnings("serial")
public class rssResult extends StrutsResultSupport{
// recuperar el nombre del objeto
private String coleccionObjeto;
// enlace para el archivo rss
private String enlace;
public String getColeccionObjeto() {
return coleccionObjeto;
}
public void setColeccionObjeto(String coleccionObjeto) {
this.coleccionObjeto = coleccionObjeto;

}
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.

rbol de un complemento de Struts


El archivo struts-plugin.xml comienza por la definicin del nombre del paquete que se utilizar ms
adelante en el resto de paquetes. Se especifica un resultado de tipo rss con la asociacin de la clase
que se va a ejecutar para este tipo. Por ltimo, se inicializan dos parmetros por defecto para el
nombre de la coleccin y el enlace de cada elemento del flujo rss.
Cdigo: struts-plugin.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="rss-default" extends="struts-default">
<result-types>
<result-type name="rss"
class="com.pluginstruts2.rss.rssResult" default="false">
<param
name="coleccionObjeto">listaObjetos</param>
<param name="enlace">http://localhost</param>
</result-type>
</result-types>
</package>
</struts>
Hemos terminado con la estructura, ahora podemos pasar a la creacin del archivo con la
herramientajar incluida en el jdk de Java. Para ello, es necesario situarse en el directorio del
complemento y ejecutar el siguiente comando:
jar -cvf plugin-struts2-rss.jar *
Se crear un nuevo paquete con el nombre plugin-struts2-rss.jar en la raz del rbol del complemento.

Generar un archivo para el complemento de Struts

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;

public Object getModelo() {


return cliente;
}
public Cliente getCliente() {
return cliente;
}
public void setCliente(Cliente cliente) {

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;

// anotacin del complemento Convention


@Action("/ListadoCliente")
public String execute()
{
System.out.println("Registr en el mtodo de lista");
listaClientes=ClienteModelo.getListaClientes();
return SUCCESS;
}
public List<Cliente> getListaClientes() {
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {
this.listaClientes = listaClientes;
}
}

rbol del proyecto ejemplo37


Para definir la accin por defecto, el parmetro <default-action-ref name="ListadoCliente" /> del
archivo de configuracin struts.xml puede sustituirse por una definicin de accin en forma de
anotacin. En nuestro proyecto, la accin ejecutada por la URL actual (value="/") es la
funcinListado() de nuestro proyecto.
// anotacin del complemento Convention
@Actions({
@Action(value="/",
results={@Result(name="success", location="/WEBINF/content/ListadoCliente.jsp")}
),
@Action(value="/ListadoCliente",
results={@Result(name="success", location="/
WEB-INF/content/ListadoCliente.jsp")}
)
})
public String listado()
{

System.out.println("Registro en el mtodo de lista");


listaClientes=ClienteModelo.getListaClientes();
return SUCCESS;
}

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;
}

// anotacin del complemento Convention


@Action(value="/AgregarCliente",
results={
@Result(name="success",
location="/ListadoCliente", type="redirect"),
@Result(name="input",
location="/jsp/ListadoCliente.jsp")
},
interceptorRefs=@InterceptorRef(value="validation",params={"progra
mmatic", "true", "declarative", "false"})
)
public String agregar()
{
ClienteModelo.agregar(cliente);
return SUCCESS;
}

// Mostrar el formulario de edicin


@Action(value="/EditarCliente",
results={@Result(name="success",
location="/jsp/EditarCliente.jsp")}
)
public String editar()
{
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;
}

// eliminar un cliente a partir del parmetro recibido llamado


idCliente
@Action(value="/EliminarCliente",
results={@Result(name="success", location="/ListadoCliente",
type="redirect")}
)
public String eliminar()
{
ClienteModelo.eliminar(idClienteActual);
return SUCCESS;
}

public List<Cliente> getListaClientes() {


listaClientes=ClienteModelo.getListaClientes();
return listaClientes;
}
public void setListaClientes(List<Cliente> listaClientes) {

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;
}
}

rbol del proyecto ejemplo38

Gestin completa de los clientes a partir de anotaciones

5. Anotaciones de espacios de nombre


La anotacin de namespace permite cargar la convencin de nomenclatura utilizada por defecto con
Struts. Cuando esta anotacin se coloca al nivel de la clase de accin, sta se aplica a todos los
mtodos de la accin. Podemos modificar nuestro proyecto anterior ejemplo38 aadiendo un nuevo
namespace para acceder al proyecto por URL diferentes.
Cdigo: ejemplo38.acciones.ClienteAccion.java
package ejemplo38.acciones;
...
@Namespace("/GestionCliente")
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
...
Ahora se puede acceder a la aplicacin desde las siguientes URL:
http://localhost:8080/ejemplo38/ListadoCliente.action
http://localhost:8080/ejemplo38/GestionCliente/ListadoCliente.action

6. Anotaciones de las vistas


La anotacin @ResultPath permite especificar la ruta de almacenamiento de los resultados. Podemos
utilizar esta anotacin antes de la declaracin de la clase de accin.

Cdigo: ejemplo38.acciones.ClienteAccion.java
package ejemplo38.acciones;
...
@ResultPath("/jsp")
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
...

7. Anotaciones de las excepciones


La anotacin @ExceptionMappings se define al nivel de la declaracin de la clase de accin y contiene
anotaciones @ExceptionMapping que permiten definir los tipos de excepciones que se van a
gestionar.
Cdigo: ejemplo38.acciones.ClienteAccion.java
package ejemplo38.acciones;
...
@ExceptionMappings({
@ExceptionMapping(exception =
"java.lang.NullPointerException", result = "success", params =
{"parametro1", "valorparametro1"})
})
@SuppressWarnings("serial")
@InterceptorRef("paramsPrepareParamsStack")
public class ClienteAccion extends ActionSupport implements
Preparable{
...

8. Carga automtica de la configuracin


El complemento Convention puede recargarse automticamente sin reiniciar el contexto de la
aplicacin como el archivo de configuracin struts.xml y la directiva <constant name="struts.devMode"
value="true" />.
Para recargar automticamente el complemento Convention, debemos utilizar la siguiente constante
en el archivo struts.xml de la aplicacin: <constant name="struts.convention.classes.reload"
value="true" /> o en el archivo de propiedades.

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

Gestin de la depuracin de las aplicaciones.


exception
Gestin del procesamiento de las excepciones.
i18n
Gestin de la internacionalizacin para las aplicaciones multilinges.
fileUpload
Gestin de las cargas de archivos.
store
Gestin de los mbitos de aplicacin de los mensajes de aplicacin y de errores.
chain
Gestin del encadenado de acciones y de la transmisin de parmetros.
createSession
Gestin de la sesin del usuario actual.
conversionError
Gestin de los errores de conversin en las acciones.
alias
Gestin de los nombres de parmetros en las consultas HTTP.
checkbox
Gestin de las casillas de verificacin en los formularios.
cookie
Gestin de la cookie del usuario.
workflow
Gestin de la llamada de los mtodos de validacin en las clases de accin.
actionMappingParams
Gestin del mapping de los parmetros de las acciones.

Object-Graph Navigation Language OGNL


OGNL es un lenguaje de descripcin que permite acceder y modificar las propiedades de los objetos
Java. OGNL forma parte del proyecto opensymphony (http://www.opensymphony.com y
http://www.ognl.org/) y puede utilizarse como complemento de las taglibs de Java EE.
OGNL hace referencia a los objetos en funcin de su mbito de aplicacin y de la coleccin de datos
(Context Map):
application: esta coleccin
aplicacinServletContext.

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 #.

de propiedades, podemos utilizar la etiqueta <s:property


con el parmetro value adaptado. Dado que la accin hace
pila, el caracter almohadilla (#) es opcional. Por contra, cuando
en el contexto (session, application o attr) debemos utilizar la

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

atributo lengthy acceder a los elementos por ndice.


Lista de pa&iacute;ses: <s:property value="listaPaises"/><br/>
Lista de pa&iacute;ses: <s:property value="[0].listaPaises"/> <br>
Tama&ntilde;o de la tabla de los pa&iacute;ses: <s:property
value="listaPaises.length"/><br/>
Acceso al primer &iacute;ndice de la tabla: <s:property
value="listaPaises[0]"/><br/>
OGNL tambin permite manipular las listas de valores en forma de claves/valores (Map) mediante el
getter asociado y las distintas propiedades dedicadas. La lista de ciudades declaradas en la clase de
accin se utiliza en la vista mediante OGNL.
// Lista de ciudades
private Map<String,String> listaCiudades=new
HashMap<String,String>();
{
listaCiudades.put("MA", "Madrid");
listaCiudades.put("BA", "Barcelona");
listaCiudades.put("VA", "Valencia");
}
Podemos mostrar directamente el contenido de la lista mediante su nombre, su tamao con el
atributo sizey acceder a los elementos por clave.
Lista de ciudades: <s:property value="listaCiudades"/><br/>
Lista de ciudades: <s:property value="[0].listaCiudades"/> <br>
Tama&ntilde;o de la lista de ciudades: <s:property
value="listaCiudades.size"/><br/>
Acceso al primer &iacute;ndice de la tabla: <s:property
value="listaCiudades[BA]"/><br/>
OGNL tambin permite manipular las listas de objetos (List) mediante el getter asociado y las distintas
propiedades dedicadas. La lista de clientes declarados en la clase de accin se utiliza en la vista
mediante OGNL.
// 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);
}
Podemos mostrar directamente el contenido de la lista mediante su nombre, su tamao con el
atributo sizey acceder a los elementos por clave.
Lista de clientes: <s:property value="listaClientes"/><br/>
Lista de clientes: <s:property value="[0].listaClientes"/> <br>
Tama&ntilde;o de la lista de clientes: <s:property
value="listaClientes.size"/><br/>
Acceso al primer &iacute;ndice de la tabla: <s:property
value="listaClientes[0].getIdentificador()"/><br/>
&iquest;Lista vac&iacute;a? : <s:property value="listaClientes.isEmpty"/><br/>

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&eacute;todo de clase de acci&oacute;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&oacute;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&oacute;n en la sesi&oacute;n:
<s:property value="#session.contrasena"/><br/>
Lectura de la informaci&oacute;n en la sesi&oacute;n:
<s:property value="#session[contrasena]"/><br/>
<hr/>
Lectura de la informaci&oacute;n en el contexto de
la aplicaci&oacute;n (web.xml): <s:property
value="#application.emailContacto"/><br/>
<hr/>
Manipulaci&oacute;n de tablas (Arrays)<br/>
Lista de pa&iacute;ses: <s:property value="listaPaises"/><br/>
Lista de pa&iacute;ses: <s:property value="[0].listaPaises"/>
<br>
Tama&ntilde;o de la tabla de los pa&iacute;ses: <s:property
value="listaPaises.length" /><br/>
Acceso al primer &iacute;ndice de la tabla:
<s:property value="listaPaises[0]"/><br/>
<hr/>
Manipulaci&oacute;n de listas (Map)<br/>
Lista de ciudades: <s:property
value="listaCiudades"/><br/>
Lista de ciudades: <s:property
value="[0].listaCiudades"/> <br>
Tama&ntilde;o de la lista de ciudades: <s:property
value="listaCiudades.size"/><br/>
Acceso al primer &iacute;ndice de la tabla:
<s:property value="listaCiudades[BA]"/><br/>
<hr/>
Manipulaci&oacute;n de listas (List)<br/>
Lista de clientes: <s:property
value="listaClientes"/><br/>
Lista de clientes: <s:property
value="[0].listaClientes"/> <br>
Tama&ntilde;o de la lista de clientes: <s:property

value="listaClientes.size"/><br/>
Acceso al primer &iacute;ndice de la tabla:
<s:property value="listaClientes[0].getIdentificador()"/><br/>
&iquest;Lista vac&iacute;a? : <s:property
value="listaClientes.isEmpty"/><br/>
<hr/>
Ejecutar un m&eacute;todo de clase de acci&oacute;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>

rbol del proyecto ejemplo39

Manipulacin de la informacin con la ayuda de OGNL

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.

También podría gustarte