Esta capa actúa de interfaz entre el cliente y la capa de negocio. Sus responsabilidades comprenden:
Típicamente una aplicación J2EE usará en esta capa servlets para la lógica y el control, páginas JSP para la presentación y entrada de datos y JavaBeans para el almacenamiento temporal de datos. Aunque en principio la capa de presentación debería diseñarse para atender a distintos tipos de clientes (navegadores web, applets, aplicaciones....) aquí nos ceñiremos al caso más común, el de aplicaciones web.
En el Sun Java Center se definen una serie de patrones J2EE orientados a la capa de presentación:
Struts (versión 1.1 en ZIP) es un framework para implementar la capa de presentación , desarrollado en el seno del proyecto Apache Jakarta y que está bastante difundido. Permite implementar aplicaciones web que sigan la arquitectura MVC. Al núcleo central de Struts se han ido incorporando diversos subproyectos, ofreciendo en la actualidad las siguientes funcionalidades (entre otras):
Struts implementa de la siguiente forma cada uno de los componentes de la arquitectura MVC:
web.xml
) para que las peticiones del usuario se redirijan
siempre al servlet.La instalación es sencilla. Basta con colocar en la carpeta WEB-INF/lib
de nuestra aplicación web las librerías (ficheros .jar) que vienen
con la distribución estándar de Struts. Si utilizamos alguna de
las etiquetas propias de Struts, necesitaremos incluir también el correspondiente
descriptor (fichero .tld).
Como se ha comentado, el controlador en Struts es un servlet que recibe las
peticiones del usuario y las redirige a las clases Java que las van a tratar.
Salvo que tengamos necesidades muy especiales, podemos usar como controlador
directamente la clase org.apache.struts.action.ActionServlet
, que
ya viene implementada en la distribución de Struts. Para que todas las
peticiones del usuario se redirijan a este servlet habrá que modificar
el fichero web.xml, como en el siguiente ejemplo (los ejemplos
de este tema muestran código de la aplicación amigosJ2EE):
<!-- servlet que hace de controlador --> <servlet> <servlet-name>controlador</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet> <!-- redirigir ciertas peticiones al controlador --> <servlet-mapping> <servlet-name>controlador</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
En el ejemplo anterior, todas las peticiones que sigan el patrón *.do
se redirigirán al controlador de Struts. Por ejemplo la petición
login.do
será capturada por Struts y redirigida a la acción
de nombre login
.
La asociación entre el nombre simbólico de la acción y
la clase Java que la procesa se realiza en el fichero de configuración
struts-config.xml
(que se colocará en /WEB-INF
).
El mapeado entre acciones y clases se realiza dentro del elemento <action-mappings>
y cada uno de ellos constituye un <action>
. Por ejemplo,
supongamos que la encargada de procesar la acción login
del ejemplo anterior es nuestra clase Java acciones.AccionLogin
.
En el fichero struts-config.xml
se colocarían las siguientes
líneas:
<?xml version = '1.0' encoding = 'ISO-8859-1'?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!--definición de otros elementos del fichero de configuración --> ... ... <!--definición de acciones --> <action-mappings> <!-- hacer login --> <action path="/login" type="acciones.AccionLogin"> <forward name="OK" path="/personal.jsp"/> <forward name="errorUsuario" path="/error.html"/> </action> <!-- definición de otras acciones --> ... </action-mappings>
Como se ve en el ejemplo, los atributos básicos de un action
son su nombre (el path
de la URL solicitada, sin el elemento común
a todos las URL de acción, en este caso, sin el .do
) y
la clase Java que la procesa (type
).
Una acción ejecutada puede tener diversos resultados (por ejemplo, un
login puede ser correcto o no, dependiendo de la contraseña). Cada uno
de ellos se especifica mediante un elemento <forward>
, en
el que se define un nombre simbólico para el resultado de la acción
(name
) y la URL de la página que contiene la vista asociada
(path
).
Las clases encargadas de ejecutar las acciones deben descender de la clase
abstracta org.apache.struts.action.Action
, proporcionada por Struts.
Cuando se ejecuta una acción lo que hace Struts es llamar a su método
execute
, que debemos sobreescribir para que realice la tarea deseada.
Por ejemplo, para el caso de la acción login:
package acciones;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class AccionLogin extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
boolean usuarioOK;
//obtener login y password y autentificar al usuario
//si es correcto, poner usuarioOK a 'true'
...
//dirigirnos a la vista adecuada según el resultado
if (usuarioOK)
return mapping.findForward("OK");
else
return mapping.findForward("errorUsuario");
}
Hay que destacar varias cosas del código de la acción:
Struts incorpora una serie de acciones con comportamientos predefinidos, que
son de uso común en muchas aplicaciones. Por ejemplo, para dirigir al
usuario desde la página de login hacia la de registro de nuevo usuario,
podríamos en principio utilizar simplemente un enlace, pero esto iría
"contra la filosofía" de MVC, en la que todas las peticiones
pasan por el controlador. En su lugar, podemos utilizar una acción de
la clase org.apache.struts.action.ForwardAction
, que redirige una
petición hacia una vista, especificada mediante el atributo parameter
.
Por ejemplo:
<action path="/inicioRegistro" type="org.apache.struts.actions.ForwardAction" parameter="/registro.jsp"> </action>
De esta manera, la petición a la URL inicioRegistro.do
,
se redigiría a la página registro.jsp
. Así
se puede cambiar el flujo de navegación sin tener que cambiar directamente
los enlaces de las páginas. Struts incorpora otras acciones por defecto,
que veremos posteriormente.
Aunque los datos introducidos en formularios podrían obtenerse directamente de la petición HTTP, Struts ofrece un mecanismo alternativo que proporciona distintas ventajas: los ActionForms. Empleando ActionForms podemos conseguir:
Podemos considerar un ActionForm como si fuera un JavaBean que captura los datos de un formulario. Los datos se pueden extraer, validar, cambiar y volver a colocar en otro formulario. No obstante, no tiene por qué haber una correspondencia uno a uno entre un ActionForm y un formulario HTML, de manera que se puede utilizar el mismo ActionForm para englobar varios formularios separados en distintas páginas (caso típico de un asistente, o de un proceso realizado en pequeños pasos, como por ejemplo introducir todos los datos para un pedido vía web: items pedidos, dirección de envío, datos de la tarjeta,...). También se puede reutilizar el mismo ActionForm en distintos formularios que compartan datos (por ejemplo, para dar de alta o modificar los datos de un usuario registrado).
En Struts se pueden definir básicamente dos tipos distintos de ActionForms
struts-config
,
de manera que se pueden añadir o modificar campos minimizando la necesidad
de recompilar código. Mediante la clase DynaValidatorForm
se puede incluso validar datos de manera automática, según las
reglas especificadas en un fichero de configuración aparte.La generación y procesamiento de un ActionForm pasa por varias etapas:
reset()
del objeto, que el desarrollador
puede sobreescribir para "limpiar" sus campos. Esto tiene sentido
si el ActionForm persiste más allá de la petición actual
(lo cual se puede especificar al definirlo).validate()
,
que el desarrollador debe sobreescribir para implementar la validación
deseada. execute()
de la acción y finalmente se muestra la vista asociada a esta, desde
donde también se puede acceder al ActionForm.Los ActionForm se definen dentro del fichero struts-config.xml
,
dentro de la sección <form-beans>
. Cada ActionForm
viene definido por un elemento <form-bean>
. Por ejemplo,
para definir un ActionForm con el nombre simbólico FormLogin y asociado
a la clase Java acciones.formularios.FormLogin
haríamos:
<form-beans> <form-bean name="FormLogin" type="acciones.formularios.FormLogin"> </form-bean> </form-beans>
Para que los datos de un ActionForm sean accesibles a una acción, hay
que definir una serie de atributos dentro del elemento action de struts-config.xml
.
Por ejemplo, para asociar un ActionForm de la clase FormLogin
a
la acción login de los ejemplos anteriores, se podría hacer:
<action path="/login" type="acciones.AccionLogin" name="FormLogin" scope="session" validate="true" input="/index.jsp"> <forward name="OK" path="/personal.jsp"/> <forward name="errorUsuario" path="/error.html"/> </action>
El atributo name
indica el nombre simbólico para el ActionForm.
El scope
tiene el mismo significado que cuando tratamos con JavaBeans.
En caso de que se desee validar los datos, hay que especificar el atributo validate=true
y utilizar el atributo input
para designar la página a la
que se volverá si se han producido errores de validación.
Esta tarea es muy similar a definir un JavaBean. Hay que especificar métodos get/set para cada campo, y colocar la lógica de validación en el método validate(). Por ejemplo:
package acciones.formularios; import org.apache.struts.action.*; import javax.servlet.http.HttpServletRequest; public class FormLogin extends ActionForm { private String login; private String password; public void setLogin(String l) { login = l } public String getLogin() { return login; } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { if ((getlogin()==null)||(getLogin.equals(""))) errores.add(ActionErrors.GLOBAL_ERROR , new ActionError("error.requerido.usuario")); return errores; } }
El método validate() debe devolver una colección de errores (o null), representada por el objeto ActionErrors. Cada error es un objeto ActionError. Para crear un error hay que especificar el mensaje de error, aunque para hacerlo más flexible, el mensaje no es directamente una cadena, sino una clave dentro de un fichero de texto del tipo properties. Por ejemplo, la clave "error.requerido.usuario" significará que debe haber un fichero .properties en el que se especifique algo como:
error.requerido.usuario = es necesario especificar nombre de usuario
Para indicar a struts cómo encontrar el fichero .properties, utilizamos el fichero de configuración struts-config.xml, mediante el elemento <message-resources>. Por ejemplo:
<message-resources parameter="util.recursos"/>
Indicaría a struts que busque un fichero recursos.properties dentro de la carpeta util. Normalmente, se toma como base de la búsqueda la carpeta /WEB_INF/classes, por lo que el fichero buscado será finalmente /WEB-INF/classes/util/recursos.properties.
En formularios con muchos campos, resultará tedioso definir métodos get/set para cada uno de ellos. En su lugar, podemos utilizar DynaActionForms, en los que los campos se definen en el fichero de configuración de Struts. Por ejemplo:
<form-bean name="FormLogin" type="org.apache.struts.action.DynaActionForm"> <form-property name="login" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean>
Para acceder a los campos de un DynaActionForm se utiliza un método genérico get al que se pasa como parámetro el nombre del campo. Algo similar ocurre para cambiar el valor de un campo (método set).
Si deseamos validar un DynaActionForm, tendremos que utilizar una clase propia que herede de ésta, y sobreescribir su método validate(), o mejor aún, podemos validar datos automáticamente utilizando el módulo validator.