Anexo Struts ANEXO: STRUTS Struts es un framework MVC desarrollado dentro de la Apache Software Foundation que proporciona soporte a la creación de las capas vista y controlador de aplicaciones web basadas en la arquitectura Model2. Está basado en tecnologías estándar como Servlets, JavaBeans y XML. Struts proporciona un controlador que se integra con una vista realizada con páginas JSP, incluyendo JSTL. Este controlador evita la creación de servlets y delega en acciones creadas por el desarrollador, simplificando sobremanera el desarrollo de aplicaciones web. En cuanto a la capa vista, Struts permite utilizar entre otras tecnologías páginas JSP para la realización del interfaz. Para facilitar las tareas comunes en la creación de esta capa existen una serie de tecnologías que se integran con Struts: Struts taglib, una librería de etiquetas que proporciona numerosa funcionalidad evitando el escribir código Java en las páginas JSP. JSTL, la librería de etiquetas estándar de Java que añade funcionalidades a la librería de tags de Struts y sustituye alguna de las ya presentes. Tiles, una extensión que permite dividir las páginas JSP en componentes reusables para la construcción del interfaz. Struts Validator, proporciona validación de los formularios basándose en reglas fácilmente configurables. StrutsTestCase para el desarrollo de casos de prueba. Struts Menú, un proyecto que basándose en un fichero de configuración genera vistosos menús. xDoclets para struts que permite la generación automática de datos del fichero de configuración de struts a partir de javadoc de las clases de struts. CSS, hojas de estilo en cascada, que permite la realización de interfaces web de mayor calidad y separa mejor la presentación de los datos. Seguidamente se van a definir un conjunto de buenas prácticas que ayuden a minimizar problemas de desarrollo y mantenimiento, además de mejorar la calidad y rendimiento del software desarrollado. 1.1 Funcionamiento de Struts El punto fuerte del framework Struts es su implementación de la capa de control. La finalidad de Struts es proveer de clases y métodos que disminuyan el trabajo y la complejidad de dicha capa y no se ocupa de ningún modo de las otras dos capas del modelo que pueden ser desarrolladas con múltiples tecnologías. Para ello Struts se apoya fundamentalmente en un fichero de configuración, en dos tipos de clases y en un servlet controlador: Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 1 Anexo Struts Clases que heredan de ActionForm: estas clases son java beans que contienen una propiedad con sus correspondientes métodos get y set por cada campo de formulario que se recogerá en las peticiones, también disponen de un método que es interesante mencionar, el método validate, este método es llamado antes de procesar cualquier acción y se puede sobrescribir para realizar distintas validaciones sobre los dantos introducidos por el usuario. Clases que heredan de Action: estas clases son clases java estándar multi-hilo que procesan la acción solicitada, son las encargadas de interactuar con la capa de negocio y reciben como uno de sus parámetros a la clase ActionForm en caso de haberla. El servlet controlador es la clase RequestProcessor, TilesRequestProcessor en caso de usar el módulo de Tiles, este servlet se encarga de recibir las petición del navegador, y de devolver las páginas adecuadas a este según el resultado de las acciones, en muchas ocasiones es interesante hacer uso de la herencia para poder sobrescribir algunos métodos muy interesantes de este Servlet como son el método proccessRoles en el que se suelen realizar las validaciones de autentificación y autorización de usuarios o el método proccessActionPerform que se encarga de llamar a la clase de acción adecuada. El fichero de configuración de Struts, comúnmente conocido como struts-config.xml contiene la declaración de las clases ActionForm, redirecciones, excepciones globales y de los llamados action-mapping que son la definición del comportamiento que ha de seguir el servlet controlador ante las peticiones del navegador y las respuestas de las acciones y validaciones de formularios. Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 2 Anexo Struts En las siguientes capturas se muestra un ejemplo sencillo de configuración en struts-config.xml de dos acciones que hacen uso de un ActionForm: Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 3 Anexo Struts En el siguiente esquema muestra como es procesada una petición del navegador en una aplicación desarrollada bajo Struts: El gráfico nos indica que se realizan los siguientes pasos entre el momento en el que el navegador hace la solicitud y recibe la página. Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 4 Anexo Struts El navegador envía la solicitud, el Servlet Controlador recibe la petición y consulta el fichero de configuración de struts, en este punto se validan las credenciales y permisos del usuario en caso de haber sobrescrito el método validateRoles de controlador. Si el usuario no tiene credenciales o permisos para ejecutar la acción solicitada se servirá directamente una página de error (punto 7 del diagrama). En caso de que las credenciales y permisos sean correctos, o en caso de que no se estén comprobando, el servlet controlador verificará si la página tiene asociado algún formulario. En caso de ser así se encargará de llamar a los métodos set de cada una de las propiedades que se corresponda con los campos del formulario y llamará al método validate de la clase de actionForm. Si se obtienen errores retornará la página de introducción de datos con los mensajes de error apropiados, en caso de que todo sea correcto llamará desde el método proccessActionPerform a la acción solicitada. Está realizará las operaciones necesarias llamando a las clases de negocio y retornará un objeto de redirección (que normalmente está definido en el fichero de configuración) indicando qué acción se ha de ejecutar. Esta acción suele ser una redirección a la vista correspondiente pero puede ser otra acción encadenada lo que permite una mayor modularización y reutilización de Acciones más sencillas. Como se puede observar existe un punto (número 8) en el que la capa de vista accede al modelo, dichas operaciones han de ser únicamente operaciones de consulta de datos. 1.2 Uso correcto de Struts En este apartado del documento se intentará prevenir sobre los errores que se suelen cometer con más frecuencia en el desarrollo de aplicaciones bajo Struts así como recomendar ciertas prácticas que facilitan el mantenimiento y organización de la aplicación, Uno de los errores más comunes en Struts y en casi cualquier modelo MVC es la inclusión del código perteneciente a una capa, en objetos pertenecientes a otra, por ejemplo introducir código de control o incluso de negocio dentro de los JSPs, para prevenir este problema el programador ha de conocer la filosofía del modelo MVC y tener bien identificados los objetos pertenecientes a cada una de las tres capas, como guías generales se pueden seguir las siguientes reglas: Los JSPs nunca han de incluir código relacionado con el control del flujo de la aplicación, nunca se debe llamar directamente a otras páginas desde un JSP, en vez de eso se ha de llamar a una acción que puede ser un simple forward o un complejo grupo de acciones encadenadas, tampoco ha de relacionarse con la capa de negocio, la única excepción es la consulta de datos que van a ser mostrados en la página. La capa de control está formada por el conjunto de clases que heredan de las clases Action, ActionForm así como el servlet controlador, en estas clases nunca se deben incluir decisiones de visualización ni de negocio, el acceso al modelo de datos se debe efectuar desde clases de la capa de negocio que serán llamadas desde las clases de acción. La capa de Negocio está formada por aquellas clases que se encargan de realizar las operaciones sobre el modelo de datos. Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 5 Anexo Struts Un ejemplo en el que se diese de alta un empleado a través de un formulario podría estar compuesto por las siguientes clases y JSPs: CAPA DE MODELO: o BeanEmpleado.java - java bean de persistencia de hibernate. o Empleado.java - esta clase provee de métodos público que realizan las operaciones necesarias sobre la clase de persistencia. CAPA DE CONTROL: o EmpleadoActionForm.java - esta clase posee las propiedades y métodos necesarios para recoger y validar los datos introducidos desde el formulario. o AltaEmpleadoAction.java - esta clase realiza el alta llamando a un método de la clase Empleado y se encarga de la navegación retornando éxito o fracaso. CAPA DE VISTA: o altaEmpleadoForm.jsp - este jsp muestra un formulario con los campos que es necesario rellenar para completar un alta de empleado, también muestra los errores de validación retornados por el servlet controlador en caso de haberlos. o existo.jsp - este jsp muestra un mensaje de éxito de la operación que se ha realizado. o error.jsp - este jsp se encarga de mostrar los errores no relacionados con la validación de datos. Uno de los problemas a los que se enfrentan las aplicaciones de gran tamaño es la gran cantidad de código duplicado que se puede dar en las clases de control, para evitar esto en la medida de lo posible, manteniendo una buena modularidad de las mismas y facilidad de mantenimiento se recomiendan las siguientes prácticas: Uso de una clase de acción para cada una de las acciones que se realizan en la aplicación, esto simplifica notablemente el mantenimiento de las acciones aunque genera un mayor número de clases de control, es muy normal que se detecten fragmentos de código que se repiten en dichas clases, esto puede ser subsanado heredando de clases propias que a su vez hereden de la clase Action de Struts en vez de hacerlo directamente, esta/s clases de las que nuestras verdaderas acciones heredarán pueden contener aquellas operaciones que realizamos repetidamente en las clases de acción. El caso inverso suele ser adecuado para el desarrollo de los ActionForm, es conveniente desarrollar un solo ActionForm para cada Objeto del modelo de datos con todas las propiedades necesarias en los distintos formularios que actúan sobre él en vez de desarrollar un ActionForm para cada uno de los formularios dado que en la mayoría de ocasiones existe un gran número de campos duplicados, el ejemplo más claro es las operaciones de inserción y modificación de entidades, en estos casos ambos formularios JSP suelen componerse de los mismos campos. Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 6 Anexo Struts Algunos de los errores más comunes y que más problemas pueden causar debido a su difícil detección es la utilización de variables estáticas en las clases de acción, el programador ha de tener presente que las clases de acción son multi-hilo y la misma instancia puede ser utilizada por la aplicación para resolver peticiones distintas, este grave problema no puede ser detectado en los casos prueba y es prácticamente imposible de reproducir en un entorno de desarrollo o integración, es por ello que el desarrollador ha de tener sumo cuidado de no usar este tipo de variables dentro de sus clases de acción. Así mismo el buen uso del control de errores es determinante a la hora del buen funcionamiento y robustez de una aplicación, como norma general se han de considerar los errores según su procedencia y gravedad, una primera división se puede observar entre los errores producidos en la capa del modelo y la capa de control, los errores de la capa de modelo o de negocio han de ser relanzados y el peso del control de los mismos ha de recaer sobre las clases de acción de la aplicación mediante los mecanismos que provee Struts. Ahora hemos de distinguir entre los errores de validación y los errores de acción, los errores de validación han de detectarse en el método validate de nuestros ActionForm, las acciones nunca se deben recibir datos erróneos o sin validar, estos errores han de ser añadidos al objeto ActionErrors que retorna el método validate de nuestro ActionForm al servlet controlador, este se encargará de volver a mostrar el formulario de introducción de datos con los mensajes adecuados. Los errores de acción ya sean excepciones relanzadas desde la capa de negocio o errores de lógica han de ser categorizados según su gravedad y añadidos al objeto ActionMessages que provee Struts según su nivel de gravedad (información, alerta, error o fatal) y retornar el objeto de redirección adecuado (volver al formulario, a una página de éxito, a una de error …) Así mismo se hace necesario en la mayoría de aplicaciones actuales un buen control del acceso y permisos de los usuarios, Struts se apoya en el sistema de autentificación y autorización basado en roles de Java (JAAS) aunque es posible implementar otras aproximaciones como SingleSignOn. En cualquiera de los dos casos es recomendable implementar dichos controles de acceso y permiso basándose en el método processRoles() de nuestro servlet controlador, para ello crearemos una clase que extienda de RequestProcessor (o TilesRequestProcessor en caso de usar Tiles) y que sobrescriba dicho método, en el realizaremos las comprobaciones necesarias, normalmente validar que el usuario está autentificado en el sistema y validar que posee permisos para ejecutar la acción solicitada, dentro del fichero de configuración de struts podemos definir un atributo roles para cada action con una lista de los roles que deseamos que tenga permisos para ejecutar dicha acción. En caso de no cumplir los requisitos para ejecutar dicha acción lanzaremos una excepción propia que definiremos como excepción global en nuestro fichero de configuración de struts y que mostrará automáticamente una página de error, nuestra página de login o cualquiera que le indiquemos. Para validación de permisos sobre los objetos de datos o sobre determinados campos de formularios Struts no provee de ningún sistema lo recomendable en estos casos es implementar dicha lógica en los propios beans de datos para el primer caso y en la propia vista en el segundo. Una de las dudas que surgen al desarrollador durante la implementación de un componente es si un ActionForm debe estar declarado en contexto de request, session o application, La recomendación es utilizar siempre ActionForms en contexto de request, esta recomendación solo ha de saltarse en raras excepciones en las que dicho ActionForm va a tener un uso muy intensivo y mantenerlo en contexto de sesión mejoraría notablemente el rendimiento de la aplicación, en cuyo caso nos asalta la duda de cómo destruirlo cuando ya no es necesario, esta destrucción puede realizarse de dos maneras: Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 7 Anexo Struts La primera es guardar en un HashMap en sesión las referencias a los ActionForms de sesión, extender la clase RequestProcessor y destruir los ActionForms de sesión más antiguos durante las distintas peticiones. Otra forma es destruir todos los ActionForms de sesión cuando se acceda a nueva operación de negocio (por ejemplo desde un menú lateral) Las siguientes recomendaciones se refieren al control de la navegación El uso de global-forwards para redirecciones de páginas habituales hace más fácil el desarrollo y mantenimiento de las redirecciones y navegaciones más comunes. <global-forwards> <forward name="sessionError" path="/login.tiles" /> </global-forwards> Crear clases que hereden de ExceptionHandler para manejar y redireccionar las excepciones más comunes como las de validación de usuarios, fallos en la conexión a BD, etc… <global-exceptions> <exception key="global.error.Message" type="java.lang.Exception" handler="package.CustomExceptionHandler” path="/error.tiles"/> </global-exceptions> Usar ForwarAction para hacer redirecciones simples de una vista a otra que no requieran acciones, con ello evitamos llamadas directas a páginas desde la capa de vista, manteniendo la lógica de navegación en la capa de control. <action path="/home" parameter="/home.tiles" type="org.apache.struts.actions.ForwardAction" scope="request" validate="false"> </action> La validación de datos introducidos por el usuario suele ser una de las partes del desarrollo que más trabajo puede requerir, además, su mala implementación suele ser en gran medida la culpable de las inconsistencias en las bases de datos, Struts posee una extensión fácilmente configurable llamada struts-validator, este plugin de Struts permite definir validaciones de datos de las propiedades de los ActionForms en un fichero de xml, para que estas reglas tengan efecto hemos de hacer que nuestros ActionForms hereden de la clase ValidatorActionForm, configurar dichas reglas mediante un lenguaje de expresiones dentro del XML de configuración e incluir los mensajes de error en nuestro fichero de recursos. Además estas validaciones no solo son realizadas en el servidor si no que puede generarse automáticamente código javascript mediante el uso de una taglib indicando el nombre del ValidatorActionForm y una sentencia javascript en el evento onSubmit del elemento <html:form> esta extensión de Struts permite por tanto una gran flexibilidad y fácil manejo de las validaciones de entrada de datos en los formularios y relega el uso del método validate de la clase ValidatorActionForm a su uso para comprobaciones más específicas. Otra tecnología que es interesante estudiar es el uso de xDoclets para struts, xDoclets permite generar el fichero de configuración de struts a partir de una sintaxis específica dentro del javaDoc de las clases de Acción, ActionForm, clases de excepción, etc… debido a la Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 8 Anexo Struts naturaleza del fichero se hace necesario el uso de ficheros de configuración generados manualmente para su posterior mezcla automática con los datos generados por xDoclets por lo que la decisión de usar xDoclets como herramienta complementaria es más arbitraria que por razones de rendimiento, mejora o ahorro de tiempo. Por ultimo destacar la importancia de los casos de prueba durante el desarrollo de la aplicación, los casos de prueba garantizan en buena medida que la aplicación funciona correctamente ante las situaciones contempladas. Subdirección General de Desarrollo, Tecnología e Infraestructuras. Página: 9