Tema 2: La capa de presentación

 

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.

2.1 Patrones J2EE para la capa de presentación

En el Sun Java Center se definen una serie de patrones J2EE orientados a la capa de presentación:

2.3 Struts: un framework para 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):

2.3.1 MVC en Struts

Struts implementa de la siguiente forma cada uno de los componentes de la arquitectura MVC:

2.3.2 Instalar Struts

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

2.3.3 Configurar el controlador

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.

2.3.4 Mapear las acciones

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

2.3.5 Crear las clases que ejecutan las acciones

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:

2.3.6 Utilizar las acciones por defecto

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.

2.3.6 Obtener y validar las entradas del usuario: ActionForms

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

2.3.7 El ciclo de vida de un ActionForm

La generación y procesamiento de un ActionForm pasa por varias etapas:

2.3.8 Declarar ActionForms

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.

2.3.8 Definir un ActionForm básico

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.

2.3.9 Definir un DynaActionForm

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.