El API Struts
Construir los Componentes de la Vista
Introducción
Este capítulo se enfoca en la tarea de construir los componentes de la Vista de una aplicación, que principalmente está creada usando tecnología JavaServer Pages (JSP). En particular Struts proporciona soporte para construir aplicaciones internacionalizadas, así como para interactúar con formularios de entrada.
Mensajes Internacionalizados
Hace unos pocos años, los desarrolladores de aplicaciones podían tener que contar sólo con los residentes de su país, que normalmente sólo usaban un idioma (a veces dos), y una forma de representar cantidades numéricas, como fechas, números y valores monetarios. Sin embargo, la explosión del desarrollo de aplicaciones basadas en tecnologías Web, así como el despliegue de dichas aplicaciones sobre Internet y otras redes accesibles, han hecho que los límites nacionales sean invisibles en muchos casos. Esto se ha traducido en la necesidad de que las aplicaciones soporten la internacionalización (frecuentemente llamada "i18n" porque 18 es el número de letras entre la "i" y la "n") y localization.
Struts se construye sobre la plataforma Java proporcionada para construir aplicaciones internacionalizadas y localizadas. Los conceptos clava para familiarizarnos con ellos son:
- Locale - La clase fundamental Java que soporta internacionalización es java.util.Locale. Toda Locale representa una elección particular de país e idioma (además de variantes opcionales del idioma), y también un conjunto de asumpciones de formateo para cosas como los números y las fechas.
- ResourceBundle - La clase java.util.ResourceBundle proporciona la herramienta fundamental para el soporte de mensajes en varios idiomas.
- PropertyResourceBundle - Una de las implementaciones estándard de ResourceBundle que nos permite definir recursos usando la misma sintaxis "nombe=valor" usada para inicializar ficheros de propiedades. Esto es muy conveniente para preparar paquetes de recursos con mensajes que son usados en una aplicación Web, porque estos mensajes normalmente están orientados a texto.
- MessageFormat - La clase java.text.MessageFormat nos permite reemplazar porciones de un string de un mensaje (en este cado, recuperado de un paquete de recursos) con argumentos especificados en tiempo de ejecución. Esto es útil en casos donde estámos creando una sentencia, pero las palabras podrían aparecer en diferente orden en diferentes idiomas. El string contenedor {0} del mensaje es reemplazado por el primer argumento, {1} es reemplazado por el segundo argumento, etc.
- MessageResources - La clase Struts org.apache.struts.util.MessageResources nos permite tratar un conjunto de paquetes de recursos como una base de datos, y nos permite solicitar un string de mensajes particular para una Localidad particular (normalmente asociado con el usuario actual) en lugar de la localidad por defecto en la que el propio servidor se está ejecutando.
Por favor, observa que el soporte de i18n en un marco de trabajo como Struts está limitado a la presentación de texto e imágenes internacionalizadas al usuario.. El soporte para localidades específicas métodos de entrada (usado con idiomas como el Japonés, el Chino y el Koreano) se deja al dispositivo del cliente, que normalmente es un navegador Web.
Para una aplicación internacionalizada, seguimos los pasos descritos en el documento Internationalization del paquete de documentación del JDK de nuestra plataforma para crear un fichero de propiedades que contenga los mensajes para cada idioma. Más adelante, un ejemplo ilustrará esto.
Asumimos que nuestro código fuente se ha creado en el paquete com.mycompany.mypackage, por eso está almacenado en un directorio (relativo a nuestro directorio fuente) llamado com/mycompany/mypackage. Para crear un paquete de recursos llamado com.mycompany.mypackage.MyResources, creariamos los siguientes ficheros en el directorio com/mycompany/mypackage:
- MyResources.properties - Contiene los mensajes del idioma por defecto de nuestro servidor. Si el idioma por defecto es Inglés, podríamos tener una entrada como esta:prompt.hello=Hello
- MyResources_xx.properties - Contiene los mismos mensajes en el idioma cuyo código de idioma ISO es "xx". Para una versión Española del mensaje mostrado arriba, tendríamos esta entrada: prompt.hello=Hola. Podemos tener ficheros de recursos para tantos idiomas como necesitemos.
Cuando configuramos el servlet controlador en el descriptor de despliegue de la aplicación Web, una de las cosas que necesitamos definir en un parámetro de inicialización es el nombre base del paquete de recursos para la aplicación. En el caso descrito arriba, sería com.mycompany.mypackage.MyResources.
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>com.mycompany.mypackage.MyResources</param-value>
</init-param>
<.../>
</servlet>
Los importante para este paquete de recursos es encontrarse en el classpath de nuestra aplicación. Otra aproximación es almacenar el fichero MyResources.properties en la carpeta classes de nuestra aplicación. Entonces podremos especificar simplemente "myResources" como el valor de la aplicación. Debemos tener cuidado de no borrarlo si construimos scripts de borrado de clases como parte de una fuente de "limpieza".
Si lo hace, hay una tarea de Ant a ejecutar cuando compilemos nuestra aplicación que copia los contenidos de un directorio src/conf al directorio classes:
<!-- Copy any configuration files -->
<copy todir="classes">
<fileset dir="src/conf"/>
</copy>
Interacciones de Forms y FormBean
Una vez u otra, la mayoría de los desarrolladores web han construido formularios usando las capacidades estándard del HTML, como la etiqueta <input>. Los usuarios esperan que las aplicaciones interactivas tengan ciertos comportamientos, y uno de estos está relacionado con el manejo de errores -- si el usuario comete un error, la aplicación debería permitirle corregir sólo lo que necesita ser modificado -- sin tener que re-introducir cualquier parte del resto de la información de la página o formulario actual.
Conseguir esto es tedioso y aburrido cuando codificamos usando HTML y páginas JSP. Por ejemplo, un elemento de entrada para un campo username podría parecerse a esto (en JSP):
<input type="text" name="username"
value="<%= loginBean.getUsername() %>"/>
lo que es dificil de teclear correctamente, confunde a los desarrolladores HTML que no tienen conocimientos sobre conceptos de programación, y puede causar problemas con editores HTML. En su lugar Struts proporciona una facilidad comprensiva para construir formularios, basada en la facilidad de las Librerías de Etiquetas Personalizadas de JSP 1.1. El caso de arriba sería renderizado de esta forma usando Struts:
<html:text property="username"/>
sin la necesidad de refirnos explicitamente al formulario JavaBean del que se recupera el valor inicial. Esto lo maneja automáticamente el marco de trabajo.
Algunas veces los formularios HTML se usan para cargar otros ficheros. La mayoría de los navegadores soportan esto a través de un elemento <input type="file">, que genera un botón navegador de ficheros, pero es cosa del desarrollador manejar los ficheros entrantes. Struts maneja estos formularios "multipart" de la misma forma que los formularios normales. En la siguiente sección, usaremos Struts para crear un simple formulario de login, y también un simple formulario multiparte.
Construir Formularios con Struts
Un ejemplo completo de un formulario de login ilustrara cómo Struts trata con los formularios de una forma menos dolorosa que usar sólo las facilidades HTML y JSP estandards. Consideremos la siguiente página (basada en la aplicación de ejemplo incluida con Struts) llamada logon.jsp:
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld"
prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld"
prefix="bean" %>
<html:html>
<head>
<title>
<bean:message key="logon.title"/>
</title>
<body bgcolor="white">
<html:errors/>
<html:form action="/logon" focus="username">
<table border="0" width="100%">
<tr>
<th align="right">
<html:message key="prompt.username"/>
</th>
<td align="left">
<html:text property="username"
size="16"/>
</td>
</tr>
<tr>
<th align="right">
<html:message key="prompt.password"/>
</th>
<td align="left">
<html:password property="password"
size="16"/>
</td>
</tr>
<tr>
<td align="right">
<html:submit>
<bean:message key="button.submit"/>
</html:submit>
</td>
<td align="right">
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</td>
</tr>
</table>
</html:form>
</body>
</html:html>
Los siguientes ítems ilustran las características clave del manejo de formularios en Struts, basadas en este ejemplo:
- La directiva taglib le dice al compilador de la página JSP donde encontrar el tag library descriptor para la librería de etiquetas Struts. En este caso, estamos usando bean como el prefijo que identifica las etiquetas de la librería struts-bean, y "html" como el prefijo que identifica las etiquetas de la librería struts-html. Se puede usar cualquier prefijo que deseemos.
- Esta página usa varias ocurrencias de la librería de mensajes para buscar los strings de mensajes internacionalizados desde un objeto MessageResources que contiene todos los recursos de esta aplicación. Para que esta página funcione, se deben definir las siguientes claves de mensajes en estos recursos:
- logon.title - Título de la página de login.
- prompt.username - Un string para pedir el "Username:"
- prompt.password - Un string para pedir la "Password:"
- button.submit - Etiqueta para el botón "Submit"
- button.reset - Etiqueta para el botón "Reset"
Cuando el usuario entra, la aplicación puede almacenar un objeto Locale en la sesión de este usuario. Este Locale se usará para seleccionar mensajes en el idioma apropiado. Esto se hace sencillo de implementar dando al usario una opción para elegir el idioma -- simplemente cambiamos el objeto Locale almacenado, y todos los mensajes se modificaran automáticamente.
- Las banderas de error muestran cualquier mensaje de error que haya sido almacenado por un componente de lógica de negocio, o ninguna si no se ha almacenado ningún error. Esta etiqueta se describirá más adelante.
- La etiqueta form renderiza un elemento <form> HTML, basándose en los atributos especificados. También asocia todos los campos que hay dentro del formulario con un FormBean con ámbito de sesión que se almacena bajo la clave logonForm. El desarrollador de Struts proporciona la implementación Java para este bean de formulario, extendiendo la clase ActionForm de Struts. Este bean se usa para proporcionar valores iniciales para todos los campos de entrada que tienen nombres que corresponden con nombres de propiedades del bean. Si no se encuentra un bean apropiado, se creará uno nuevo automáticamente, usando el nombre de la clase Java especificado.
- El bean formulario también se puede especifiar en el fichero de configuración de Struts, en cuyo caso se pueden omitir el Nombre y el Tipo. (Puedes ver más detalles en Fichero de Configuraciín para Action Mappings).
- La etiqueta text se renderiza como un elemento <input> de HTML del tipo "text". En este caso también se han especificado el número de caracteres y la posición a ocupar en la ventana del navegador. Cuando se ejecuta esta página, el valor actual de la propiedad username del bean correspondiente (es decir, el valor devuelto por getUsername()).
- La etiqueta password se usa de forma similar. La diferencia está en que el navegador mostrará asteriscos, en lugar del valor de entrada, mientras el usuario teclea su password...
- Las etiquetas submit y reset generan los botones correspondientes en la parte inferior del formulario. Las etiquetas de texto para cada botón se crean usando la librería de mensajes, como las peticiones, para que estos valores sean internacionalizados.
Manejar formularios multiparte también es sencillo. Obviamente cuando creamos un formulario multiparte estámos creando un formulario que al menos tiene un entrada del tipo "file". El primer paso para crear el formulario multiparte es utlizar la librería de etiquetas struts-html para crear la página de presentación:
<%@page language="java">
<%@taglib uri="/WEB-INF/struts-html.tld"
prefix="html">
<html:form action="uploadAction.do">
Please Input Text:
<html:text property="myText"><br/>
Please Input The File You Wish to Upload:<br/>
<html:file property="myFile"><br />
<html:submit />
</html:form>
El siguiente paso es crear nuestro bean ActionForm :
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
public class UploadForm extends ActionForm {
protected String myText;
protected FormFile myFile;
public void setMyText(String text) {
myText = text;
}
public String getMyText() {
return myText;
}
public void setMyFile(FormFile file) {
myFile = file;
}
public FormFile getMyFile() {
return myFile;
}
}
Podemos ver los Javadocs del FormFile para ver los métodos que expone para manipular y subir ficheros. También podemos ver los Javadocs para ActionServlet y ActionMapping para ver los distintos parámetros que podemos especificar para cambiar la forma en que se (suben) cargan los ficheros. Básicamente en nuestro método peform() de nuestra clase action deberíamos llamar a ((UploadForm) form).getMyFile() para recuperar el FormFile y hacer lo que queramos con él.
Tipos de Campos de Entrada Soportados
Struts define etiquetas HTML para todos estos tipos de campos de entrada:
- checkboxes
- Campos hidden
- Campos de entrada password
- Botones de radio
- Botones de reset
- Lsitas select con opciones embebidas o ítems de opciones.
- option
- options
- Botones submit
- Campos de entrada de texto text
- textareas
En cada caso, una etiqueta de campo debe estár anidada dentro de una etiqueta form, por eso los campos saben qué bean utilizar para inicializar los valores mostrados.
Otras Útiles Etiquetas de Presentación
Hay varias etiquetas útiles para crear presentaciones, consulta la documentación de cada librería de etiquetas especifica, junto con la Guía de Desarrolladores de Etiquetas, para más información:
- [logic] iterate repite su cuerpo de etiqueta una vez por cada elemento de una colección especificada (que puede ser una Enumeration, un Hashtable, un Vector, o un array de objetos).
- [logic] present dependiendo del atributo que se especifique, esta etiqueta chequea la solicitud actual, y evalua el contenido de los campos anidados de esta etiqueta sólo si hay un valor presente. Sólo se puede usar uno de los atributos en una ocurrencia de esta etiqueta, a menos que utilicemos el atributo property, en cuyo caso también es necesario el nombre del atributo. Los atributos incluyen cookie, header, name, parameter, property, role, scope, y user.
- [logic] notPresent el contrario de la etiqueta present, notPresent proporciona la misma funcionalidad pero cuando el atributo especificado no está presente.
- [html] link genera un elemento <a> HTML como una definición de un enlace o un hiperenlace a la URL especificada, y automáticamente aplica codificación URL para mantener el estado de la sesión en la ausencia del soporte de cookies.
- [html] img genera un elemento <img> HTML con la habilidad de modificar dinámicamente las URLs especificadas por los atributos "src" y "lowsrc" de la misma forma en que se puede hacer con <html:link>.
- [bean] parameter recupera el valor del parámetro solicitado, y define el resultado como un atributo de ámbito de página del tipo String o String[].
Validación Automática de Formularios
Además de las interacciones entre el formulario y el bean descrita arriba, Struts ofrece una facilidad adicional para validar los campos de entrada que ha recibido. Para utilizar esta característica, sobreesribimos el siguiente método en nuestra clase ActionForm:
public ActionErrors
validate(ActionMapping mapping,
HttpServletRequest request);
El método validate() es llamado por el servlet controlador después de que se hayan rellando las propiedades del bean, pero antes de se llame al método perform() correspondiente de la clase action. El método validate() tiene las siguientes opciones:
- Realiza las validaciones apropiadas y no encuentra problemas -- Devuelve null o ejemplares de ActionErrors de longitud cero, y el servlet controlador procederá a llamar al método perform() de la clase Action apropiada.
- Realiza las validaciones apropiadas y encuentra problemas -- Devuelve un ejemplar de ActionErrors conteniendo ActionError's, que son clases que contienen las claves del mensaje de error (dentro del paquete MessageResources de la aplicación) que se deberían mostrar. El servlet controlador almacena este array como un atributo de la solicitud disponible para usarse por la etiqueta <html:errors>, y devolverá el control al formulario de entrada (identificado por la propiedad input de este ActionMapping).
Como se mencionó anteriormente, esta característica es totalmente opcional. La implementación por defecto de validate() devuelve null, y el servlet controlador asumirá que no se requiere que se haga ninguna validación por parte de la clase Action.
Una aproximación común es realizar validaciones iniciales usando el método validate(), y luego manejar la validación de la "lógica de negocio" desde el objeto Action.
Un paquete opcional para realizar validaciones ActionForm está disponible en la web site de David Winterfeldt's.
Otras Técnicas de Presentación
Aunque el aspecto y el comportamietno de nuestra aplicación puede construirse completamente basándonos en las capacidades estándards de JSP y la librería de etiquetas de Struts, deberíamos considerar emplear otras técnicas que mejoren la reutilización de componentes, reduzca los esfuerzos de mantenimiento, y/o reduzca los errores. En las siguientes secciones se explican varias opciones.
Etiquetas Personalizadas Específicas de la Aplicación
Más allá del uso de las etiquetas personalizadas proporcioandas por la librería de Struts, es fácil crear etiquetas que sean específicas de la aplicación que estamos construyendo, para asistirnos en la creación del interface de usuario. La aplicación de ejemplo incluida con Struts ilustra este principio creando las siguientes etiquetas únicamente para la implementación de esta aplicación:
- checkLogon - Chequea la existencia de un objeto session particular, y reenvía el control a la página de logon si no esite. Esto se usa para capturar casos donde un usuario ha colocado un página del medio de la aplicación en su bookmark e intenta saltarse el login. o si ha expirado la sesión de un usuario.
- linkSubscription - Genera un hiperenlace a una página de detalles para una Subscription, que pasa los valores de la clave primaria requerida como atributos de la solicitud. Esto se usa cuando se listan las subcripciones asociadas con un usuario, y proporciona enlaces para editarlas o borrarlas.
- linkUser - Genera un hiperenalce a una página de detalles de usuario, que pasa los valores de la clave primaria requerida como un atributo de la solicitud.
El código fuente de estas etiquetas está en el directorio, src/example, en el paquete org.apache.struts.example, junto con otras clases Java que son usadas por esta aplicación.
Composición de Páginas con Includes
Crear la presentación completa de una página en un fichero JSP (con etiquetas personalizadas y beans para acceder a los datos dinámicos requeridos) es un aproximación de diseño muy común, y fue empleada en el ejemplo incluido con Struts. Sin embargo, muchas aplicaciones requieren mostrar varias porciones de distinciones lógicas de nuestra aplicación en una sóla página
Por ejemplo, una aplicación portal, podría tener alguna o todas estas capacidades funcionales disponibles en la página "home" del portal:
- Acceso a un motor de búsqueda para este portal.
- Uno o más displays "alimentadores de noticias" con los tópicos de interés personalizados desde el perfil de registro del usuario.
- Acceso a tópicos de discusión relacionados con este portal.
- Un indicador de "mail esperando" si nuestro portal proporciona cuentas gratuitas de correo.
El desarroolo de los distintos segmentos de esta site es sencillo si podemos dividir el trabajo y asignar los distintos segmentos a diferentes desarrolladores. Luego, podemos usar la capacidad include de las páginas JSP para combinar los resultados en una sóla página, o usar la etiqueta include proporcionada por Struts. Hay disponibles tres tipos de include, dependiendo de cuando queremos que ocurra la combinación de la salida:
- Una directiva <%@ include file="xxxxx" %> puede incluir un fichero que contiene código Java o etiquetas JSP. El código incluido en el fichero puede incluso referenciar variables declaradas antes en la página JSP exterior. El código se pone en línea dentro de la otra página JSP antes de que sea compilada y por eso puede contener definitivamente más que sólo código HTML.
- El include de action (<jsp:include page="xxxxx" flush="true" />) se procesa en el momento de la solicitud, y es manejado de forma transparente por el servidor. Junto con otras cosas, esto significa que podemos realizar el include condicionalmente anidandolo dentro de una etiqueta como equals usando sus atributos de parámetros
- La etiqueta bean:include toma un argumento "forward" que representa un nombre lógico mapeado al JSP a incluir, o el argumento "id", que representa la variable string del contexto de la pagina a imprimir en la página JSP.
Otra aproximación a esto sería el uso de la librería de plantillas de etiquetas de Struts. Puedes ver más detalles en la Guia del Desarrollador.
Tiles es una alternativa a la Librería de Plantilla de Etiquetas, ofreciendo varias mejoras y nuevas capacidades. Tiles está disponible en el la web site de Cedric Dumoulin.
Componentes de Renderizado de Imágenes
Algunas aplicaciones requieren generar imágenes dinámicamente, como cartas de precios sobre una site de informe de stocks. Normalmente se utilizan dos diferentes aproximaciones para obtener estos requerimientos:
- Renderizar un hiperenlace con una URL que ejecuta una solicitud Servlet. El servlet usa una librería gráfica para renderizar la imagen, selecciona el tipo de contenido apropiadamente (como a image/gif), y envia de vuelta al navegador los bytes de la imagen, que los mostrará como si hubiera recibido un fichero estático.
- Renderizar el código HTML necesario para descargar un Applet Java que cree el gráfico necesario. Podemos configurar el gráfico selecionando los parámetros de inicialización del applet en el código de renderizado, o podemos tener que hacer que el applet haga su propia conexión al servidor para recibir estos parámetros.
Dibujo de Texto
Algunas aplicaciones requieren que se genere texto o marcas, como XML, dinámicamente. Si está siendo renderizada una página completa, y puede sacarse usando un PrintWriter, es muy fácil hacerlo desde un Action:
response.setContentType("text/plain"); // or text/xml
PrintWriter writer = response.getWriter();
// use writer to render text
return(null);










































