Manual de desarrollo de aplicaciones J2EE (0.5Mb)

124
Manual de Desarrollo de Aplicaciones J2EE

Transcript of Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Page 1: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Manual de Desarrollode Aplicaciones J2EE

Page 2: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Manual de Desarrollo de Aplicaciones J2EEVersión 1.5

publicado 26-Junio-2006Copyright © Gobierno del Principado de Asturias 2005

Page 3: Manual de desarrollo de aplicaciones J2EE (0.5Mb)
Page 4: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

iv

Tabla de contenidos1. Presentación de openFWPA .............................................................................................. 1

Introducción .............................................................................................................. 1Visión General de openFWPA ...................................................................................... 2

Empaquetamiento de openFWPA .......................................................................... 4Requerimientos técnicos y de sistema ..................................................................... 5Lista completa de funcionalidades ......................................................................... 5Lista de componentes de terceras partes .................................................................. 6

Arquitectura de referencia ............................................................................................ 8Desarrollo de aplicaciones .......................................................................................... 10

Estructura de directorios ..................................................................................... 10Compilación y despliegue del sistema ................................................................... 11Pruebas unitarias ............................................................................................... 11Instalación de la aplicación de ejemplo (Sample App) .............................................. 11

2. Arquitectura Modelo -Vista- Controlador con openFWPA ..................................................... 13MVC ...................................................................................................................... 13Desarrollo de la Vista ................................................................................................ 14

Aspecto corporativo de las aplicaciones del Principado de Asturias ............................. 14Cascading Style Sheets (CSS) ............................................................................. 14

Desarrollo del Controlador ......................................................................................... 16Declaración de Actions ...................................................................................... 16Jerarquía de Actions .......................................................................................... 17Action Forms ............................................................................................. 29

Desarrollo de lógica de negocio .................................................................................. 31Service Locator ................................................................................................ 31Session EJBs .................................................................................................... 31Value Objects .................................................................................................. 31Excepciones ..................................................................................................... 32Utilidades ........................................................................................................ 33

Otras clases de utilidad .............................................................................................. 33PrincastMessageFmtter ............................................................................ 33PrincastUtils ............................................................................................ 33ParameterCaster ........................................................................................ 33ServletPathUtils ...................................................................................... 33DateDecorator ............................................................................................ 34PrincastPathResolver .............................................................................. 34PrincastOSCacheInterceptor .................................................................. 35Providers ......................................................................................................... 35

3. Implementación de la Arquitectura de Referencia con openFWPA .......................................... 37Inversión de Control en la Arquitectura de Referencia ..................................................... 37Introducción al manejo de la Arquitectura con Spring ...................................................... 37Estableciendo el Datasource ........................................................................................ 38Enlazando con los DAOs ........................................................................................... 39Enlazando con los Managers ....................................................................................... 40Gestión de transacciones ............................................................................................ 41Enlazado con los Delegates ........................................................................................ 42Enlazado con las Actions ........................................................................................... 42Acceso directo al ApplicationContext ................................................................. 43BeanDoc para obtener la gráfica de arquitectura ............................................................. 43Plugin SpringIDE para Eclipse .................................................................................... 43

4. Componentes para acceso a datos ..................................................................................... 45Acceso a Bases de Datos Relacionales .......................................................................... 45

Page 5: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Manual de Desarrollode Aplicaciones J2EE

v

El patrón DAO ................................................................................................. 45Loggeo de las Excepciones en los DAO ................................................................ 47Listas clave/valor .............................................................................................. 47LookupPropertyBean .................................................................................. 49Providers ......................................................................................................... 49Generadores de Secuencia .................................................................................. 49Pools de conexiones .......................................................................................... 50

5. Construccion de informes con openFWPA ......................................................................... 51Generación de Informes ............................................................................................. 51

Creación de los diseños XML. ............................................................................ 51Clases en el openFWPA para la renderización de informes. ...................................... 57

6. Operaciones ................................................................................................................. 61Sistema de Inicialización y Arranque ............................................................................ 61

Declaración de objetos inicializables .................................................................... 61Desarrollo de objetos inicializables ...................................................................... 62Arranque de aplicaciones web ............................................................................. 63Arranque manual .............................................................................................. 63

Sistema de Configuración de Aplicaciones .................................................................... 63Implementación de objetos configurables .............................................................. 64Plugins de Configuración ................................................................................... 66

Logging .................................................................................................................. 72Log4J. Componentes ......................................................................................... 72Configuración ................................................................................................... 74Componentes del openFWPA para Logging. .......................................................... 75Pista de auditoría .............................................................................................. 76Pista de Rendimiento ......................................................................................... 77

Ficheros de Configuración .......................................................................................... 77web.xml ........................................................................................................ 77struts-config.xml .................................................................................... 79

Filtros web .............................................................................................................. 83Filtros del openFWPA. PrincastFilter. ......................................................... 83Configuración del filtro GZIPFilter ................................................................. 85Configuración del filtro SecurityFilter ......................................................... 85Filtro de navegación .......................................................................................... 85Filtro de activación ........................................................................................... 86Filtros de Activación ......................................................................................... 87

Consola de gestión .................................................................................................... 887. Seguridad en aplicaciones con openFWPA ......................................................................... 90

Seguridad ................................................................................................................ 90Autentificación básica ........................................................................................ 90Autentificación basada en formulario .................................................................... 90Autentificación basada en el Filtro de Seguridad del openFWPA ............................... 90Single Sign On ............................................................................................... 103

8. Integración de Sistemas ................................................................................................ 104Tecnologías de Integración ....................................................................................... 104

XML Genérico: Configuración .......................................................................... 1049. Pruebas ...................................................................................................................... 106

Pruebas unitarias ..................................................................................................... 106Jerarquía de clases para las pruebas unitarias ........................................................ 106Convenciones a seguir ..................................................................................... 107Ejecución de las pruebas unitarias ...................................................................... 107Consultando los resultados de los tests ................................................................ 107Pruebas unitarias de objetos que acceden a bases de datos. ...................................... 108Pruebas unitarias de Spring Beans ...................................................................... 112

Page 6: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Manual de Desarrollode Aplicaciones J2EE

vi

Pruebas unitarias de objetos Configurables .................................................... 112Pruebas unitarias en contenedor ......................................................................... 113Pruebas unitarias de informes ............................................................................ 114Más información sobre la implementación de pruebas unitarias ................................ 115

Pruebas de Rendimiento ........................................................................................... 116Modo de Prueba de Rendimiento ....................................................................... 116

Page 7: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

vii

Lista de figuras1.1. Estructura de openFWPA ............................................................................................... 21.2. Arquitectura de Referencia ............................................................................................. 81.3. Estructura de directorios del proyecto Sample App. ........................................................... 102.1. Modelo Vista Controlador ............................................................................................ 132.2. Ciclo petición-acción-jsp de Struts ................................................................................. 132.3. Estructura de capas de las aplicaciones web con openFWPA ............................................... 142.4. Aspecto corporativo del portal princast.es ................................................................ 142.5. Aspecto de la aplicación de ejemplo (Sample App) ........................................................... 142.6. Jerarquía de Actions .................................................................................................... 172.7. Maquinaria de Estados de PrincastAction ................................................................ 182.8. Almacenamiento de la Action .................................................................................... 212.9. Esquema de la PrincastDispatchAction del ejemplo ............................................... 252.10. Esquema de las Actions para listados ............................................................................ 282.11. Estructura de la capa Modelo ...................................................................................... 312.12. Diagrama de Value Objects ........................................................................................ 322.13. Jerarquía de Excepciones ............................................................................................ 323.1. Ficheros de configuración de Beans ............................................................................... 373.2. Ejemplo de gráfica generada con BeanDoc ...................................................................... 433.3. Spring IDE para visualizar ficheros de Spring .................................................................. 444.1. Ejemplo de necesidad de lookup .................................................................................... 494.2. Navegación de una relación en BD con un LookupPropertyBean ......................................... 495.1. Proceso de generación de informes ................................................................................ 516.1. Estructura del sistema de configuración .......................................................................... 646.2. Jerarquía de Plugins .................................................................................................... 686.3. Estados del Sistema de Logging .................................................................................... 747.1. Esquema del sistema de autenticación ............................................................................ 919.1. Set de pruebas unitarias disponibles .............................................................................. 1069.2. Autowiring de DAOs y DataSources ............................................................................ 110

Page 8: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

viii

Lista de tablas1.1. Componentes necesarios para la ejecución del openFWPA ................................................... 71.2. Capas de la Arquitectura de Referencia de openFWPA ........................................................ 9

Page 9: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

1

Capítulo 1. Presentación de openFWPAIntroducción

openFWPA es el framework de desarrollo libre para sistemas de administración electrónica y gobiernoelectrónico desarrollado por el Gobierno del Principado de Asturias. Está basado en la tecnología J2EE ysu objetivo es facilitar el diseño, implementación, implantación y mantenimiento de las aplicaciones.

openFWPA es Software Libre / Open Source y está publicado bajo una doble licencia: LGPL 3.0 (osuperior) y EUPL 1.0 (o superior). La Licencia Pública General Menor del proyecto GNU (LGPL) esuna de las licencias desarrolladas y promovidas por la Free Software Foundation (FSF), y da permisos dereproducción, distribución, modificación y redistribución con copyleft, aunque no se impide la utilizaciónde componentes privativos dentro del sistema. La Licencia Pública de la Unión Europea (EUPL) es unalicencia de software libre con copyleft creada y apoyada por la Unión Europea para el impulso del SoftwareLibre en las administraciones públicas.

Los dos grandes objetivos del framework son:

• Simplificación y homogeneización del proceso de desarrollo de aplicaciones. Para ello openFWPAproporciona una arquitectura reutilizable y un conjunto de herramientas y librerías que implementanalgunos de los componentes más habituales, y de escritura más tediosa, en aplicaciones web. Todo ellodebiera redundar en un menor coste total de propiedad (TCO) de las soluciones desarrolladas sobreopenFWPA.

• Definición de estándares de desarrollo, calidad y aceptación. Se trata de un conjunto de directrices, deobligado cumplimiento, para exigir y garantizar unos niveles mínimos de calidad en las aplicacionesJ2EE [1]. Estos estándares son internos al Principado de Asturias, y son por tanto aplicables solamentedentro de aquellos proyectos desarrollados dentro del Principado de Asturias.

El openFWPA posee las siguientes características:

• Uso de software libre. Hay una serie de proyectos del mundo del software libre que poseen una excelentecalidad, lo que los habilita para participar en aplicaciones de misión crítica.

• Uso de patrones de diseño. El openFWPA promueve el uso de patrones de diseño, en dos sentidosimportantes. En primer lugar, el framework está diseñado y construido sobre patrones. Por otro lado,las aplicaciones que se desarrollan sobre el framework hacen uso asimismo de patrones. Entre otros,las aplicaciones desarrolladas sobre openFWPA siguen una arquitectura Modelo2 , el estándar enaplicaciones web (se trata de una adaptación del famoso MVC).

• Uso de estándares. En el diseño del openFWPA se ha promovido la utilización e incorporación deestándares (por ejemplo, XHTML [4] + CSS [14], etc.). El uso tanto de patrones de diseño comode estándares proporciona importantes ventajas en cuanto a la adaptabilidad y longevidad de lasaplicaciones que los utilizan, al ser más fácilmente mantenidas, extendidas o reutilizadas.

• Aspecto corporativo. Otra característica importante es que las aplicaciones deben integrarse con el restode aplicaciones del Principado de Asturias, tanto a nivel funcional como de aspecto (look & feel). ElopenFWPA incluye un conjunto de plantillas y componentes para construir la capa de presentación deacuerdo a las guías de estilo corporativo del Principado de Asturias. En la versión publicada en portal,estas plantillas pueden modificarse de acuerdo a las necesidades de cada proyecto. Sin embargo, losproyectos internos han de seguir las directrices de estilo corporativo.

• Integración de aplicaciones. La nueva funcionalidad, añadida al openFWPA en las últimas versiones,facilita la integración de las aplicaciones con otros sistemas del Principado de Asturias (sistema deseguridad, comunicaciones, bases de datos corporativas, sistemas de seguridad, sistemas de CRM, etc.).

Page 10: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

2

Esta funcionalidad solo está disponible para los proyectos internos, al carecer de interés en aplicacionesdesarrolladas fuera de la organización.

• Ciclo de vida. Las aplicaciones poseen un ciclo de vida más allá de su desarrollo y puesta en producción.Éstas han de ser configuradas, migradas y operadas en los diversos entornos. Por ejemplo, el frameworkproporciona piezas con una funcionalidad importante que facilita el soporte a la operación. Loscomponentes del framework están preparados para ser gestionados “en caliente” desde una consolade operaciones, y ofrece componentes para aspectos críticos de operación (como gestión adecuada delogging, pistas de auditoría, estadísticas de rendimiento y uso). En general, estos aspectos se incorporanal framework de manera transparente a las aplicaciones. Asimismo, se ofrece (opcionalmente) una seriede APIs avanzadas que permiten a las aplicaciones publicar funcionalidad en la consola de operaciones.

Visión General de openFWPAEl framework de desarrollo J2EE del Principado de Asturias posee la siguiente estructura. Dentro de cadaelemento se muestran los artefactos más relevantes:

Figura 1.1. Estructura de openFWPA

A continuación, se muestran en más detalle los diversos elementos que lo componen.

Aceptación Las aplicaciones desarrolladas con el framework para uso internodel Principado de Asturias deben pasar por una serie de controlesde calidad. A tal efecto, se han desarrollado una serie de guías quedeben seguirse para el desarrollo de estas aplicaciones. Dentro deesta guía de aceptación se define una arquitectura reutilizable quedebieran seguir las aplicaciones.

Entorno de desarrollo El framework de desarrollo es agnóstico en cuanto al entorno dedesarrollo. Éste debiera poseer los siguientes elementos:

• Entorno Integrado de desarrollo. Ofrece un entorno donde losdesarrolladores pueden desarrollar, compilar, depurar y probar elsoftware en construcción.

• Herramientas de despliegue. Permite el despliegue de lasaplicaciones en los distintos entornos (máquina local, máquinasdel entorno de desarrollo, etc.).

• Diseño de informes. Permite la construcción de informes endistintos formatos.

• Gestión de la configuración. Permite la gestión del cambio delos distintos elementos del sistema (código fuente, scripts deconstrucción, pruebas, etc.). Se trata de un sistema de control deversiones.

• Entorno de integración (semi) continua. El entorno de desarrollodebiera ofrecer funcionalidad avanzada de integración continuao semi-continua.

Los proyectos arrancados dentro del Principado de Asturias debenposeer las herramientas definidas en el puesto estándar. Estas

Page 11: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

3

herramientas deben instalarse y configurarse de manera estándar(igual para todas las estaciones de trabajo).

Software Libre Tras la definición de los requisitos del framework en términos deherramientas necesarias para el entorno de desarrollo, directricesde aceptación y diseño del runtime del framework, se realizó laselección de distintos componentes del mundo del código abiertoo gratuito para su inclusión. Por ejemplo, se seleccionó Eclipsecomo Entorno Integrado de Desarrollo, o CVS para el Controlde Versiones. Como elementos más relevantes, destaca el uso deEclipse, Spring o Struts.

Sistema de tiempo de ejecución El sistema de tiempo de ejecución es un conjunto de ficheros .jarque se despliegan con cada una de las aplicaciones del framework.Este sistema sigue las directrices de construcción de aplicaciones,y ofrece componentes reutilizables y una base extensible parael desarrollo basado fuertemente en patrones de diseño. De estamanera, las aplicaciones se reducen en tamaño y complejidad,teniendo todas la misma estructura interna (basada en unaadaptación del patrón MVC llamada Modelo2).

El sistema de tiempo de ejecución emplea diversos componentesdel mundo de código abierto, usando lo mejor de cada uno deellos e integrándolos. Esta aproximación ha facilitado enormementeel desarrollo, y emplea internamente dos frameworks – Struts ySpring.

Módulos de integración Los sistemas a construir dentro del ámbito de la Administracióndel Principado de Asturias presentan una fuerte componente deintegración con otros sistemas. Se han escrito adaptadores para losdistintos sistemas corporativos existentes dentro de la organización,de manera que se simplifica y homogeneíza enormemente las tareasde integración siguiendo un patrón proxy. Estos módulos solo estándisponibles para aquellos proyectos realizados para el Principadode Asturias.

Seguridad Es crucial que las aplicaciones desarrolladas posean un nivelde seguridad suficiente, y que esta seguridad pueda gestionarsecentralmente desde los sistemas corporativos de seguridad. A estefin, se ha desarrollado toda una infraestructura de seguridad sobreestándares (X509, Java Authentication and Authorization Service,Web Services, etc.). Desde el punto de vista de la aplicación, setrata de realizar una parametrización. Toda esta infraestructura esextensible. Dado que determinados proyectos se desarrollan porequipos externos sin acceso a la infraestructura del Principadode Asturias, se incluye un simulador de autenticación, de maneraque determinados escenarios pueden ejecutarse empleando undocumento XML en local como repositorio de credenciales.Asimismo, esta infraestructura es extensible, de manera que puedendesarrollarse adaptadores a otros repositorios (LDAP, etc) enproyectos ajenos al Principado de Asturias.

Operación Las aplicaciones han de ser operadas en los distintos entornos,de manera que el personal de operaciones pueda mantener la

Page 12: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

4

aplicación en funcionamiento. El framework posee una serie deherramientas que facilitan esta operación, como pueden ser:

• Filtro de compresión. El framework proporciona un filtro decompresión de las comunicaciones, de manera que se minimicela comunicación entre el servidor y el cliente.

• Manual de Operaciones. En este documento se describenlas operaciones que pueden realizarse sobre la aplicacióndesplegada.

• Configuración. El framework posee un subsistema flexible deconfiguración, de manera que las aplicaciones se aislan de losrepositorios de configuración.

• Auditoría. Se proporciona funcionalidad para la generación depistas de auditoría.

• Gestión de logs. El framework proporciona un potente sistemade logs, de manera que (por configuración) puede enviarse losmensajes a una BD, a ficheros de texto, XML, HTML, etc. Estaconfiguración puede cambiarse en caliente.

• Consola de Administración. Las aplicaciones desarrolladas conel framework poseen una consola de administración web para lamodificación de los distintos componentes.

• Métricas de uso. Pueden habilitarse diversas métricas de uso dela aplicación, de manera transparente para las aplicaciones.

Documentación Con el framework se entrega toda la documentación necesariapara el desarrollo y operación de aplicaciones. Se entreganuna aplicación de ejemplo (con sus pruebas de rendimientocorrespondientes) y una aplicación en blanco, con la estructura dedirectorios creada.

Soporte Existe un sitio de soporte para la resolución de dudas, incidencias,etc. Este sitio web de soporte permite comunicar al equipo demantenimiento del framework bugs detectados, etc. de maneraque se pueda liberar una nueva entrega (release) con los defectoscorregidos. A tal efecto, se crea un usuario para cada equipode desarrollo que demande soporte, para que puedan realizar unseguimiento consistente de las incidencias que puedan surgir.

Empaquetamiento de openFWPAEl conjunto completo de entregables que puede acompaña al openFWPA es el que sigue. En algunasdistribuciones, pueden no estar disponibles determinados elementos:

• Manual del desarrollador. (Este documento).

• Manual de operaciones.

• Manual de configuración de la seguridad.

• Directrices de aceptación de aplicaciones J2EE del Principado de Asturias.

Page 13: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

5

• Herramientas del desarrollador.

• Guía de estilo del lenguaje Java

• Guía de estilo del lenguaje JSP.

• openFWPA. (binarios)

• Aplicación de ejemplo: SampleApp (binarios y fuentes)

• Aplicación en blanco: App Blank (binarios y fuentes)

Requerimientos técnicos y de sistema

Para la correcta ejecución de las aplicaciones que utilizan el openFWPA es necesario disponer de lossiguientes elementos:

• Librerías de soporte (Ver “Lista de componentes de terceras partes”)

• Servidor de aplicaciones Oracle10G OC4J (versión 10.1.2) con Java JRE 1.4.2

Determinadas partes de la aplicación requieren además, los siguientes componentes:

a. Seguridad:

• Certificado Raíz de la Fabrica Nacional de Moneda y Timbre (FNMT)

• Fichero de certificados (cacerts) en la máquina virtual

Lista completa de funcionalidades

Las funcionalidades soportadas por el openFWPA son las siguientes:

• Extensión del framework Struts [8] con una colección propia de clases Action.

• Acceso a datos a través de objetos DAO.

• Automatización de la carga de consultas SQL desde ficheros de propiedades.

• Plantillas (Tiles) para la creación rápida de páginas JSP.

• Hojas de estilos con el look & feel del Principado de Asturias.

• Facilidades para la generación de informes en formato PDF.

• Etiquetas JSP para la inclusión de listas, barras de navegación, fechas y calendarios en las páginas web.

• Integración de formularios (ActionForm) con entidades (ValueObject) de la aplicación.

• Utilidades para la gestión de tablas de datos en formato {atributo, valor}.

• Facilidades para la obtención de listas paginadas como resultado de consultas.

• Herramienta para la generación automática de menús.

Page 14: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

6

• Infraestructura para pruebas unitarias.

• Infraestructura para pruebas unitarias en contenedor.

• Integración de una consola de monitorización y gestión basada en el estándar JMX [19].

• Jerarquía propia de excepciones.

• Monitorización y control integrado de errores

• Sistema de configuración centralizado.

• Sistema de inicialización y arranque configurable.

• Componentes para el acceso a pools de conexiones.

• Sistema de logging con varios niveles.

• Gestión de logging desde la consola de administración.

• Monitor de rendimiento.

• Sistema de monitorización para las clases Action.

• Generación de estadísticas de acceso a las aplicaciones.

• Generación de estadísticas de excepciones no controladas en las aplicaciones.

• Componente para monitorizar el estado del sistema sobre el que corre la aplicación.

• Infraestructura para filtros gestionados “en caliente”.

• Filtro para compresión GZIP.

• Inicialización de componentes configurables.

• Filtro de seguridad para integración con el Módulo de Autenticación del SAC del Principado de Asturias.

• Conexión con backends del Principado de Asturias (Claves, Terceros, Siebel, Módulo Común de SMS).

Lista de componentes de terceras partes

El framework de desarrollo del Principado de Asturias incorpora componentes de terceras partes. Lasaplicaciones que se construyan sobre el framework han de utilizar las versiones enumeradas en

Page 15: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

7

Tabla 1.1. Componentes necesarios para la ejecución del openFWPA

Componente Proveedor Componentes Versión Descripción

Jakarta CommonsCollections

ASF commons-collections.jar

2.1.1 Conjunto de clasesde utilidad queproporcionancoleccionesadicionales lasestándar de Java.Utilizado porStruts.

Jakarta CommonsLogging

ASF commons-logging.jar

1.0.4 Clases de utilidadpara gestión desentencias delogging. Utilizadopor Struts.

Jakarta CommonsBeanutils

ASF commons-beanutils.jar

1.6 Clases de utilidadpara el manejoy gestión deBeans. Utilizadopor Struts.

Jakarta CommonsDigester

ASF commons-digester.jar

1.6 Librería para lalectura de ficherosde configuraciónXML y creaciónde clases Javaasociadas.Utilizado porStruts.

Jakarta CommonsValidator

ASF commons-validator.jar

1.1.4 Librería pararealizarvalidaciones sobrebeans en general,y formularios enparticular.Utilizado porStruts.

Jakarta CommonsFileUpload

ASF commons-fileupload.jar

1.0 Librería parafacilitar larecepción deficheros por partede servlets yaplicaciones web.

Jakarta CommonsLang

ASF commons-lang.jar 2.0 Conjunto de clasesde utilidad queproporcionanfuncionalidad extraa las clases delpaquetejava.lang

Jakarta CommonsPool

ASF commons-pool.jar 1.0 Proporciona uninterface de poolingde objetos genérico.

Jakarta Regexp ASF jakarta-regexp.jar 1.3 Librería para laevaluación deexpresionesregulares.

Jakarta ORO ASF jakarta-oro.jar 2.0.8 Conjunto de clasespara el procesadode texto queproporcionacompatibilidad conexpresionesregulares. Utilizadopor Struts.

Sun XML API Sun Microsystems xml-apis.jar 1.2.01 Conjunto deinterfaces parael análisis dedocumentos XML

Apache Xerces ASF xercesImpl.jar 1.1 Implementa losinterfaces de lalibrería Sun XMLAPI.

Apache Xalan ASF xalan.jar 2.6.0 Librería para lamanipulación ytransformación dedocumentos XML.

Log4J ASF log4j.jar 1.2.8 Librería para lagestión desentencias delogging.

JUnit junit.org junit.jar 3.8.1 Librería para larealización depruebas unitariasautomatizadas.

DBUnit dbunit.org dbunit.jar 2.0 Extensión JUnitenfocada parapruebas unitariasque requieren lautilización de basesde datos.

Struts ASF struts.jar struts-legacy.jar

1.2 Frameworkorientado a laconstrucción deaplicaciones websiguiendo laarquitecturaModelo 2 (MVC).

Struts Test Case sourceforge.net strutstest.jar 2.1 Extensión de JUnitpara realizarpruebas unitarias enaplicaciones webque utilizan elframework Struts.

Display Tag sourceforge.net displaytag.jardisplaytag.tld

1.0-b3 Conjunto deetiquetas queproporcionapatrones depresentación webde alto nivel paralistas de beans.

JSTL Sun Microsystems jstl.jar 1.1 Conjunto deetiquetas JSPestándar.

Apache StandardTaglibs

ASF standard.jar 1.0.3 Conjunto deetiquetas JSPestándar de laFundación Apache.

Struts Menu sourceforge.net struts-menu.jarstruts-menu.tldstruts-menu-el.tld

2.2 Librería parafacilitar eldesarrollo de menúsen aplicacionesweb.

Apache Ant ASF 1.6.1 Herramienta parala automatizaciónde las operacionesde compilación,construcción ydespliegue deproyectos.

Java SDK Sun Microsystems 1.3.1_11 - 1.4.x Conjunto deherramientas ylibrerías Java.

Oracle AS9i Oracle oc4.jar admin.jar 9.0.3.0.0 - 10.1.2 Servidor deaplicaciones deOracle.

JMX ReferenceImplementation

Sun Microsystems jmxri.jarjmxgrinder.jarjmxtools.jar

1.0 Librería para lagestión dinámica deaplicaciones Java(sólo es necesariacon OC4J 9.0.3).

Base de DatosOraclae

Oracle 8.1.7.3 Sistema de Gestiónde Bases de Datos.

Jasper Reports sourceforge.net jasperreports.jar 0.6.0 Herramienta parala generación deinformes endiferentes formatos:PDF, HTML, XLS,CSV y XML.

JSSE Sun Microsystems jsse.jar 1.0.3_03 Proporcionasoporte para laconexión bajoprotocolo SSL.

JAAS Sun Microsystems jaas.jar 1.0 Proporcionasoporte paraautentificación yautorización.

JCE Sun Microsystems jce.jarlocal_policy.jarsunjce_provider.jarUS_export_policy.jar

1.2.2 Proporcionasoporte para usode protocolos deencriptación.

JDBC Sun Microsystems jdbc2_0-stdext.jar 2.0 Extensiones JDBCpara la compilacióncon versión1.3.1_11 de la JDK.

Spring Spring Framework spring.jar 1.2.6 Framework IoC.

Java Monitor API JAMon API JAMon.jar 1.1.2 Librería demonitorización ymedición detiempos

Direct WebRemoting (DWR)

Get Ahead dwr.jar 1.0 Librería de AJAX

Page 16: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

8

Arquitectura de referenciaEl framework de desarrollo del Principado de Asturias hace un uso intensivo de Patrones de Diseño. A finde lograr una homogeneidad efectiva en las aplicaciones realizadas en el marco del Principado de Asturias,se propone una Arquitectura de Referencia que describe la arquitectura de las aplicaciones desarrolladascon el openFWPA. El uso de esta arquitectura de referencia es obligatorio, al ser parte de las Directricesde Aceptación de aplicaciones.

Una arquitectura de referencia es una descripción de los elementos de los que se compone una aplicación, yde las relaciones entre estos elementos. Manejar arquitecturas de referencia es tremendamente beneficioso,ya que permite: Homogeneizar las aplicaciones. Al usar la arquitectura de referencia, las aplicacionesson estructuralmente iguales, cambiando sólo los elementos en concreto, pero no la forma que tienen derelacionarse. Esto tiene un impacto directo en el esfuerzo en desarrollo y mantenimiento.

Extender las mejores prácticas y tecnologías. La arquitectura de referencia ha de mantenerse, de maneraque se vayan introduciendo cambios basados en cambios tecnológicos o en el establecimiento de mejoresprácticas.

La arquitectura de referencia J2EE propuesta se basa en el patrón Modelo2 sobre una disposición en capas,y puede verse en el siguiente diagrama:

Figura 1.2. Arquitectura de Referencia

El concepto de separación en capas está claramente definido en esta arquitectura de referencia:

La comunicación entre capas sólo puede existir a través de a) interfaces, b) Objetos de Datos (ValueObjects). Los elementos de la arquitectura de referencia pueden verse en la siguiente tabla:

Page 17: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

9

Tabla 1.2. Capas de la Arquitectura de Referencia de openFWPA

Elemento Descripción Patrones relevantes

Capa de Acceso a Datos Encapsula toda la lógica de accesoa datos. Asimismo, encapsula losaccesos a sistemas remotos.

Data Access Object Proxy ValueObject Absctract Factory

Capa de Objetos de Datos Representa las entidades delmodelo, como objetos JavaBean ysin lógica de negocio.

Value Object

Capa de Negocio Implementa toda la lógica denegocio, implementada comoprocesos sobre la capa de Accesoa Datos. Oculta toda la comlejidada la capa superior.

Business Delegate Façade

Capa de Controlador Transforma eventos en la vista aeventos en el modelo, y viceversa.

MVC Command

Capa de Vista Presenta el modelo al usuario,y comunica sus acciones alcontrolador

MVC

Filtro web Permiten filtrar las peticiones delos clientes, a fin de propor-cionarautenticación, asertos a toda laaplicación, compresión de datos,etc.

Chain Of Responsibility

Datasource Gestiona pools de conexiones, afin de no crear una conexión porcliente a Base de Datos u otrosrepositorios.

Gestión de sesión Gestiona la sesión de los clientes,de manera que desconecta a losinactivos.

Sistema externo Representa cualquier sistema aintegrar a través de un interfazbien definido.

Dado el número de librerías que implementan el patrón MVC, tiene todo el sentido usar alguna de ellasen vez de implementarlo para un proyecto. El openFWPA da soporte para este patrón. Caso de ser unaaplicación J2EE no construida sobre el openFWPA, debiera hacer uso del framework Struts.

Una vez fijada la arquitectura de referencia, se ha acudido al mundo del software libre buscandoimplementaciones de los elementos reseñados en ella. Por ejemplo, para la capa del controlador se haoptado por usar una implementación de un proyecto del software libre en vez de proceder a realizar unaimplementación propia. Asimismo, el openFWPA ofrece soporte en la implementación de todas las capas,desde acceso a datos hasta presentación.

En general, prácticamente todas las librerías utilizadas por el openFWPA provienen de la Apache SoftwareFoundation (ASF) [5] y también pueden ser consideradas como estándares “de facto” en sus respectivasáreas. Las librerías proporcionadas por la ASF, son de código libre y abierto, están mantenidas por unnutrido grupo de desarrolladores de todo el mundo y son muy habituales en proyectos de desarrollo(principalmente Java) de cualquier índole.

Page 18: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

10

Desarrollo de aplicacionesAntes de comenzar el desarrollo de una aplicación web con el openFWPA, es importante tener en cuentalas directrices y recomendaciones que se indican en este apartado.

Estructura de directorios

Las aplicaciones definirán una estructura de directorios siguiendo la plantilla:

• build, target: Contendrá los .class generados para el proyecto.

• db: Contendrá los scripts de creación de la base de datos o la propia base de datos. En caso de darsesoporte a más de una base de datos o más de una versión, ha de crearse un directorio para cada unade las BD.

• config: Contendrá los ficheros necesarios para la creación del fichero EAR necesario para desplegarla aplicación en el contenedor J2EE (como por ejemplo application.xml), así como los ficheroscon la información necesaria para la configuración de recursos que necesitará la aplicación (por ejemploDataSources. En este caso podría incluirse un fichero data-sources.xml con la información aañadir al fichero data-soruces.xml del contenedor J2EE para la definición de los mismos).

• src: Este directorio contendrá dos subdirectorios:

• java: Contendrá los ficheros de código fuente Java y de recursos de la aplicación, y el ficherobuild.xml.

• webapp:

• pages: Contendrá el resto de ficheros de la aplicación: páginas HTML, JSP, imágenes, hojas deestilo CSS, etc.

• WEB-INF: Contendrá los ficheros de configuración XML (web.xml, struts-config.xml,validation.xml, etc.), DTDs y TLDs.

• lib: Contendrá las librerías que será necesario distribuir con la aplicación, puesto que no estaránincluidas en el contenedor J2EE.

• ejbApp:

• META-INF: Contendrá el fichero de MANIFEST.MF, así como los ficheros necesarios para eldespliegue de EJBs en caso de que sean utilizados en la aplicación. Estos ficheros sería ejb-jar.xml, orion-ejb-jar.xml, … y cualquier otro fichero que fuera necesario.

• dist: Se trata de un directorio temporal empleado para la generación de los jars, ears,… necesariospara el proyecto.

• javadoc: Contiene el javadoc generado con el target de Ant incluido al efecto.

Como ejemplo se muestra la estructura de la aplicación de ejemplo (Sample App):

Figura 1.3. Estructura de directorios del proyecto Sample App.

Page 19: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

11

Compilación y despliegue del sistemaPara la compilación y el despliegue de aplicaciones se utilizará la herramienta Ant [10] (http://ant.apache.org). Ant es una herramienta de construcción basada en Java similar al clásico Make.Los ficheros de configuración de Ant están escritos en XML y tienen por nombre build.xml. Cada unode ellos contiene un project y al menos un target (el default, que será el que se ejecutará si no se especificaningún otro en la llamada a Ant). Cada uno de ellos será el encargado de la compilación, empaquetado,despliegue en el contenedor J2EE, etc. de la aplicación.

Con las aplicaciones en blanco (Blank App) de ejemplo (Sample App) de distribuye un ficherobuild.xml. Los targets más relevantes son los siguientes:

• all (default): Realiza lo mismo que make-ear.

• compile: Compila los ficheros fuente Java de la aplicación.

• javadoc: Genera la documentación Javadoc.

• test.unit: Lanza las pruebas unitarias utilizando JUnit [11]. Busca en los paquetes de código fuente lasclases cuyo nombre termine en Test (según el convenio de nombrado de JUnit), ejecuta las pruebas ygenera informes con los resultados de las mismas en formato HTML.

• make-war: Genera un fichero WAR (Web Application Archive) con la aplicación, necesario para laposterior generación del fichero EAR.

• make-ear: Genera un fichero EAR (Enterprise Application Archive) con la aplicación, que podrá serdesplegado en un contenedor J2EE.

• deploy.localhost: Despliega la aplicación en el contenedor J2EE instalado en la máquina local.

• undeploy.localhost: Desinstala la aplicación del contenedor J2EE instalado en la máquina local.

• deploy.desa: Despliega la aplicación en el contenedor J2EE instalado en la máquina cuya IP estácontenida en la variable desa.test.host.

• undeploy.desa: Desinstala la aplicación del contenedor J2EE instalado en la máquina cuya IP estácontenida en la variable desa.test.host.

• new: Crea un nuevo proyecto a partir del proyecto actual, para ello es necesario pasarle el nombre delproyecto nuevo mediante el parámetro -Dapp.name=proyectoNuevo. Esto copiará el proyectoactual, al mismo nivel de directorio y sustituye el nombre del proyecto en los ficheros de configuraciónque sea posible.

Pruebas unitariasEs muy recomendable la implementación de pruebas unitarias, al menos para todos los componentescríticos de la aplicación. Es también recomendable, en aplicaciones web, implementar pruebas unitariaspara todos los objetos de acceso a datos (DAO). Para facilitar esta tarea se puede utilizar la librería dbUnity la clase PrincastDatabaseTestCase, suministrada en el openFWPA.

Instalación de la aplicación de ejemplo (Sample App)Para instalar la aplicación de ejemplo (Carrito) se deben seguir los pasos descritos en los siguientesapartados.

Page 20: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Presentación de openFWPA

12

Configuración de la seguridad

Para habilitar la seguridad en la aplicación de ejemplo deben seguirse los pasos especificados en eldocumento [Manual de Operaciones].

Configuración de la base de datos

Esta aplicación utiliza una base de datos MySQL. Se ha de copiar el driver JDBC para MySQL (mysql-connector-java-3.0.12-production-bin.jar) en el directorio {OC4J_HOME}/j2ee/home/applib. Para instalar la base de datos es necesario ejecutar la tarea ANT createdb incluida enbuild.xml (quizá sea necesario cambiar el usuario y contraseña para conectarse a MySQL).

A continuación se edita el fichero data-sources.xml, que se encuentra en el directorio{OC4J_HOME}/j2ee/home/config, y se le define un nuevo origen de datos para la aplicación añadiéndoleel siguiente código al fichero:

<data-source

class="com.evermind.sql.DriverManagerDataSource"

name="MySQLDS"

location="jdbc/CarritoDS"

xa-location="jdbc/xa/CarritoXADS"

ejb-location="jdbc/MySQLDS"

connection-driver="org.gjt.mm.mysql.Driver"

username="admin"

password=""

url="jdbc:mysql://localhost/carrito"

inactivity-timeout="30"/>

Si el servidor de base de datos no se encuentra en la misma máquina que OC4J, sustituir localhost por elnombre o la dirección a dicha máquina. Hacer que los campos username y password coincidan con los dealgún usuario de MySQL con privilegios para acceder a la base de datos.

Llegados a este punto es necesario re iniciar el OC4J. Una vez re iniciado ejecutar el target deploy.localhostdel fichero build.xml, si se ejecuta desde la máquina donde está instalado OC4J, o deploy.desa si se tratade una máquina remota (en este caso cambiar la variable desa.test.host del fichero build.xml debe apuntara la IP del servidor).

Una vez completado el proceso de instalación, la aplicación estará disponible desde la dirección http://localhost:8888/carrito.

Para tener acceso al sistema puede utilizar como parámetros de autenticación los siguientes:

• Identificador de usuario: cliente.

• Contraseña: cliente.

Page 21: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

13

Capítulo 2. Arquitectura Modelo -Vista-Controlador con openFWPAMVC

El patrón MVC – Model 2 puede ser visto como una implementación del lado del servidor del patrón dediseño Modelo-Vista-Controlador (MVC). Este patrón describe cómo debe implementarse una aplicacióncon tres elementos básicos:

Modelo Se trata de las entidades del dominio del problema, implementadas contotal independencia de su presentación.

Vista (Presentación) Esta capa se encarga de mostrar las entidades del modelo al usuario. Enel openFWPA, se implementa esta capa sobre la tecnología JSP. En estacapa, no hay lógica de negocio

Controlador Traduce eventos/operaciones realizadas sobre la vista a invocaciones demétodos en el modelo. En el openFWPA se emplean servlet para estacapa. Básicamente, en esta capa se procesa la petición de entrada deun cliente, se accede a las entidades del modelo y se coloca cualquierelemento a pasar a la vista en algún ámbito de aplicación (request, session,etc.). Asimismo, dispara un evento que se mapeará a una página jsp quemostará los resultados.

Esta estrategia da lugar a una separación entre presentación y contenido, produciéndose una claradefinición de los roles y responsabilidades de los desarrolladores y diseñadores de páginas, en los equiposde programación. De hecho, cuanto más compleja sea la aplicación, mayores son los beneficios de utilizarla arquitectura de Modelo 2.

Figura 2.1. Modelo Vista Controlador

El proyecto Struts de la Apache Software Foundation es una implementación del MVC Modelo 2.El núcleo del framework Struts es una capa de control flexible basada en tecnologías estándar comoservlets, JavaBeans, ResourceBundles y XML, así como varios paquetes del proyecto Jakarta Commons.(http://jakarta.apache.org/commons).

Struts suministra su propio componente controlador (Controller) y se integra con otras tecnologíaspara proporcionar el Modelo y la Vista. Para el Modelo, Struts puede interactuar con tecnologías de accesoa datos estándar, como JDBC y EJB, así como con la mayoría de paquetes de terceras partes, comoHibernate, iBATIS, u Object Relational Bridge. Para la Vista, Struts trabaja bien con JSPs, incluyendoJSTL y JSF, así como con Velocity, XSLT y otros sistemas de presentación. Actualmente, el frameworkdel Principado de Asturias sólo da soporte a JDBC y JSP.

La figura siguiente muestra como es el ciclo petición-accion-jsp del framework Struts:

Figura 2.2. Ciclo petición-acción-jsp de Struts

Para obtener información más detallada sobre Struts consultar el tutorial que se adjunta en ladocumentación de openFWPA.

Page 22: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

14

Figura 2.3. Estructura de capas de las aplicaciones web con openFWPA

Desarrollo de la Vista

Aspecto corporativo de las aplicaciones del Principadode Asturias

Las aplicaciones construidas bajo los estándares del openFWPA de desarrollo J2EE se integrarán en elportal del Principado de Asturias ya existente (http://www.princast.es) tanto en internet comointranet. Por lo tanto debe respe-tarse el “look & feel” del portal en la medida de lo posible. Seestablece como premisa la construcción de un “look & feel” ligeramente diferenciado, pero que a suvez respete la imagen corporativa del Principado de Asturias.

Para lograr este objetivo, se ha partido de la hoja de estilos general.css propiedad del Principado deAsturias, y en base a ella se han desarrollado nuevas hojas de estilos que establezcan el aspecto de la vistade las aplicaciones construidas bajo el framework. Estas hojas de estilo permiten separar las instruccionesde formateo (posición, color, tamaño, etc) del código HTML generado por la aplicación. Esto ofrece unamayor sencillez al desarrollo y una mayor adaptabilidad al cambio - en caso de ocurrir cambio de imagencorporativa, se minimiza el ámbito del cambio unas pocas hojas de estilo CSS.

Figura 2.4. Aspecto corporativo del portal princast.es

Figura 2.5. Aspecto de la aplicación de ejemplo (Sample App)

Cascading Style Sheets (CSS)La aplicación ejemplo (Sample App) maneja 5 hojas de estilos CSS. Debe tomarse esta implementacióncomo referencia de posicionamiento y formateo de textos, bloques, párrafos, etc. En general, se prohíbeel uso de directrices de estilo dentro del código HTML. Cualquier estilo o posicionamiento de bloquesdeberá ir contenido en una hoja de estilos CSS.

Hojas de estilo en la aplicación de ejemplo (Sample App)

Las hojas de estilo son enlazadas a través de la página head.jsp. En caso de necesitar nuevas hojas deestilo, se utilizará este componente para hacerlo, de forma que esta tarea quede totalmente centralizada.El código actual de la página head.jsp es:

<!-- Css basicas --><link rel="stylesheet" type="text/css" href="../../css/general.css" /><link rel="stylesheet" type="text/css" href="../../css/position.css" /><link rel="stylesheet" type="text/css" href="../../css/princast-ui.css" />

<!-- Css para el menú Tabs --><link rel="stylesheet" type="text/css" href="../../css/tabs.css" />

Page 23: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

15

<!-- Css para los listados --><link rel="stylesheet" type="text/css" href="../../css/displaytag.css" />

<!-- Css especifica de la aplicacion --><link rel="stylesheet" type="text/css" href="../../css/carrito.css" />

Las hojas de estilo manejadas por la aplicación de ejemplo SampleApp son:

general.css proviene de la hoja de estilos de referencia con el mismo nombre, incluidaen el portal princast.es. Ha sufrido ligeras modificaciones para adaptarse a lasnecesidades del framework PA. Establece los estilos para los elementos máscomunes de una página HTML (enlaces, tablas, celdas, párrafos, listas, textos…)

position.css define el posicionamiento de los bloques <div> dentro de la página. La estructurade una página se ha definido en base a bloques, de los cuales no todos tienenporque aparecer, según las necesidades de página. Para más información, véaselos apartados correspondientes a los layouts tiles.

princast-ui.css hoja de estilos para el estilo de los componentes de las etiquetas de princast paralas páginas

tabs.css hoja de estilos para el tabbed menu.

displaytag.css hoja de estilos exclusiva para el aspecto de las tablas generadas por el tagdisplaytag (Ver ???). El displaytag genera listas paginadas.

carrito.css Hoja de estilo para la ubicación y formato de componentes específicos de laaplicación de ejemplo.

Estos ficheros CSS definen los estilos para aplicaciones de tramitación. Además de estas hojas de estilo,se incluyen en el openFWPA ficheros CSS que definen estilos para aplicaciones de portal. Estas hojas deestilo son: componentsPortal.css, displaytagPortal.css y carritoPortal.css.

Según lo expuesto, el código de las páginas JSP debe reducirse al mínimo imprescindible, obteniendo asíun código mucho más claro y mantenible.

Ejemplo: código JSP del cuerpo de una página de la aplicación Sample App:

<%@ page errorPage="/pages/errorEnJSP.jsp" %><%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %><%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %><%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %><%@ taglib uri="http://displaytag.sf.net" prefix="display" %><%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %><html:xhtml /><div id="cuerpo"> <ui:errors/> <ui:box bodyId="productos_box"> <ui:box-caption headingLevel="1"> <bean:message key="productos.box.caption" /> </ui:box-caption> <display:table name="sessionScope.ListaProductoKey" id="listProd"

Page 24: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

16

pagesize="3" export="false" sort="page" requestURI="../../../action/viewlistaproducto?paginate=true" summary="Listado de productos" > <display:column> <%=listProd_rowNum%> </display:column> <display:column titleKey="productos.column.name" sortable="true" > <bean:define id="nombreProducto" name="listProd" property="name" toScope="page"/> <html:link action="/viewdetalleproducto" paramId="id" paramName="listProd" paramProperty="id" title="<%=\"Visualice el detalle de \" + nombreProducto%>" > <bean:write name="listProd" property="name" /> </html:link> </display:column> <display:column titleKey="productos.column.description" property="description" /> <display:column titleKey="productos.column.basePrice" property="basePrice" sortable="true" headerClass="sortable"/> <display:column> <bean:define id="url" name="listProd" property="smallImageURL" /> <bean:define id="nombre" name="listProd" property="name" toScope="page"/> <html:img styleClass="imagen_producto" src="<%=url%>" alt="<%=\"Imagen de \" + nombre%>"/> </display:column> <display:column titleKey="productos.column.moreInfo" sortable="false" class="centered" headerClass="centered"> <html:link action="/viewdetalleproducto" paramId="id" paramName="listProd" paramProperty="id" titleKey="label.masinformacion"> <html:img src="../../images/icon_info_sml.gif" altKey="label.masinformacion" styleClass="no_border" /> </html:link> </display:column> </display:table> </ui:box>

<html:img styleClass="carrito_image" src="../../images/productos.jpg" alt=""/>

<span id="pdf_link"> <html:link styleClass="enlace_imagen enlace_pdf" action="/viewlistaproductopdf" titleKey="productos.descargaPDF.title"> <bean:message key="productos.descargaPDF"/> </html:link> </span></div>

El código anterior responde a la forma en que se construye un cuerpo de página. No se ha utilizado enningún caso directrices de estilo o posicionamiento dentro de este código, y en esta forma resulta másclaro, donde se atiende únicamente a lo que debe mostrar la página y no a como y dónde debe mostrarlo.

Desarrollo del Controlador

Declaración de ActionsDesde la versión 1.5, las aplicaciones desarrolladas con el openFWPA, están basadas en el frameworkSpring. Para poder inyectar dependencias en las Actions de las aplicaciones, es necesario que éstassean definidas como beans de Spring. En concreto, las definiciones de Actions se realizarán ahora enel fichero: beans/web/action-beans.xml, este fichero se debe ubicar en el CLASSPATH.

<bean id="login" class="es.princast.sampleapp.web.action.LoginAction" singleton="false">

Page 25: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

17

</bean> <bean id="logout" class="es.princast.sampleapp.web.action.LogoutAction" singleton="false"> <property name="carritoDelegate"><ref bean="carritoDelegate" /></property> </bean>

Mientras que las Actions (clases) se declaran ahora en el fichero action-beans.xml, todos los aspectosrelativos a la navegación (forwards), mappings, formularios, etc. se sigue definiendo en el fichero struts-config.xml.

En cada uno de los <action-mappings>, se debe inidcar el identificador (id) del bean que implementala lógica de la Action, utilizando el atributo "type". Si el valor de este atributo es el identificadorde un bean (de la clase Action), se tomará dicho bean para procesar las peticiones. Si el valor delatributo "type" es el nombre de una clase (una Action), se instanciará normalmente (como en versionesanteriores del openFWPA).

<action path="/login" type="login" scope="request" validate="false" input="/pages/login.jsp"> <forward name="success" path="/action/viewperfil" redirect="true" /> <forward name="failure" path="/action/login" redirect="true" /> </action>

<action path="/logout" type="logout" scope="request"> <forward name="success" path="/action/login" redirect="true" /> </action>

En el código anterior se puede ver cómo se realiza el mapeo, en el fichero struts-config.xml, delas Actions definidas más arriba como beans de Spring.

Atención

Para poder realizar este tipo de mapeos es necesario utilizar como controlador (Controller)la clase PrincastRequestProcessor:

<controller processorClass="es.princast.framework.web.action.PrincastRequestProcessor" />

Jerarquía de ActionsEn el openFWPA se proporciona un conjunto de Actions de Struts. Estas Actions definen un nuevociclo de ejecución diferente del existente en las Actions típicas de Struts (ver más adelante). Lasaplicaciones que utilicen el openFWPA deben, obligatoriamente extender las Actions del Framework.Además de ser una imposición, las Actions del openFWPA ofrecen funcionalidad de uso habitual enlas aplicaciones web.

Figura 2.6. Jerarquía de Actions

PrincastAction

La clase base de la jerarquía es PrincastAction. Es una clase abstracta que implementa una máquinade estados de la que podrán hacer uso el resto de Actions. Define métodos que deben ser sobrescritospor las actions de la aplicación. Estos métodos sobrescritos serán invocados por el framework para darrespuesta a una solicitud de un cliente, y en un orden preestablecido. Este orden se presenta como unesquema de la máquina de estados:

Page 26: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

18

Figura 2.7. Maquinaria de Estados de PrincastAction

Cada uno de los métodos que aparecen en la figura anterior tiene un cometido en particular. Este cometidoes el siguiente:

preProcess () Se emplea para comprobar las precondiciones que debe cumplir laPrincastAction. En caso de que no se cumpla alguna precondiciónse debe dejar un registro de ello mediante la creación de un error o unmensaje, dependiendo de la gravedad del mismo. Al dejar constanciade la incidencia se redireccionará el flujo de ejecución hacia unapágina de error o a una de alerta, invocándose findFailure() yfindAlert(), respectivamente. La forma de crear una incidencia sedetalla en la sección 4.5.1.1.1.

executeActionLogic () Implementa la lógica de negocio de la PrincastAction. Éste seráel método sobrescrito de forma obligatoria por todas las acciones quehereden de PrincastAction.

catchException() Se encarga del tratamiento de cualquier excepción que se pueda lanzardurante la ejecución de la lógica de negocio de la PrincastAction.Si no se quiere que la excepción sea lanzada de nuevo debe notificarsesu tratamiento mediante la llamada al método unsetException().De esta forma se entenderá que todo el tratamiento necesario ya ha sidollevado a cabo y la excepción no será elevada.

Page 27: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

19

postProcess() Se emplea para comprobar las poscondiciones que debe cumplir laPrincastAction. En caso de que no se cumpla alguna poscondiciónse debe dejar constancia de ello mediante la creación de un error o unmensaje. Al dejar constancia de la incidencia se redireccionará el flujode ejecución hacia una página de error o a una de alerta, invocándosefindFailure() y findAlert(), respectivamente. La forma decrear una incidencia se detalla en la sección 4.5.1.1.1.

findFailure() Redirecciona a una página de error. Por defecto, la redirección se hace alo que se indique en el atributo input de la Action. En caso de que esteatributo no sea definido se intentará hacer la redirección a un forwardllamado “failure”.

findAlert() Redirecciona a una página de alerta en la que se muestra un mensajeinformativo. Por defecto la redirección se hace a un forward llamado“warning”.

findSuccess() Redirecciona a la página de éxito, es decir, a aquella a la que se deberíair si la ejecución de la acción no tiene ningún error. Por defecto seredirecciona a un forward llamado “success”.

Creando un error en una PrincastAction

El openFWPA posee soporte integrado a la gestión de errores para usuario. Por error se entiende cualquiersituación anómala en la aplicación, sea por un fallo del sistema o por datos incorrectos suministrados porel usuario. Los errores que no son tratados por las aplicaciones se muestran al usuario final.

AtenciónEl método saveErrors(HttpServletRequest, List) está deprecado a partir de laversión 1.5 del openFWPA . Los errores ya no se deben almacenar directamente en la request, ensu lugar, se utiliza el almacenamiento interno de las Action . Si se utiliza el método deprecado,las Actions pueden no funcionar correctamente.

A continuación se muestra un ejemplo de creación de un error:

protected void preProcess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {

java.util.List error = new java.util.ArrayList();error.add(“error.general”);saveErrors(error);};

Para crear un error, se debe crear una instancia de java.util.List a la que se le añadirán hasta cincoelementos. El primero de estos elementos es la clave asociada al mensaje de error en el fichero de recursos.En caso de que el mensaje de error sea una simple cadena de caracteres (como ocurre en el ejemplo) bastarácon un solo parámetro. En caso de que el mensaje lleve parámetros de la forma {0},{1},{2},{3},un posible mensaje sería:

error.general=Ha ocurrido un error de tipo {0} a las {1} horas en {2} con usuario {3}

En este caso, la creación del error sería como sigue:

java.util.List error = new java.util.ArrayList();error.add(“error.general”);

Page 28: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

20

error.add(“GRAVE”); // Parámetro {0}error.add(“15:30”); // Parámetro {1}error.add(“Gestión de usuarios”); // Parámetro {2}error.add(“Administrador”) // Parámetro {3}

El usuario de la aplicación vería el siguiente mensaje:

Ha ocurrido un error de tipo GRAVE a las 15:30 horas en Gestión de usuarios con usuario Administrador

Creando un mensaje de advertencia en una PrincastAction

La forma de crear un mensaje de advertencia es similar al de la creación de un mensaje de error, con lasalvedad de que en lugar de llamar al método saveErrors(HttpServletRequest, List) se hade invocar el método saveMessages(List).

Modificando una redirección

En el curso de tratamiento de una petición, puede ser necesario redirigir la petición a otro servlet.PrincastAction proporciona una implementación por defecto para las redirecciones que pueden tenerlugar durante la ejecución de una petición a una acción. Los métodos que se encargan de estas redireccionesson:

findSuccess() Redirecciona a un forward etiquetado “success”.

findFailure() Redirecciona a lo que se indique en el atributo input del elemento <action>correspondiente o a un forward etiquetado “failure” en caso de que no sedefina el atributo input.

findAlert() Redirecciona a un forward etiquetado “warning”.

Todos ellos siguen la misma signatura:

ActionForward find<redireccion> (ActionMapping mapping, ActionFormform, HttpServletRequest request, HttpServletResponse response);

La forma de modificar la redirección de estos métodos es devolviendo una instancia diferente delActionForward. Por ejemplo, imaginemos que cuando la ejecución de la PrincastAction eséxitosa deseamos que se nos redireccione a un forward etiquetado como “ok”. En este caso, deberíamossobrescribir el método findSuccess() como se muestra a continuación:

ActionForward findSuccess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return mapping.findForward(“ok”);};

Como se puede apreciar en el ejemplo anterior, la cuestión es obtener del mapping (o crear indicando elpath) un ActionForward a donde deseamos redireccionar la repuesta.

ActionForward findSuccess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return new ActionForward("ok", "/action/test?method=ok", true);};

Page 29: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

21

Almacenamiento interno de una Action

Las Actions de Struts no son thread-safe. No es correcto utilizar atributos de instancia para compartirinformación entre los distintos métodos del ciclo de vida de una Action. En las ocasiones en que fueraindispensable utilizar un atributo de instancia, se recomienda utilizar el almacenamiento interno de laAction. Este almacén es un mapa de parámetros thread-local, cuyo ámbito se limita a los métodos delciclo de vida de la Action (preProcess(), executeActionLogic(), catchException()y postProcess()).

Figura 2.8. Almacenamiento de la Action

Para acceder y manipular este almacenamiento, la PrincastAction dispone de los siguientes métodos:

deleteActionParameter(nombre)Borra del almacén el parámetro especificado.

getActionParameter(nombre)Obtiene del almacén el parámetro cuyo nombre se especifica.

getActionParameters() Obtiene un iterador con los nombres de todos los parámetros delalmacén.

setActionParameter(nombre,valor)

Almacena un parámetro identificándolo con el nombre dado.

A continuación se muestra un ejemplo de uso de este almacén:

public class MyAction extends PrincastAction {

public MyAction(){ setActionParameter("oneParam", "oneValue"); } protected void preProcess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { setActionParameter("myParam", "foo"); }

protected void executeActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String param1 = (String) getActionParameter("myParam"); String param2 = (String) getActionParameter("oneParam"); … } }

En la Action del ejemplo: MyAction, se han sobrescrito dos métodos del ciclo de vida de la Action:preProcess() y executeActionLogic(). En el método preProcess(), se establece el valorde un parámetro: “myParam”, asignándole la cadena “foo”. Por otro lado, en el constructor, se estableceun valor para el parámetro “oneParam”. En el método executeActionLogic(), se recuperan losvalores de ambos parámetros. Recuérdese que únicamente los métodos del ciclo de vida de la ejecución dela Action tienen visibilidad del almacén. Por este motivo, en el método executeActionLogic(),la variable param1 tomará el valor “foo”, mientras que la variable param2 tendrá como valor null.

Page 30: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

22

Interrupción de la maquinaria de estados de la Action

En algunas ocasiones es necesario interrumpir el proceso de la maquinaria de estados de la Action sinredirigir a un estado de error. Para interrumpir la ejecución de la Action, basta con disparar una excepciónde tipo ActionProcessInterruption.

Un uso práctico de esta excepción es el siguiente:

Paginación sin reejecución de la lógica de negocio.

El problema es el siguiente: utilizando la librería Display Tag, cada vez que se produzca un movimiento depágina, se solicita una nueva ejecución de la Action que genera el listado, suponiendo esto la reejecuciónde la lógica de negocio completa (con acceso a datos incluido). La solución a este problema es la que sigue:

• Almacenar siempre las listas de bean a mostrar por el Display Tag en el scope session.

• En la etiqueta del Display Tag, en el atributo “requestUri”, añadir a la URL de la Action unparámetro GET (que la no entre en conflicto con alguno que ya utilice la Action).

<display:table name="sessionScope.ListaProductoKey" align="center" id="listProd" pagesize="3" export="false" sort="page" requestURI="../../action/viewlistaproducto?paginate=true">

• Extender el método preProcess(). En este método se detectará la existencia del parámetro definidoy, en tal caso, se disparará una ActionProcessInterruption.

protected void preProcess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { if (request.getParameter("paginate") != null) { throw new ActionProcessInterruption(); } }

Otra opción es utilizar una de las Actions que ya vienen con esta funcionalidad implementada:PrincastCRUDAction y PrincastListAction.

Diferentes tipos de PrincastAction

Además de la clase base PrincastAction, en el openFWPA se proporcionan otros tipos de Actions.Por un lado están PrincastDispatchAction y PrincastCRUDAction, una generalización de laanterior, y por otro una serie de implementaciones concretas de la PrincastAction tratada en el puntoanterior que facilitan el desarrollo de funcionalidades recurrentes en aplicaciones de gestión.

Implementaciones concretas

Existen varias clases que tienen una funcionalidad determinada y que pueden ser reutilizadas tal y comoestán. Estas son:

• PrincastExistAttributeAction

• PrincastRemoveAttributeAction

Page 31: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

23

• PrincastForwardAction

• PrincastParameterAction

PrincastExistAttributeAction

Esta clase se encarga de verificar la existencia de un atributo en alguno de los scopes o ámbitos (request,session o application) de la aplicación web. En la propiedad "parameter" del <action-mapping> se indicará el scope y el nombre del atributo a eliminar, separados por ";". Por ejemplo:

parameter="application;HOURS". Si se quiere buscar el atributo en cualquier scope se utilizaráun *. Por ejemplo:

parameter="*;HOURS". Si no se especifica alguno de los dos parámetros, se produce un error.

PrincastRemoveAttribute

Esta clase trata de eliminar un atributo dentro de uno de los ámbitos posibles (application, request,session). Si el atributo existe devuelve el control a un ActionForward etiquetado con “success”y, sino, a uno etiquetado con “failure”.

Tanto el ámbito como el atributo se pasan en la propiedad parameter de ActionMapping separados por";" (parameter="application;HORAS").

Para indicar que la búsqueda se realice en todos los ámbitos, el primer parámetro debe ser un asterisco("*") en lugar del nombre de un ámbito (parameter="*;HORAS"). El atributo sólo será eliminado delprimer contexto en el que sea localizado.

PrincastForwardAction

Acción que redirecciona a la URI relativa al contexto especificada por la propiedad parameter delActionMapping. Esta clase puede ser usada para integrar la aplicación con otra lógica de negocio deotros componentes implementados como Servlets o páginas JSP, pero manteniendo la funcionalidaddel Servlet controlador de Struts (como el procesado de form beans).

Para configurar una PrincastAction de este tipo en el fichero struts-config.xml es necesariocrear una etiqueta como ésta :

<action path="/guardaSuscripcion"type="es.princast.framework.web.action.PrincastForwardAction"name="suscripcionForm" scope="request" input="/suscripcion.jsp"parameter="/path/a/servlet"/> que redireccionará el control a la URI relativa al contexto /path/a/servlet .

PrincastParameterAction

Esta Action busca un parámetro en la request llamado dispatch y usa su valor para recuperarun forward local. Una vez conseguido este forward busca un segundo parámetro en la requestcuyo nombre debe ser especificado en la propiedad parameter del ActionMapping. Este valorse concatena con el valor de la propiedad path del forward obtenido con anterioridad. LaURI resultante es la que se usa para hacer la redirección. Un ejemplo de la declaración de unaPrincastParameterAction de este tipo es:

<action path="/menu/busca" type="es.princast.framework.web.action.PrincastParameterAction" name="menuForm" validate="false"

Page 32: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

24

parameter="keyValue"> <forward name="titulo" path="/do/busca/Titulo?titulo=" /> <forward name="autor" path="/do/busca/Autor?autor=" /> <forward name="contenido" path="/do/busca/Contenido?contenido=" /> </action>

Un fragmento de una página JSP que hiciera uso de esto podría ser:

<html:form action="menu/busca"> Busca artículos por : <html:select property="dispatch"> <html:option value="titulo">Titulo</html:option> <html:option value="autor">Autor</html:option> <html:option value="contenido">Contenido</html:option> </html:select> <html:text property="keyValue" /> <html:submit>Enviar</html:submit> </html:form>

Si el usuario elige Contenido y escribe Java en el campo de texto, el navegadorenviará: dispatch=contenido keyValue=Java Con esta información laPrincastParameterAction busca el forward contenido y concatena el valor de keyValueal path del forward, quedando algo del estilo: /do/busca/Contenido?contenido=JavaEn los forwards definidos dentro del mapping de la PrincastParameterAction es posibleincluir parámetros almacenados en la request utilizando la notación ${<nombre del parámetro>}.La PrincastParameterAction buscará, en el path (definido en el forward) la cadena"${<parámetro>}" y la sustituirá por “<parámetro>=<valor de parámetro>". Si, porejemplo, el valor del parámetro "Titulo" es "Rambo" y se define la siguiente forward: <forwardname=”titulo” path=”/do/busca?${Titulo}” /> La PrincastParameterActiondirigirá a la siguiente URL: /do/busca?Titulo=Rambo.

Actions Compuestas (Dispatch)

En muchas ocasiones interesa tener juntas aquellas acciones que se encargan de tareas relacionadas. Losmétodos que se encargan de la ejecución de tales tareas son encapsulados en una misma clase. Para estoscasos están pensadas las acciones que se presentan en este apartado: PrincastDispatchAction,PrincastCRUDAction.

PrincastDispatchAction

Esta clase es una especialización de la PrincastAction. Mantiene la misma estructura de máquinade estados que su clase padre pero se puede decir que cada una de las acciones que encapsula dispone desu propia máquina de estados.

Si por ejemplo queremos encapsular juntas las acciones “update” e “insert” tendríamos:updatePreProcess, updateExecuteActionLogic, etc. - y también insertPreProcess,insertExecuteActionLogic, etc. A pesar de que cada acción pueda tener su propia máquina deestados, puede interesar que las acciones compartan determinada funcionalidad. Para estos casos están losmétodos defaultPreProcess, defaultExecuteActionLogic, etc.

Page 33: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

25

¿Cómo identificar los métodos a ejecutar?

A la hora de seleccionar los métodos a ejecutar la PrincastDispatchAction hace uso del valorque se le pasa en el parámetro parameter del ActionMapping asociado. Si lo que queremos es ejecutarlos métodos de la máquina de estados asociada a la acción “update”, entonces este parámetro debe ser<action-mapping …… parameter="method" ….. />, donde el valor del parámetro method,será update. Si no se da implementación a alguno de los métodos update<estado_máquina>,por ejemplo updatePreProcess(), la PrincastDispatchAction ejecutará el métododefaultPreProcess(). De igual modo ocurre con el resto de métodos.

Es posible desacoplar el valor del parámetro del nombre del método. Se pueden establecer mapeos{valor_de_parameter, nombre_de_método} extendiendo el método getMethodKey() de la clasePrincastDispatchAction.

La PrincastDispatchAction permite, por defecto, una salida de éxito (success), otra de error(error) y para cada método de la Action. Por convenio, en la PrincastDispatchAction, lasalida de éxito de un método será un forward cuyo nombre será el mismo que la clave del método. Elforward de error equivaldrá al nombre del método concatenado con la cadena “-failure”. Para elforward de advertencia se concatenará la cadena “-warning” al nombre del método.

<action path="/customDispatchAction" name="aForm" parameter="method" type="customDispatchActionBean" validate="false" scope="session"> <forward name="method1" path="success.path" /> <forward name="method1-failure" path="failure.form" /> <forward name="method2" path="success.dos.path" /> <forward name="failure" path="failure.path" /> </action>

En el ejemplo superior, se está mapeando una Action de tipo PrincastDispatch con dosmétodos: method1 y method2. Cuando se ejecute con éxito el método method1, se redireccionaráal path: “success.path”. Si hay algún error, la redirección se realizará al path: “failure.form”.Por el contrario, cuando se ejecute el método method2, en caso de éxito la redirección se haráal path: “success.dos.path” y cuando se produzca un error, el path de redirección será:“failure.path” (ya que, aunque no ha sido definido un forward de error específico, se ha definido elforward de error por defecto: “failure”).

Figura 2.9. Esquema de la PrincastDispatchAction del ejemplo

En ocasiones, es necesario que una DispatchAction tenga mayor control sobre las redirecciones(forwards) que debe realizar para cada uno de los métodos de dispatch. Al igual que ocurre conotros métodos de la Action (executeActionLogic(), catchException(), etc.) es posibleredefinir los métodos de redirección (findSuccess(), findAlert() y findFailure()). Elsistema es exactamente el mismo: prefijar cada método con la clave (MethodKey). Por ejemplo:method1FindSuccess(), method2FindFailure(), etc.

PrincastLookupDispatchAction

La clase PrincastLookupDispatchAction pemite implementar un tipo especial de DispatchActions para formularios con mas de un botón (submit). Es este escenario, el botón que se utilice parael envío del formulario (submit) será quien determine el método que se ejecutará en la Action.

Page 34: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

26

Todas las actions lookup deben manejar formularios que extiendan la clase LookupDispatchForm, yaque será esta clase quien se encargue de gestionar las correspondencias entre los botnoes del formularioy los claves para seleccionar los métodos de la Action.

Para extender LookupDispatchForm se debe implementar el método getButtonKeys(),devolviendo un array que contendrá las posibles claves que se contemplan para seleccionar el método aejecutar. Por otro lado, el formulario maneja otro array (buttons), del mismo tamaño, con una posiciónreservada para cada botón.

Al enviarse el formulario, el array buttons, tendrá todos sus campos nulos, salvo el correspondienteal botón utilizado para el envío (submit). Para seleccionar el método a ejecutar, se utilizará la clavealmacenada, en el array de claves, en la misma posición que el botón activo.

En el ejemplo que se muestra a continuación, se presenta un formulario con tres botones "Aceptar","Volver" y "Cancelar". Cada uno de estos botones ejecuta un método distinto: "foo1" para "Aceptar","foo2" para "Volver" y "foo3" para "Cancelar".

public class FooLookupForm extends LookupDispatchForm {

public String[] getButtonKeys() { return new String[]{"foo1", "foo2", "foo3"}; } }

En el ActionForm, basta con ordenar los botones y asignarle una clave a cada uno: foo1, foo2 y foo3.

public class FooLookupAction extends PrincastLookupDispatchAction {

protected void foo1ExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

//Implementar logica de negocio }

protected void foo2ExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

//Implementar logica de negocio }

protected void foo3ExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

//Implementar logica de negocio }}

Las PrincastLookupDispatchAction, desde el punto de vista de su implementación sonexactamente iguales que las PrincastDispatchAction habituales.

<html:form action="test"> <html:submit property="buttons[0]" value="Aceptar"/>

Page 35: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

27

<html:submit property="buttons[1]" value="Volver"/> <html:submit property="buttons[2]" value="Cancelar"/> </html:form>

En la JSP cada botón submit se debe asignar, por orden, a una entrada del array "buttons".

PrincastCRUDAction

Esta Action está pensada para la gestión del ciclo de vida de entidades del modelo de la aplicación. Estados Action define los métodos del ciclo de vida de una entidad:

new Este método debe precargar los campos necesarios para mostrar el formulario de creaciónde una nueva entidad.

retrieve Este método permitirá recuperar una entidad.

list Este método debe obtener listados de entidades.

delete Permite borrar una entidad.

update Permite actualizar los datos de una entidad.

create Este método servirá para inserter nuevas entidades.

Atención

Para poder utilizar correctamente este tipo de Actions es necesario mapearlas dos veces enel fichero struts-config.xml. Uno de los mapeos tendrá la validación de formulariosdesactivada (validate = true) y se utilziará para solicitar los métodos que no requieren unformulario: new, retrieve, list y delete. El otro mapeo tendrá la validación activada yse utilizará para los métodos que sí requieren formulario: update y create.

<action path="/productosAction" type="productosActionBean" input="facturas.listaProductos" validate="false" scope="request" name="productoForm"> <forward name="list" path="facturas.listaProductos"/> <forward name="retrieve" path="facturas.listaProductos"/> <forward name="new" path="facturas.listaProductos"/> <forward name="delete" path="facturas.listaProductos"/></action>

<action path="/productosFormAction" type="productosActionBean" input="facturas.listaProductos" validate="true" scope="request" name="productoForm"> <forward name="create" path="facturas.listaProductos"/> <forward name="update" path="facturas.listaProductos"/></action>

Estas Action también soportan, además, paginación sin necesidad de reejecutar la lógica de negocio(Ver “Paginación sin reejecución de la lógica de negocio.”). Basta con incluir en la request el parámetro

Page 36: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

28

“paginate”. En el caso de esta Action, al contrario que la PrincastListAction, hay que registrarel objeto que se devuelve para listar, explícitamente en la session.

Validación de Formularios en acciones compuestas

La validación de formularios, en el framework Struts, redirecciona de forma automática, en caso de error,a una página de “input” definida en el mapeo de la action (en el fichero struts-config.xml).Este sistema tiene una limitación y esta es que las Actions compuestas (DispatchAction) solamentepueden definir una única página de “input” para todos sus métodos.

El openFWPA permite solucionar esta limitación del framework Struts. Para ello, basta con seguir lossiguientes pasos:

1. Utilizar el controlador PrincastTilesRequestProcessor (deprecado) oPrincastRequestProcessor. Para ello, es necesario incluir la siguientedefinición de controlador en el fichero struts-config.xml: <!--Para poner multiples input --> <controllerprocessorClass="es.princast.framework.web.action.PrincastRequestProcessor" />

2. En el mapeo de la action compuesta (DispatchAction) que tiene más de una entrada, dejar ladefinición de input vacía.

3. Para cada método de la Action, definir un forward utilizando el siguiente convenio de nombrado:“<nombre del metodo>Input”.

<action path="/productosFormAction" type="productosFormAction" parameter="method" validate="true" scope="request" name="productoForm"> <forward name="create" path="/action/productosAction?method=list"/> <forward name="update" path="/action/productosAction?method=list"/> <forward name="createInput" path="facturas.addProducto"/> <forward name="updateInput" path="facturas.detalleProducto"/>

Actions para Listados

Un subconjunto especial de Actions son aquellas que no tienen ninguna lógica de negocio especial.Su único objetivo es obtener un conjunto de objetos para ser mostrados. En función de si el listadose mostrará en una página HTML o en un PDF, se utilizará la PrincastListAction o laPrincastPDFReportAction.

Figura 2.10. Esquema de las Actions para listados

PrincastListAction

Si una Action tiene únicamente como propósito obtener un listado, se puede utilizar laPrincastListAction. No hace falta sobrescribir ningún método del ciclo de vida de esta Action,basta con implementar el método getContentList() y devolver el objeto (o colección de objetos)que serán mostrados. El objeto devuelto quedará registrado en sesión, bajo la clave que se especifique enel atributo parameter, en el mapeo de ese action, en el fichero struts-config.xml.

Page 37: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

29

En caso de que no se especifique ningún valor para el atributo parameter se disparará una excepciónde tipo PrincastActionProcessException

Esta Action permite realizar paginación sin necesidad de reejecutar la lógica de negocio (Ver “Paginaciónsin reejecución de la lógica de negocio.”).

PrincastPDFReportAction

Esta Action permite obtener un listado en formato PDF utilizando las utilidades para generación deinformes de openFWPA (Ver “Generación de Informes”). Para implementar una “Report Action”,basta con redefinir el método getReport(), devolviendo un objeto proveedor de contenido PDF(PDFProvider), por ejemplo, un objeto PrincastReport o PrincastMultiReport.

Habitualmente, los informes compilados (en formato .jasper) se almacenan juntos en una mismacarpeta. Para facilitar la carga de los ficheros “.jasper”, la clase PrincastPDFReportActionimplementa el método loadReport() que devuelve el InputStream correspondiente al fichero delinforme. Este método, supone que todos los informes se encuentran en la misma carpeta (por defecto: /WEB-INF/reports). Para buscar los informes en una carpeta distinta, se debe sobrescribir el métodogetRelativePathToReportFolder().

PrincastDispatchPDFReportAction

Este Action es la versión dispatch de la PrincastPDFReportAction, permite definir varios métodospara obtener el PDFProvider, por ejemplo, si el parámetro pasado al Action es myMethod se ejecutaríael método myMethodGetReport. Para más información, consultar el Javadoc de la clase y la “ActionsCompuestas (Dispatch)”.

PrincastXMLAction

Este Action permite servir contenido XML. Para servir una respuesta XML, basta con implementar elmétodo getXMLProvider, que retorna un proveedor de contenido XML.

El proveedor de contenido XML, será una clase que implemente el interfaz XMLProvider, el cual, obligaimplementar el método writeXML(Writer writer). Donde simplemente se escribirá el XML, aservir.

PrincastDispatchXMLAction

Este Action es la versión dispatch de la PrincastXMLAction, permite definir varios métodos paraobtener el XMLProvider, por ejemplo, si el parámetro pasado al Action es myMethod se ejecutaríael método myMethodGetXMLProvider. Para más información, consultar el Javadoc de la clase y la“Actions Compuestas (Dispatch)”.

Action Forms

El openFWPA dispone de una clase base para el desarrollo de los beans de formulario. Se trata de la clasePrincastActionForm. Entre las propiedades destacables de esta clase se encuentran:

mutable Para evitar que una PrincastActionForm sea rellenada de forma automática al hacerun forward entre diferentes acciones, establezca el valor de mutable a true y asegúresede que todos los setters comprueban el valor de dicha propiedad (if (isMutable())this.field = field;).

locale propiedad de la clase Locale. Si la instancia de la form es mutable, se le asigna la localede sesión de Struts siempre que se llame a reset(). Para actualizar el locale de la sesióndebe usarse putSessionLocale().

Page 38: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

30

En cuanto a los métodos:

setSessionLocale(Locale) Establece el atributo locale.

getSessionLocale() Devuelve el atributo locale.

setMutable(boolean) Establece el valor del atributo mutable.

isMutable() Devuelve el valor del atributo mutable.

reset(ActionMapping,HttpServletRequest)

Las subclases que deseen resetear el valor de sus atributos debencomprobar el valor de éste atributo (if (isMutable()) ...)

resetSessionLocale(HttpServletRequest)Cambia el atributo locale al valor que tenga el objeto localealmacenado en la sesión e la petición en curso bajo la claveGlobals.LOCALE_KEY.

putSessionLocale(HttpServletRequest)Cambia el atributo Globals.LOCALE_KEY de la sesión por elatributo locale o por el Locale por defecto si el atributo localees null.

getLocaleDisplay() Devuelve el Locale del usuario o el Locale por defecto.

setLocaleDisplay(String) Cambia el atributo locale a un código de lenguaje ISO dado. Recibecomo atributo un String con el código del país.

isBlank(String) Comprueba si el String que se le pasa es null o la cadena vacía.

describe() Devuelve un Map con las propiedades deesta PrincastActionForm. Se usa el métodoPropertyUtils.describe(). Sobrescriba el método siconsidera que alguna propiedad no debería ser mostrada de estemodo, o si un nombre de una propiedad debería ser cambiado. Estemétodo devuelve las propiedades públicas.

set(PrincastValueObject) Rellena las propiedades de la clase con las delPrincastValueObject que se le pasa como parámetro. Seproporciona una implementación vacía de este método para que seasobrescrito.

populate(PrincastValueObject)Permite cargar los datos del formulario sobre un Value Object. Estemétodo recibe como parámetro el Value Object sobre el que se vana cargar los datos. Devuelve una referencia al objeto que contienetodos los datos del formulario.

Para la definición de ActionForms dinámicos, se incluye en el openFWPA una clase base:PrincastDynaActionForm. Se incluye además una clase base para los formularios que van aser utilizados por dispatch actions: PrincastDispatchActionForm. Este tipo de formulariosincluyen un campo (method) para seleccionar el método de dispatch que se ejecutará paraprocesarlo. Las clases para la implementación de formularios se encuentran en el paquete:es.princast.framework.web.form.

La clase LookupDispatchForm permite disponer de formularios con más de un botón desubmit. Para obtener más información acerca de este tipo de forms, véase el apartadoPrincastLookupDispatchAction en la sección dedicada a las Actions.

Page 39: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

31

Desarrollo de lógica de negocioEs importante disponer de un buen diseño técnico antes de programar la lógica de negocio. En este áreaintervienen dos tipos de objetos: Business Delegates y Business Managers. Los objetos “Delegate” seencargarán de crear y gestionar los objetos de lógica de negocio y proporcionarán un interfaz, para laaplicación web, de los métodos de negocio. Los objetos "Manager", se encargarán de implementar la propialógica de negocio.

Figura 2.11. Estructura de la capa Modelo

Utilizando esta estructura, se puede modificar la implementación del servicio sin que sea necesariomodificar el resto de la aplicación. Un ejemplo de implementación puede verse en la aplicación de ejemplo(Sample App). En ningún caso la lógica de negocio ha de tener dependencias con el protocolo http (comopor ejemplo hacer uso de la sesión), ya que sus servicios han de poder reutilizarse desde cualquier otroentorno (como Web Services, JMS, etc.). Las únicas dependencias al protocolo concreto de acceso han deestar en las acciones (View Adapters y Actions).

Service LocatorEl patrón de diseño “Service Locator” permite encapsular, en una clase, la localización y acceso a objetos deservidor. El openFWPA incluye un componente que implementa este patrón: la clase ServiceLocator.El ServiceLocator proporciona los siguientes métodos para la búsqueda de objetos:

getDataSource() Permite instanciar un DataSource definido en el servidor (EnOC4J, en el fichero data-sources.xml)

getLocalHome() Obtiene un interfaz ejbHome (local) para la creación de un EJB.

getRemotelHome() Obtiene un interfaz ejbHome (remoto) para la creación de un EJB.

getQueue() Obtiene una cola de mensajes JMS.

getQueueConnectionFactory()Obtiene una factory de conexiones a colas de mensajes JMS.

getTopic() Obtiene un Topic JMS.

getTopicConnectionFactory()Obtiene una factory de conexiones a Topics JMS

Session EJBsHabitualmente, es necesario, cuando se trabaja con Session EJBs, gestionar, para cada uno de ellos,de forma específica el mantenimiento del contexto de la sesión (SessionContext). Para evitar laobligación de implementar los métodos de mantenimiento de la sesión, se ha incluido en el openFWPA,una clase base para los Session EJBs: PrincastSessionEJBTemplate. Esta clase implementa losmétodos setSessionContext() y unsetSessionContext(), dejando la instancia del contextoen la variable protegida “context”.

Value ObjectsLos objetos de datos (Patrón Value Object), en las aplicaciones desarrolladas sobre el openFWPA, debenimplementar el interfaz PrincastValueObject.

Page 40: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

32

Figura 2.12. Diagrama de Value Objects

Esta interfaz define el método toXML() que permite ver una descripción del objeto en formato XML.Para facilitar la implementación de Value Objects, se han incluído dos clases base: BasePrincastVO,que realiza una implementación por defecto para el método toXML() basada en reflectividad, yBasePrincastLazyLoadingVO, que debe ser utilizada si los Value Objects de la aplicación se vana usar en conjunción con Lazy loading de los Value Objects.

Ambas clases extienden la clase AbstractBasePrincastVO, que define otro método de utilidad:toPropertyBeans().

En la clase BasePrincastVO, este método permite "desmenuzar" un VO, mapeándolo a una listade objetos de tipo PropertyBean. El nombre de la propiedad se asignará al campo "value" delPropertyBean. El valor se asignará al campo "label".

Si alguna de las propiedades del VO es un objeto compuesto (una lista, una tabla, un array u otro VO)estos serán, a su vez, descompuestos. Se seguirá el siguiente convenio de nombrado para las propiedades:

Propiedades de tipo VO(PrincastValueObject)

Si una propiedad es de tipo PrincastValueObject, el nombrede cada una de sus propiedades se mapeará siguiendoel patrón: <nombre de la propiedad detipo PrincastValueObject>.<nombre de cadapropiedad del VO>

Propiedades de tipo List o arrays El nombre de este tipo de propiedades se compone como sigue:<nombre de la propiedad>[<posición de cada unade las propiedades de la lista>]

Propiedades de tipo Map El nombre de este tipo de propiedades se compone como sigue:<nombre de la propiedad>(<clave en el Map>).

También se ha empaquetado en el Framework un tipo de Value Object muy habitual: PropertyBean.Este objeto es un Value Object que almacena pares {valor-etiqueta}. La clase PropertyBean tambiéndispone de un método estático: pupulateList() que recibe como parámetro un Map y lo transformaen una lista de PropertyBeans.

Debe tenerse en cuenta que la clase BasePrincastLazyLoadingVO tiene implementaciones vacíaspara los métodos toPropertyBeans() y toXML(), por lo que deberán ser sobreescritos por los ValueObjects de la aplicación en caso de necesitar un comportamiento diferente.

ExcepcionesEl openFWPA dispone de su propia jerarquía de excepciones. La política general de manejo de excepcionesen el openFWPA es que se utilicen excepciones Runtime (no manejadas estáticamente).

Figura 2.13. Jerarquía de Excepciones

La clase base para la creación de excepciones es PrincastException. Existen dos ramas en estajerarquía de excepciones runtime: excepciones de sistema (PrincastSystemException), reservadaspara el openFWPA y sus componentes y excepciones de modelo (PrincastModelException), queson disparadas por las excepciones.

Page 41: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

33

Como norma general, las aplicaciones no beráin nunca extender las excepciones del sistema. Siempredeben extender PrincastModelException.

Además, el openFWPA también tiene una clase base para la creación de excepciones gestionadas:PrincastRequiredHandlingException.

La excepción DeprecatedAPIException se reserva para ser disparada desde métodos deprecadosen los que no sea posible implementar una lógica alternativa.

UtilidadesJunto con las excepciones, se incluye una clase (ToXMLExceptionHelper) auxiliar para facilitar elfromateo de las mismas y su traducción a XML.

Otras clases de utilidadJunto a las Actions, en el paquete web se incluyen algunas clases de utilidad para los componentes dela capa de aplicación.

PrincastMessageFmtter

Clase para facilitar el formateo de cadenas de caracteres (mensajes, etc). Esta clase permite:

• Reemplazar tokens en un String. Método replace().

• Formatear un mensaje, siendo éste una cadena con parámetros del tipo {0}, {1}, … {n}. Estemétodo (format()) recibirá como parámetros una cadena de texto y un array de objetos. El objeto enla posición 0 se introducirá en lugar de la subcadena “{0}” y así sucesivamente.

PrincastUtils

Contiene métodos de utilidad general. Actualmente únicamente implementa el métodonormalizePath() que tiene como objetivo normalizar los paths en los distintos sistemas operativos.

ParameterCaster

Clase de utilidad para la capa web. Permite traducir el tipo (casting) de los parámetros que se reciben deuna request http.

ServletPathUtils

Clase de utilidad del paquete web que permite gestionar paths de peticiones http. Los métodos que defineson:

match() Valida si un path se ajusta a un patrón URL (url-pattern)determinado.

extractRelativePath() Obtiene el path relativo a partir de un path absoluto.

getCompleteURL() A partir de una request, obtiene la URL solicitada completa,incluyendo los parámetros GET.

getURLParametersSeparator()A partir de una URL, determina si los parámetros que se vayana añadir a continuación se preceden de un carácter ‘?’ ó ‘&’,

Page 42: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

34

en funciónd e si dicha URL ya tenía, o no, parámetros GETanteriormente.

DateDecorator

Clase que facilita la escritura de fechas y horas con un formato determinado. Esta clase implementael patrón “Decorator” sobre la clase java.util.Date, sobrescribiendo su método toString().La clase es.princast.framework.core.util.DateDecorator permite definir el patrónde formato que se aplicará al obtener la representación textual de la fecha utilizando el métodoDate.toString().

Además, también define los patrones para los formatos de fecha más comunes:

/** * Formato corto para las fechas tomando como separador el caracter /. */ public static final String SHORT_DATE = "dd/MM/yyyy";

/** * Formato corto para las fechas tomando como separador el caracter -. */ public static final String SHORT_DATE_DASH = "dd-MM-yyyy";

/** * Formato para mostrar sólo horas, minutos y segundos. Las horas varían en * el rango 0..24. */ public static final String ONLY_TIME = "HH:mm:ss";

/** * Formato largo para la fecha, tomando como caracteres de separación el * caracter / para día, mes, año y el caracter : para horas, minutos y * segundos. */ public static final String LONG_DATE = "dd/MM/yyyy HH:mm:ss";

/** * Formato largo para la fecha, tomando como caracteres de separación el * caracter - para día, mes, año y el caracter : para horas, minutos y * segundos. */ public static final String LONG_DATE_DASH = "dd-MM-yyyy HH:mm:ss";

PrincastPathResolver

El objetivo del PrincastPathResolver es ofrecer, al programador de aplicaciones, un puntocentralizado para resolver paths (a recursos) uniformemente.

Este objeto (que implementa el patrón Singleton) define los siguientes métodos:

resolvePath(path) Resuelve un path, que se especifica por parámetro, devolviendo el pathabsoluto.

resolveToFile(path) Resuelve un path, devolviendo el objeto File correspondiente.

Page 43: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

35

resolveToStream(path) Resuelve un path, devolviendo un stream de lectura sobre el recursoque se halle en dicho path. Si no encuentra ninguno, dispara unaFileNotFoundException.

Existen varios tipos de "path resolvers" en el openFWPA, en función del tipo deaplicación. En general, se puede asignar cualquier tipo de "path resolver" definido porel usuario. Para ello, basta con extender la clase PrincastPathResolver y utilizar elmétodo PrincastPathresolver.registerResolver(). Los resolvers implementados en elopenFWPA son:

DefaultPathResolver Implementación por defecto. Resuelve paths absolutos y relativos alclasspath y al "working dir" de la aplicación.

WebAppPathResolver Implementación por defecto en aplicaciones web (siempre que utilicen elPrincastStartupListener). Resuelve paths absolutos y relativosal classpath y al directorio de despliegue de la aplicación.

PrincastOSCacheInterceptor

Esta clase permite a través de Spring y OSCache, realizar cacheos transparentes de las llamadas a cualquiermétodo de cualquier bean de Spring. Esto es útil, por ejemplo para cachear las llamadas al sistema deGenéricos del Gobierno del Principado de Asturias.

El uso de esta clase está documentado en la Javadoc. La funcionalidad por defecto establece una cachépor método cacheado, donde la clave para buscar en la cache es la concatenación del toString, de losargumentos. Si dos llamadas al mismo método tienen el mismo toString concatenado de los argumentosse devuelve el resultado cacheado. Este comportamiento se puede sobreescribir heredando de la clase.

El tiempo de refresco se establece en la definición de bean, por defecto son 600 segundos se recomiendaver la Javadoc, para ver la sintáxis de los tiempos de refresco en función del método.

ProvidersPara aislar la capa de acceso a datos de otras capas de la aplicación, habitualmente es buena idea definirinterfaces “providers”. Estos interfaces proporcionan datos a la capa del controlador, o directamente a lavista, sin indicar donde ni cómo se obtienen esos datos. El controlador (o la vista) pueden manipular los“providers” directamente sin preocuparse de cómo éstos se han obtenido.

Desde la versión 1.5 del openFWPA, los providers se encuentran en el paquete:es.princast.framework.facilities.providers.

El openFWPA define un conjunto de providers habituales:

EntityContentProvider Se trata de un proveedor de entidades. Este interfaz devuelve unasola entidad que puede ser utilizada directamente. Por ejemplo, paramostrar sus datos en un formulario.

ListContentProvider Provee conjuntos de entidades. Este interfaz devuelve listas deentidades. Se pueden utilizar para listados.

PaginatedContentProvider Provee listas paginadas de entidades. Este interfaz proporcionalistas que pueden recorrerse de forma paginada. Se pueden utilizaren listados en los cuales toda la lista no cabe en una sola páginaHTML

Page 44: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Arquitectura Modelo -Vista-Controlador con openFWPA

36

PDFProvider Provee documentos en formato PDF. Este interfaz proporciona unarray de bytes que contienen un documento PDF. Se puede utilizarpara la realización de informes o documentos.

XMLProvider Provee contenido en formato XML. Este interfaz proporcionaun método writeXML(Writer writer), donde se escribirádirectamente el XML. Se puede utilizar para servir contenido XML,junto con la PrincastXMLAction.

Un ejemplo de este tipo de Provider, incluido en el openFWPA, esel PrincastVelocityXMLProvider que provee contenido,a través del motor de plantillas Velocity. Su principal objetivo esla generación de contenido XML basado en plantillas, aunque sepuede usar para generar cualquier tipo de contenido. Para mayorinformación acerca de su uso, se recomienda leer la Javadoc

PropertyBeansProvider Es una implementación del ListContentProvider queprovee a la aplicación de beans de propiedades (PropertyBean).El provider puede cargar estos beans de objetos Map o de ficherosde properties (.properties).

Para conocer con mayor detalle el interfaz de cada uno de los providers, consúltese la documentaciónJavadoc del openFWPA.

Page 45: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

37

Capítulo 3. Implementación de laArquitectura de Referencia conopenFWPAInversión de Control en la Arquitectura deReferencia

A partir de la versión 1.5 de openFWPA, se hace un uso intensivo de la inversión de control (IoC), paraimplementar la arquitectura de referencia, en las aplicaciones realizadas con el openFWPA.

Para ello, se hace uso de Spring Framework, que ofrece la implementación del patrónAbstractFactory basado en ficheros XML. Esto permite, eliminar los elementos de unión en lasaplicaciones, como las factorías, y singletons. Además, permite tener la arquitectura modularizada en"piezas", que por estar definidas en un fichero XML, son intercambiables. Lo que deriva, en un sistemadébilmente acoplado, más tolerable a cambios y modificaciones.

Introducción al manejo de la Arquitectura conSpring

Para manejar la Arquitectura de referencia con Spring, se hace uso de una serie de ficheros XML, donde sedefinen los beans que forman parte de la arquitectura del sistema. Estos ficheros están ubicados en src/java/beans y sigue la estructura de directorios, propuesta para la arquitectura.

Figura 3.1. Ficheros de configuración de Beans

Los ficheros siguen la sintaxis de definición de beans de Spring, al igual que el fichero de inicializaciónde openFWPA (princast-init-script.xml).

Para hacer uso de la inversión de control, es necesario seguir una serie de pasos. Como ejemplo, se va a vercómo se construye una clase Action dependiente de una clase Delegate desde cero. El proceso de inyectarla dependencia se ha denominado "enlazado", tomándolo como traducción libre del término "wiring",utilizado en el manual de referencia de Spring.

La primera tarea que hay que hacer, es implementar el Action. Como se tiene una dependencia, con unManager, se introduce un campo o propiedad (privado o protegido), en la clase Action. Además, se defineun setter para ese campo, de esta forma se puede inyectar esa dependencia.

A la hora de usar el objeto Delegate se utiliza normalmente, aunque parezca que al usarlo apunta a un valornulo, el motor de inversión de control se encarga de inicializarlo.

public class GetListaProductoAction extends PrincastListAction { // inyeccion de dependencia (/beans/web/action-beans.xml) protected CarritoDelegate carritoDelegate;

Page 46: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

38

public void setCarritoDelegate(CarritoDelegate carritoDelegate) { this.carritoDelegate = carritoDelegate; } protected Object getContentList(ActionMapping mapping, ActionForm form, HttpServletRequest request) {

//Llamamos al delegate para obtener la lista de productos. return carritoDelegate.getListaProducto(); } }

Una vez programada la clase, se debe registrar en el fichero de beans correspondiente, en este caso, comose trata de un action, se registra en el fichero actions-beans.xml. Para ello se le da un identificadormediante el atributo id. Un nombre de clase con el atributo class, y mediante el atributo singleton,se especifica si se quiere que la clase sea un singleton o no (si no se especifica ese atributo, por defecto,será un singleton).

<bean id="viewlistaproducto" class="es.princast.sampleapp.web.action.GetListaProductoAction" singleton="false"> <property name="carritoDelegate"><ref bean="carritoDelegate" /></property> </bean>

Para inyectar la dependencia se realiza mediante el elemento property, donde se establece un atributoname, que coincide con el nombre asociado al setter, que se ha definido en la clase que se implementóanteriormente.

En el contenido del elemento property, se hace referencia mediante ref al identificador (id) de otrobean. Este bean puede estar definido, en ese mismo fichero o en cualquiera de la estructura comentadaanteriormente. En este caso, la definición es de un Delegate, por lo que estará definido en el ficherodelegate-beans.xml.

<bean id="carritoDelegate" class="es.princast.sampleapp.web.delegate.CarritoDelegate"> <property name="carritoManager"><ref bean="carritoManager"/></property> <property name="formasPagoManager"><ref bean="formasPagoManager"/></property> <property name="agenciasManager"><ref bean="agenciasManager"/></property> </bean>

Estableciendo el DatasourceLa inversión de control, comienza desde la primera dependencia que se tiene. En este caso el datasource,para ello se define en el fichero datasource-beans.xml, un bean que representa un datasource JNDI,a continuación se muestra un ejemplo de definición del mismo:

<!-- JNDI Datasource -->

Page 47: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

39

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>jdbc/MySQLDS</value> </property> </bean>

En el caso de la aplicación en blanco, para que una aplicación pueda desplegar sin necesidad de datasource,se ha definido un datasource nulo. Este datasource, sirve para poder enlazar las dependencias sin lanecesidad de un datasource real. En el momento que se disponga de uno real, es recomendable cambiarlopor el datasource JNDI.

<!-- Datasouce Nulo, BORRAR cuando se use un datasource real --> <bean id="dataSource" class="es.princast.framework.facilities.dao.PrincastNullDataSource" />

Para realizar las pruebas unitarias, es necesario disponer de un datasource para realizar los test. En laaplicación de ejemplo, se suministra un test sobre una clase DAO, que utiliza un datasource para test. Unejemplo de definición de datasource de test, es el siguiente:

<!-- DataSource para Test Unitarios --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property> <property name="url"><value>jdbc:mysql://localhost/carrito</value></property> <property name="username"><value>admin</value></property> <property name="password"><value></value></property> </bean>

Para más información sobre la realización de pruebas sobre los DAOs, ver “Pruebas unitarias de objetosque acceden a bases de datos.”

Enlazando con los DAOsUna vez definido el datasource, se definen los DAOs, para ello se utiliza el fichero dao-beans.xml.A continuación se muestra un ejemplo de definición:

<bean id="carritoDAO" class="es.princast.sampleapp.business.dao.MySQLCarritoDAO"> <property name="dataSource"><ref bean="dataSource"/></property> </bean> <bean id="formaPagoDAO" class="es.princast.sampleapp.business.dao.MySQLFormaPagoDAO"> <property name="dataSource"><ref bean="dataSource"/></property> </bean>

Page 48: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

40

Se recuerda que para que se produzca la inyección de la dependencia, la clase que implementa el DAO,debe disponer de un setter para el campo dependiente, como se observa en el siguiente ejemplo:

public class MySQLCarritoDAO implements CarritoDAO, PrincastDAO {

// La conexion se injecta en /beans/business/dao-beans.xml protected DataSource dataSource; ... // Este setter es necesario para la injeccion de la conexion en // la configuracion de los beans public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } ...

Enlazando con los ManagersPara enlazar los DAOs con los Managers, se realiza en el fichero manager-beans.xml. Hay que teneren cuenta, si el manager que se está definiendo, está sujeto a transacciones. A continuación, se muestrala declaración de un Manager que no está sujeto a transacciones y que no tiene ninguna dependencia conDAOs:

<!-- Este no esta sujeto a transacciones luego declaracion normal --> <bean id="agenciasManager" class="es.princast.sampleapp.business.manager.AgenciasManager"> </bean>

En el caso de estar sujeto a transacciones, la definición del bean es un poco más complicada, ya debeheredar de una plantilla para transacciones. Esto se realiza mediante el atributo parent, donde se leespecifica el nombre de la plantilla, que estará definida en el fichero transaction-beans.xml.

La definición propiamente dicha de la clase Manager, se realiza dentro de la propiedad target, y se leinyecta las dependencias normalmente. A continuación, se muestra la definición de un Manager sujeto amanejo de transacciones.

<bean id="carritoManager" parent="transactionTemplate"> <property name="target"> <bean class="es.princast.sampleapp.business.manager.CarritoManager"> <property name="carritoDAO"><ref bean="carritoDAO"/></property> </bean> </property> </bean>

Page 49: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

41

Como en los casos anteriores, la dependencia se establece por medio de la definición de un setter, paracada campo dependiente.

Gestión de transaccionesDesde el fichero transaction-bean.xml se controla la gestión de transacciones. Esta gestión serealiza de forma declarativa, por lo que los Managers que hereden de estas plantillas de transacciones, notendrán que implementar código para la gestión de transacciones, ni conexiones.

Antes de la definición de la plantilla se establece un transactionManager sobre el datasource autilizar. Un ejemplo de definición es el siguiente:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"><ref bean="dataSource"/></property> </bean>

Una vez declarado el transactionManager, se define una plantilla para el manejo de transacciones.De esta plantilla se debe de heredar, en la definición de beans, los Manager que esten sujetos a transacciones(mediante el atributo parent, visto en el apartado anterior). Se podrían definir varias plantillas, en funciónde las necesidades. Un ejemplo de plantilla para la gestión de transacciones es el siguiente:

<bean id="transactionTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="transactionAttributes"> <props> <!-- Los metodos que comiencen por get en los Manager seran readOnly --> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>

Como se observa en el ejemplo de plantilla, se dispone de una property de nombretransactionAttributes, que define las propiedades de la transacción. Las transacciones se definena nivel de método de un Manager, por lo que, cada ejecución de un método de un Manager estableceríauna transacción.

Además se pueden establecer patrones, de forma que definiendo el patrón 'get*', las propiedadesestablecidas para ese patrón, afectará a todos los métodos del Manager que comiencen por 'get'.

De esta manera definiendo las propiedades, 'PROPAGATION_REQUIRED,readOnly' se tendrá unatransacción por método, y además estará optimizada para sólo lectura.

Page 50: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

42

El comportamiento por defecto, para la política de rollback, define que si se produce una excepción detipo Runtime durante la ejecución del método, se hará un rollback.

Algunas de las propiedades que se pueden establecer son:

• PROPAGATION_LEVEL: Nivel de propagación. Por defecto se establece el nivel de propagación aPROPAGATION_REQUIRED, esto significa que creará una nueva transacción, sólo si se necesita.

• ISOLATION_LEVEL: Nivel de aislamiento. Por defecto trendrá el valor ISOLATION_DEFAULT.

• readOnly: Optimización de sólo lectura. Se optimiza la transacción para las operaciones de sólolectura.

• Lista de Excepciones: Establece la política de rollback o commit, al margen del comportamiento pordefecto. Por ejemplo, si se le añade la cadena '-MiExcepcion', se hara rollback en caso de quedispare la excepción de nombre 'MiExcepcion', aunque el tipo de excepción no sea Runtime. Si seañade la cadena '+OtraExcepcion', se hará commit aunque la excepción sea Runtime.

Para más información sobre las propiedades disponibles consultar el manual de referencia de Spring.

Enlazado con los DelegatesPara enlazar las definiciones de Managers, con las clases Delegate, se hace de manera similar a losenlazados visto anteriormente, en este caso se realiza en el fichero delagate-beans.xml Un ejemplode enlazado es el siguiente:

<bean id="carritoDelegate" class="es.princast.sampleapp.web.delegate.CarritoDelegate"> <property name="carritoManager"><ref bean="carritoManager"/></property> <property name="formasPagoManager"><ref bean="formasPagoManager"/></property> <property name="agenciasManager"><ref bean="agenciasManager"/></property> </bean>

Se recuerda que la clase CarritoDelegate debe disponer de un setter para cada dependencia.

Enlazado con las ActionsUna vez enlazados los Delegates, se deben enlazar las Actions. Para ello se definen los beans en el ficheroaction-beans.xml. Un ejemplo, de definición de beans para las Actions es el siguiente:

<bean id="viewlistaproducto" class="es.princast.sampleapp.web.action.GetListaProductoAction" singleton="false"> <property name="carritoDelegate"><ref bean="carritoDelegate" /></property> </bean>

En el caso de las Actions es importante establecer el atributo singleton="false", así se creará unanueva instancia del action, por cada petición de struts.

Page 51: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

43

Una vez definidos los beans para las Actions deben ser referenciados desde el fichero struts-config.xml, la referencia se hará mediante el atributo type. Hasta ahora, ese atributo referenciaba elnombre cualificado de una clase, a partir de la versión 1.5, se buscará primero por id de bean definidoen el fichero action-beans.xml, si no encuentra ningún bean definido, tomará el nombre como el deuna clase, tal y como se hacía en versiones anteriores.

Un ejemplo de un Action referenciando a una definición de bean en struts-config.xml, es elsiguiente:

<action path="/viewdetalleproducto" name="detalleProductoForm" input="/index.jsp" type="viewdetalleproducto" scope="request" validate="false"> <forward name="success" path="carrito.detalleprod" /></action>

Se observa que el atributo type tiene el valor type="viewdetalleproducto" que coincide conel id del bean definido en action-beans.xml.

Acceso directo al ApplicationContextOtra forma de acceder a los beans (menos recomendable que las anteriores) declarados en cualquierfichero de definición de beans, cargado por la aplicación, es utilizar el GlobalApplicationContext(GlobalApplicationContext.getInstance().getApplicationContext()). Estecontexto de aplicación global es una clase singleton que da acceso directo al ApplicationContextde Spring cargado por la aplicación.

Atención

En aplicaciones Struts, únicamente se puede acceder, por este método, a los beans cargados porel módulo por defecto (módulo sin prefijo).

BeanDoc para obtener la gráfica dearquitectura

Como la arquitectura esta reflejada en ficheros XML, se dispone de una herramienta para generar unagráfica de arquitectura, Spring BeanDoc, que es un subproyecto de Spring Framework.

Se dispone de una tarea Ant en la aplicación de ejemplo para ejecutar esta herramienta, aunque deberá estarpreviamente instalada. Para instalarla, se debe ir a la página principal del proyecto Spring Framework, yseleccionar el subproyecto BeanDoc, donde se indica cómo instalarla y configurarla.

Figura 3.2. Ejemplo de gráfica generada con BeanDoc

Plugin SpringIDE para EclipsePara facilitar el desarrollo, y el tratamiento de ficheros de Spring, existe el plugin para Eclipse, SpringIDE[http://springide.org/project].

Page 52: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Implementación de la Arquitecturade Referencia con openFWPA

44

Este plugin, permite ver de una forma visual la definición de beans, además incluye caraterísticas como elautocompletado de clases y el acceso directo desde el gráfico de visualización, a la definición del bean ode la clase. Para su instalación y primeros pasos, se debe acudir a la página del proyecto SpringIDE.

Figura 3.3. Spring IDE para visualizar ficheros de Spring

Page 53: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

45

Capítulo 4. Componentes para accesoa datosAcceso a Bases de Datos Relacionales

Un componente básico de la capa de modelo Modelo es el acceso a datos (generalmente, a bases de datosrelacionales). Tal y como se especifica en la Guía de Aceptación de Aplicaciones, no estápermitido el acceso a datos desde cualquier componente de la aplicación que no pertenezca a esta capa(páginas JSP, Actions, etc.). La herramienta clave para la implementación de esta capa es el patrón dediseño DAO (Data Access Object).

El patrón DAOPara realizar el acceso a datos se utilizará el patrón DAO (Data Access Object). El openFWPA dispone deuna interface base PrincastDAO que deben de implementar todos los DAOs, o extender de una clase quela implemente. Esta interfaz posee dos métodos, getDataSource y setDataSource que permitenobtener y establecer, el DataSource que utliza el DAO, para acceder a Base de Datos.

El framework también dispone de una clase de utilidad para facilitar la implementación de objetos DAO: laclase PrincastDAOHelper. Un objeto PrincastDAOHelper siempre debe estar asociado con unobjeto DAO (que se llamará OwnerDAO). Solamente los OwnerDAOs de un HelperDAO podrán acceder asus métodos. Es altamente recomendable, que para mejorar el rendimiento general de la aplicación, todoslos DAO de una misma clase puedan acceder al mismo DAOHelper a través de una variable static.

La característica más interesante que proporcionan los DAOHelpers es la capacidad de cargar consultasSQL desde un fichero .properties. Este fichero debe cumplir las normas de ficheros properties Java.

Cada propiedad del fichero se corresponderá con una consulta SQL identificada por una etiqueta.

Las consultas SQL del fichero de properties pueden tener parámetros, utilizándose para ello el carácter“?” de forma análoga a los PreparedStatement.

Los métodos públicos de esta clase DAOHelper son:

PrincastDAOHelper(Class) Constructor del DAOHelper que recibe como parámetro elOwnerDAO

getQueries() Devuelve un Properties con las consultas manejadas por elDAOHelper

getQuery(String) Devuelve un String con la consulta cuya clave en el fichero deproperties se le pasa como parámetro.

getStament(String,Object [])

Devuelve un String con la consulta construida, cuya clave en elfichero de properties se le pasa como parámetro, haciendo uso delos parámetros estructurales, que también se pasan como parámetro.

executeQuery(String,Object[], Object[],DataSource)

Devuelve una List de Map con el resultado de ejecutar laconsulta cuya clave en el fichero de properties se le pasacomo parámetro. Utiliza como parámetros los objetos que sele pasan en dos arrays: a) El primero de los arrays contiene

Page 54: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Componentes para acceso a datos

46

parámetros estructurales. Se utilizan para componer una consultay se identifican por números entre “{“ y ”}” (por ejemplo:“SELECT * FROM {0};” ). Este tipo de parámetros puedenconsiderarse como “comodines” para todas aquellas situacionesen las que no se puedan utilizar los parámetros estándar de laclase java.sql.PreparedStatement. b) El segundo arrayde parámetros contendrá los valores de los parámetros estándar(identificados por caracteres ?).

executeQueryForList(String,Object[], Object[],DataSource)

Es equivalente a executeQuery.

executeQueryForList(String,Object[], Object[],PrincastRowMapper,DataSource)

Es equivalente a executeQueryForList. Salvo por elparámetro PrincastRowMapper, que permite mapear unResultSet, a una clase de negocio (VO), esto permite que elmétodo devuelva una lista de VOs.

executeUpdate(String,Object[], Object[],DataSource)

Ejecuta la consulta de actualización cuya clave en el fichero deproperties se le pasa como parámetro.

reload() Refresca la tabla de consultas releyéndolas del fichero propertiescorrespondiente.

Desde la versión 1.5 del framework, se ha introducido la posibilidad de mapear los ResultSet a clases denegocio, generalmente VOs. Para ello se necesita crear una clase que herede de PrincastRowMapper,e implemente el método mapRow. Desde una clase DAOHelper se podrá, usar y reutilizar estosmapas, mediante llamadas al método executeQueryForList que acepta como parámetro unPrincastRowMapper. Un ejemplo de mapeo se puede observar en la clase ProductosMapper, enla aplicación Sample App.

public class ProductosMapper extends PrincastRowMapper{

public Object mapRow(ResultSet rs, int rowNum) throws SQLException { ProductoVO producto = new ProductoVO(); producto.setId(rs.getInt("id")); producto.setName(rs.getString("nombre")); producto.setDescription(rs.getString("descripcion")); producto.setSmallImageURL(rs.getString("smallImageURL")); producto.setBasePrice(rs.getDouble("basePrice")); return producto; }

}

La aplicación en blanco (App Blank) dispone de plantillas para la realización de DAOs (concretamente enla clase DAOTemplate), y la aplicación de ejemplo ( Sample App) dispone de un ejemplo de utilizaciónen la clase MySQLCarritoDAO.

public class MySQLCarritoDAO implements CarritoDAO, PrincastDAO {...protected static PrincastDAOHelper helper = new PrincastDAOHelper( MySQLCarritoDAO.class);...

Page 55: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Componentes para acceso a datos

47

/** * Devuelve una List con todos los productos que maneja la aplicación. */ public List getListaProducto() { List listaProducto;

listaProducto = helper.executeQueryForList("listaProducto", null, new Object[] {}, new ProductosMapper(),dataSource);

return listaProducto; }...}

Loggeo de las Excepciones en los DAOComo se ha visto en capítulos anteriores, a partir de la versión 1.5 no hace falta capturar las excepcionesproducidas en el acceso a datos, sin embargo si que es recomendable imprimir en el log de la aplicación, laexcepción que se produce. Para ello, basta con definir dos spring beans en el fichero dao-beans.xml,y las excepciones que se produzca quedan loggeadas automáticamente.

<!-- Con estas dos definiciones se auditan todas las expcepciones que se produzcan en los DAOs a nivel de log de ERROR--> <bean id="loggerThrowsAdvice" class="es.princast.framework.facilities.interceptor.logger.LoggerThrowsAdvice"> <property name="level"><value>ERROR</value></property> </bean>

<bean id="daoBeanAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"><value>*DAO</value></property> <property name="interceptorNames"> <list> <value>loggerThrowsAdvice</value> </list> </property> </bean>

La primera definición de bean loggerThrowsAdvice, indica la clase de log que se va a usar. Lapropiedad más importante es el nivel de log, en el ejemplo se indica que las excepciones se van a loggeara un nivel de ERROR (son válidos los mismos niveles de log que para log4j).

La segunda definición indica los beans a los que se va a aplicar el log, se aplican por nombre de bean.En este caso se indica con un patrón "*DAO", esto quiere decir que el log se aplica a todos los métodosde todos los beans que su identificador acabe en la cadena DAO. Aunque aquí se auditen los DAO, sepuede auditar cualquier otra clase por su definición de bean de spring, por ejemplo todos los "Delegate"con el patrón "*Delegate".

Listas clave/valorExiste un tipo especial de DAO que permite acceder y manipular a información estructurada como listasde pares clave/valor sobre una tabla de una base de datos. Se trata de la clase PropertiesTableDAO.

El principal uso de este tipo de objetos DAO es facilitar el acceso a listas de valores que puedan serutilizadas para cargar componentes de interfaz de usuario como inputs HTML de tipo select.

Page 56: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Componentes para acceso a datos

48

Esta clase devuelve objetos (o listas de objetos) del tipo PropertyBean, que tiene dos campos valuey label correspondientes a la clave y su valor asociado respectivamente.

Los métodos públicos de esta clase son:

PropertiesTableDAO(DataSource,String, String, String)

Constructor. Recibe un DataSource, el nombre de la tabla,nombre de la columna donde se almacenan las claves y nombre dela columna que tiene los valores.

getAllProperties() Devuelve una List de PropertyBeans con todos los pares dela tabla.

getProperties(String) Devuelve una List de PropertyBeans con los pares de latabla que se cumplen una determinada condición especificada porparámetro.

findProperty(String) Devuelve el PropertyBean cuya clave se pasa como parámetro.

insertProperty(String,String)

Inserta en la tabla de la base de datos un nuevo registro con la clavey el valor que se pasan como parámetro.

updateProperty(String,String)

Actualiza en la base de datos el valor de la clave especificada.

deleteAllProperties() Elimina todos los pares clave/valor de la tabla.

deleteProperty(String) Elimina el par cuya clave se pasa como parámetro.

getReportIterator(Object[])Devuelve un Iterator con todos los pares de la tabla. Si el array quese le pasa esta vacío o es nulo devolverá todos los valores de latabla. Sin embargo, si contiene valores se buscarán todos los parescuya clave esté en el array

Puede verse un ejemplo de utilización en la clase MySQLFormaPagoDAO, de la aplicación de ejemplo(Sample App).

public class MySQLFormaPagoDAO implements FormaPagoDAO, PrincastDAO {... protected static List formasPago = null;

public MySQLFormaPagoDAO() {}

/** * Devuelve una List con las formas de pago existentes */ public List getFormaPago() { // se mantiene en memoria las formas de pago en una variable static if (formasPago==null){ PropertiesTableDAO propsTable = new PropertiesTableDAO(dataSource, "formapago", "id","descripcion"); formasPago = propsTable.getAllProperties(); } return formasPago; }...}

Page 57: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Componentes para acceso a datos

49

Otra forma de crear listas de beans con pares atributo/valor (PropertyBean) es a partir deun Map. La propia clase PropertyBean dispone de un factory method estático que, recibiendo comoparámetro una referencia a un objeto Map, crea una lista de pares atributo / valor (PropertyBeans).

public static List populateList(Map props)

LookupPropertyBean

Una situación habitual es aquella en la que se necesita “navegar” una relación entre entidades. En estasituación, se dispone de una clave y se necesita obtener el valor de dicha clave.

Figura 4.1. Ejemplo de necesidad de lookup

Si en el modelo de la figura, se necesita hacer un listado con: {ARTICULO, DESCRIPCION} , esnecesario realizar una acceso a base de datos para cada entrada de la lista, o bien modificar el diseñode la aplicación, cambiando los campos de la clase ArticuloVO. Una forma más sencilla es utilizarel componente LookupPropertyBean, que proporciona el openFWPA. Este bean permite, recibiendo unaclave, obtener el valor asociado a dicha clave.

Figura 4.2. Navegación de una relación en BD con un LookupPropertyBean

En ocasiones, los datos sobre los que hay que hacer " lookup ", no están almacenados en una base dedatos. Estos datos pueden estar en memoria, en un Map o en una lista de PropertyBeans . Para podermanejar todas estas situaciones, el componente LookupPropertyBean , dispone de los siguientesconstructores:

LookupPropertyBean(DataSourcedataSource, StringtableName, StringkeyColumn, StringvalueColumn)

Constructor estándar para este tipo de objetos. Asocia el lookupbean con una base de datos. Como parámetros debe recibir: eldataSource de la base de datos, la tabla en la que se almacenan losdatos de la relación, el nombre de la columna de las claves y elnombre de la columna de los valores.

LookupPropertyBean(Mapprops)

Utilizando este constructor, la relación lookup se realiza sobre losdatos almacenados en un Map.

LookupPropertyBean(Listbeans)

Este constructor debe recibir como parámetro una lista de objetos detipo PropertyBean. Las claves de la asociación se correspondencon la propiedad " value" de los beans y los valores, con alpropiedad " label". Cualquier objeto en la lista que no pertenezca ala clase PropertyBean será ignorado.

ProvidersA partir de la versión 1.5 del openFWPA, los providers están ubicados en elpaquete: es.princast.framework.facilities.providers. Los providers del paquetees.princast.framework.dao.providersestán deprecados. Ver “Providers”.

Generadores de SecuenciaOtra utilidad incluida en el openFWPA son los generadores de secuencia. Este tipo de objetos permitengenerar secuencias de números consecutivos. Su utilidad más habitual es la generación de claves primarias.

Page 58: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Componentes para acceso a datos

50

La clase base para generadores de secuencia, en el openFWPA, es SequenceGenerator, cuyo métodomás relevante es: generateId().

Se proporcionan dos implementaciones: MySQLSequenceGenerator yOracleSequenceGenerator que utilizan bases de datos MySQL y Oracle respectivamente.

Pools de conexionesLa configuración de Pools de conexiones en el contenedor oc4jse realiza en el fichero data-sources.xml. El propio fichero trae un ejemplo de configuración para base de datos Oracle, y en laaplicación de ejemplo (Sample App) se puede encontrar la definición de un DataSource para el servidorMySQL.[17]

<data-source class="com.evermind.sql.DriverManagerDataSource" name="MySQLDS" location="jdbc/CarritoDS" xa-location="jdbc/xa/CarritoXADS" ejb-location="jdbc/MySQLDS" connection-driver="org.gjt.mm.mysql.Driver" username="admin" password="" url="jdbc:mysql://localhost/carrito" inactivity-timeout="30"/></data-sources>

Para opciones avanzadas de configuración (número mínimo y máximo de conexiones del Pool, númeromáximo de intentos de conexión, etc.) consultar el DTD de dicho fichero, disponible en la direcciónhttp://xmlns.oracle.com/ias/dtds/data-sources.dtd.

Page 59: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

51

Capítulo 5. Construccion de informescon openFWPAGeneración de Informes

La generación de informes se basa en el proyecto JasperReports [20]. JasperReports es una libreríade clases que puede verse como un motor de reporting para desarrollos java. El principal objetivo deeste proyecto es facilitar la construcción de documentos con contenido dinámico y su visualización endiferentes formatos.

JasperReports organiza la información que le servirá para generar los documentos en forma de ficheroXML. El fichero XML, de acuerdo al DTD http://jasperreports.sourceforge.net/dtds/jasperreport.dtd, es visto como el diseño del informe. Una vez en disposición de un diseñoXML válido, es necesario realizar un proceso de compilación, que generará un objeto que es serializado,y podría ser almacenado en disco con extensión .jasper. La compilación validará todas las expresionesjava que pudieran estar embebidas en el XML. Una vez compilado, y para proveer al informe con datosdinámicos, se realiza el proceso de completado (fill). Este proceso puede recibir diferentes fuentes dedatos, entre ellas conexiones a bases de datos relacionales (instancias de clase Connection), coleccioneso arrays de Beans (instancias de clase JRDataSource de la librería de JasperReports) o fuentes de datospersonalizadas, extendiendo el interface JRDataSource. Otra forma de pasar información dinámica alos documentos es a través de parámetros de informe, que forman parte del diseño XML y pueden serestablecidos por programación, e incluso tener valores por defecto en diseño.

Este proceso puede verse en la siguiente figura (obtenida del libro “[The JasperReports Ultimate Guide]”)

Figura 5.1. Proceso de generación de informes

Creación de los diseños XML.El proyecto JasperReports no provee de ninguna herramienta adicional que facilite la labor de la creaciónde los ficheros XML. En este punto, un desarrollador que pretenda crear el diseño XML de un documentonecesario para su aplicación, debería crearse el fichero XML desde un editor de texto, o editor XML.Evidentemente, esto es una tarea tediosa y poco operativa. Más bien se necesitaría otra herramienta quefacilitara la labor de visualizar, compilar y generar el XML del documento que se está diseñando. Unaherramienta visual del tipo WYSIWYG.

Existen varias herramientas de este tipo desarrolladas como proyectos abiertos. Algunas de ellas puedenverse en http://jasperreports.sourceforge.net/gui.tools.html. El desarrollador eslibre de elegir su herramienta preferida; el openFWPA no provee de ninguna de ellas, aunque se hanrealizado pruebas con el proyecto iReport-Designer for JasperReports [21]

La herramienta iReports

La herramienta iReports es un editor visual de ficheros XML listos para ser usados por el motor dereporting JasperReports. Trabaja en modo standalone (aplicación de ventana de java) y tiene un interfazclaro. En la página del proyecto http://ireport.sourceforge.net puede obtenerse una ampliadocumentación sobre como manejar esta herramienta, e incluso un video tutorial que muestra algunos delos aspectos más interesantes sobre ella.

Page 60: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

52

Al estar estrechamente relacionada con JasperReports, es necesario conocer los aspectos en queeste último organiza su información (bandas, grupos, subreports, parameters, fields, variables…)para optimizar el manejo de la herramienta visual. Una vez descargado el proyecto y ejecutado, seabrirá una ventana como la de la siguiente figura, donde se ha abierto un fichero XML llamadosolicitudQuemaEnBlanco.xml.

No es objeto de este manual mostrar el manejo detallado de iReports. No obstante, se introducen algunosconceptos sobre la herramienta. Una vez se tiene un diseño cargado, o bien se ha optado por un diseñonuevo a partir de cero al cual se le van añadiendo elementos, se puede compilar el diseño, tal y como laharía JasperReports a través de su motor. Con esto se asegura que el diseño que se está generando es válidoy no producirá errores de compilación en el momento de que la aplicación a desarrollar tome el ficheroXML para compilarlo y mostrarlo en pantalla. Para esto se dispone del icono Compliar (1) a la derechaen la barra del menú. También es posible observar como se vería el documento que se está diseñandocon alguno de los visores predeterminados, previa configuración de los programas externos que maneja laherramienta, de dos modos distintos: con una fuente de datos vacía o con la fuente de datos actual (previaconfiguración de las fuentes de datos).

Establecer los programas externos.

Menú # Tools # Options # Pestaña External Programs Desde esta ventana se pueden establecer losejecutables para los programas externos que puede usar la herramienta para visualizar los informes.

Crear fuentes de datos.

Menú # Datasource # Connections / Datasources Desde esta ventana se pueden gestionar las fuentes dedatos (crear, borrar y modificar)

Admite cuatro tipos diferentes de fuentes de datos:

• DataBase JDBC Connection

• XML file DataSource

• JavaBeans set DataSource (Collection o Array de Beans)

• Custom JRDataSource

Las más interesentas son las conexiones JDBC y las colecciones de Beans. El framework de programaciónde JasperReports provee de una serie de implementaciones de referencia para conjuntos de Beans.Son clases que extienden el interface base JRDataSource. También existe la prosibilidad de que eldesarrollador extienda su propia implementación del interface, con lo que conseguiría una fuente de datosCustomJRDataSource, la cual podría probar utilizando la herramienta iReports (creando una fuentede datos de este tipo y configurando la clase asociada)

Establecer la fuente de datos actual

Menú # Build # Set active connection Desde esta opción se establecerá la fuente de datos que la herramientautilizará cuando se visualice el diseño usando una fuente de datos (icono en la barra de navegación uopción de menú Menú # Build # Execute report (using active conn.)

Page 61: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

53

Establecer la vista del informe

Menú # Build Desde este grupo de opciones se tiene la posibilidad de establecer como obtener la salidavisual del documento que se está diseñando, seleccionando alguno de los 7 radio-buttons disponibles.Se deberían tener correctamente establecidos los programas externos para poder utilizar correctamentelas vistas. La opción JRViewer preview funciona en cualquier caso, al ser el visor por defecto deJasperReports, que ya incluye la herramienta iReports.

Report Wizard

Menú # Report Wizard Esta opción permite la creación de nuevos diseños a partir del wizard de iReports.El proceso consta de una serie de pasos que se detallan a continuación:

1. Introducir la consulta que obtendrá los datos de la fuente de datos.

2. : Seleccionar los campos que se desean visualizar en el documento a generar.

3. : Agrupar por algún campo si se desea. No es obligatorio agrupar.

4. Elegir una plantilla para el layout. iReports viene con una serie de plantillas que se pueden utilizar, obien el desarrollador podría construir sus propias plantillas.

Con esto finaliza el proceso, y se genera un informe en vista diseño como el siguiente:

Que puede ser compilado y visto en PDF, pulsando el icono correspondiente:

En este ejemplo se ha utilizado una fuente de datos JDBC Connection. No se han realizado pruebaspara obtener datos de collecciones de Beans (fuente de datos JRDataSource) aunque el proceso nodebería diferir sustancialmente de lo aquí expuesto.

Report Parameters

Menú # View # Report Parameters Los parámetros son una buena forma de añadir datos dinámicos alinforme y parametrizar éste, de forma que se puedan visualizar elementos según el valor o rango de algúnparámetro, e incluso modificar la consulta SQL según parámetros. Los parámetros añaden más flexibilidada la generación de los informes, hasta el punto de que es posible crear informes sin fuente de datos, y quetoda su información llegue por parámetros.

Un parámetro tiene un nombre, un tipo (clase java seleccionable de una lista) y opcionalmente un valorpor defecto (expresión java que será compilada en el proceso de compilación y evaluada en el procesode completado del informe) Los parámetros son referencias a objetos que son pasadas al proceso decompletado (fill) del informe. Su uso, como ya se ha comentado, servirá para enviar información almotor de reporting que no puede ser encontrada en la fuente de datos (DataSource)

Los parámetros se identifican en el fichero XML por construcciones similares a la siguiente:

<parameter name="ejemplar_para" isForPrompting="false" class="java.lang.String">

Page 62: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

54

<defaultValueExpression > <![CDATA[new String("Ejemplar para ... por defecto")]]> </defaultValueExpression> </parameter>

En el entorno de iReports, se puede hacer referencia a este parámetro mediante la construcción$P{ejemplar_para}

Consultas parametrizadas

Menú # View # Report quey Es posible parametrizar la consulta que lanzará el informe según el valor deuno o varios parámetros en tiempo de ejecu-ción. Para hacer referencia a un parámetro definido se utilizala construcción $P{nombre_de_parametro} y estas construcciones se pueden insertar en las propiaconsulta, de forma que sean evaluadas durante el proceso de completado. Un ejemplo puede verse en lasiguiente imagen, donde se observa la intrusión de los parámetros “fecha_inicio” y “fecha_fin”como parte de la sentencia SQL. El objetivo de esta consulta es filtrar la búsqueda entre dos fechas, queposi-blemente el usuario introduzca en algún formulario de la vista de su aplicación. Una forma de hacerllegar dinámicamente estos valores a la consulta es utilizando parámetros de informe, cuyos valores seránasignados por programación en tiempo de ejecución y pasados como información adicional al proceso decompletado del informe.

Fields

Los fields representan la forma de mapear campos en el diseño del report con datos de la fuente de datos.Si el DataSource es una base de datos, entonces el mapeo es directo con los nombres de campos enuna o varias tablas. Si el DataSource es una collección de Beans, el mapeo se realizará con las property´s de éstos.

Un Field se puede idetificar en el XML por una construcción como: <field name="DL_FINALIDAD"class="java.lang.String"/> En el entorno de iReports se le puede hace referencia como$F{DL_FINALIDAD}

Variables

Se declaran como expresiones en java, y más tarde pueden ser usadas de forma masiva. Pueden servircomo forma de no repetir código. Tambíen se utilizan para realizar cálculos. Exiten funciones built-in que facilitian algunas tareas de cálculo, como Count, Sum, Average, Lowest, Highest yStandardDeviation. Las variables serán reinicializadas según la jerarquía de niveles: Report,Page, Column y Group.

Exiten una serie de variables inherentes a todo informe (built-in variables), a las que se puede hacerreferencia en todo momento.

• PAGE_NUMBER

• REPORT_COUNT

• COLUMN_COUNT

En el entorno de iReports se les puede hace referencia como $V{NOMBRE_DE_VARIABLE}

Page 63: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

55

El diseño XML

Ya sea con la herramienta iReports o con cualquier otra disponible, el objetivo será obtener un ficheroXML que se considerará el diseño del documento. Este fichero será pasado al proceso de compilación,completado y visualización de las aplicaciones construidas bajo el framework, en la forma que se verámás adelante.

El código XML generado es extenso y pesado de tratar con herramientas de edición de texto o editoresXML. Es por esto la necesidad del uso de herramientas como iReports para diseñar los informes de lasaplicaciones. A continuación se pueden ver trozos de XML que generan las herramientas:

<?xml version="1.0" encoding="UTF-8" ?><!-- Created with iReport - A designer for JasperReports --><!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd"><jasperReport name="listadoTipoQuema" columnCount="1" printOrder="Vertical" orientation="Portrait" pageWidth="595" pageHeight="842" columnWidth="535" columnSpacing="0" leftMargin="30" rightMargin="30" topMargin="20" bottomMargin="20" whenNoDataType="AllSectionsNoDetail" isTitleNewPage="false" isSummaryNewPage="false"> <property name="ireport.scriptlethandling" value="2" /> <parameter name="P_Titulo" isForPrompting="false" class="java.lang.String"> <defaultValueExpression ><![CDATA["ESPACIO PARA EL TITULO DEL INFORME"]]></defaultValueExpression> </parameter> <parameter name="P_AmpliacionTitulo" isForPrompting="false" class="java.lang.String"> <defaultValueExpression ><![CDATA["Descripción extendida del propósito del informe"]]></defaultValueExpression> </parameter> <parameter name="fecha_inicio" isForPrompting="false" class="java.lang.String"> <defaultValueExpression ><![CDATA["01/01/1900"]]></defaultValueExpression> </parameter> <parameter name="fecha_fin" isForPrompting="false" class="java.lang.String"> <defaultValueExpression ><![CDATA["31/12/2999"]]></defaultValueExpression> </parameter> <queryString><![CDATA[select f.dl_finalidad, c.dl_concejo, r.cn_tiporesol, s.ca_permisofrom quesolicitud s, queresolucion r, quefinalidad f, queconcejo cwhere s.ca_permiso = r.ca_permiso and s.cn_finalidad = f.cn_finalidad and s.cn_concefin = c.cn_concejo and (s.fe_registro between to_date($P{fecha_inicio},'dd/mm/rrrr') and to_date($P{fecha_fin},'dd/mm/rrrr'))ORDER BY f.dl_finalidad asc, c.dl_concejo asc, r.cn_tiporesol asc]]></queryString>

Page 64: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

56

<field name="DL_FINALIDAD" class="java.lang.String"/> <group name="DL_CONCEJO" isStartNewColumn="false" isStartNewPage="false" isResetPageNumber="false" isReprintHeaderOnEachPage="false" minHeightToStartNewPage="0" > <groupExpression><![CDATA[$F{DL_CONCEJO}]]></groupExpression> <groupHeader> <band height="23" isSplitAllowed="true" > <rectangle radius="3" > <reportElement mode="Opaque" x="13" y="3" width="515" height="17" forecolor="#8080FF" backcolor="#B6CBEB" key="element-25" stretchType="NoStretch" positionType="FixRelativeToTop" isPrintRepeatedValues="true" isRemoveLineWhenBlank="false" isPrintInFirstWholeBand="false" isPrintWhenDetailOverflows="false"/> <graphicElement stretchType="NoStretch" pen="Thin" fill="Solid" /> </rectangle> <staticText> <reportElement mode="Transparent" x="20" y="4" width="65" height="14" forecolor="#6666FF" backcolor="#FFFFFF" key="element-26" stretchType="NoStretch" positionType="FixRelativeToTop" isPrintRepeatedValues="true" isRemoveLineWhenBlank="false" isPrintInFirstWholeBand="false" isPrintWhenDetailOverflows="false"/> <textElement textAlignment="Left" verticalAlignment="Middle" lineSpacing="Single"> <font fontName="Verdana" pdfFontName="Helvetica-Bold" size="10" isBold="true" isItalic="false" isUnderline="false" isPdfEmbedded ="false" isStrikeThrough="false" /> </textElement> <text><![CDATA[Concejo de]]></text> </staticText>

Bugs en el XML generado por iReport

Se han detectado una serie de problemas que hacen que el XML generado por iReports no respondadirectamente a lo que JasperReports espera encontrar, produciéndose fallos de compilación del XML unavez se intenta visualizar el informe en las aplicaciones construidas bajo el framework. Si el desarrolladorse encuentra con este problema, intente lo siguiente:

1. Editar el XML en un editor de texto

Page 65: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

57

2. Utilizar como encoding UTF-8 en lugar de ISO-8859-1. El XML debería comenzar con la siguientelínea: <?xml version="1.0" encoding="UTF-8"?>

3. Eliminar todas las referencias al atributo pdfEncoding. Los elementos de texto songenerados por defecto con el atributo pdfEncoding=”CP1252”. Borrar todos los literalespdfEncoding=”XXXXXX” del fichero XML.

4. Si se utilizan imágenes en los informes, modificar todas las rutas. La herramienta introduce rutasabsolutas para mapear las imágenes. Deberá cambiar estas rutas a relativas de acuerdo a la estructurade las aplicaciones que se estén desarrollando.

5. El creador de iReports ha introducido una opción para que las expresiones puedan ser multilínea(una facilidad para el diseñador del informe) Esta posibilidad se controla en Menú # Tools # Options(Pestaña General) # CheckBox Using multi line expresions. Si está habilitada, en el XML aparecerá elatributo isCode en ciertos elementos. Este atributo no forma parte del DTD, por lo que no será posibleparsear el XML convenientemente y mucho menos compilar el diseño por JasperReports. Eliminar elatributo isCode del XML.Es posible que en futuras versiones de JasperReports el atributo isCodeforme parte del DTD, si fructifican las conversaciones a este respecto entre el creador de JasperReprotsy el de iReports.

6. Si no se va a utilizar una clase Scriptlet en el informe, no será necesario queesto conste en el diseño. Es posible que al generar un nuevo diseño con la herramientaiReport se añada por defecto la clase dori.jasper.engine.JRDefaultScriptlet.En la versión de jasperreports actualmente utilizada (0.6.0) esta clase no existe, con loque si se intenta compilar el informe ocurrirá una excepción ClassNotFoundException.Para eliminar esta referencia se puede editar el XML y eliminar la siguiente líneascriptletClass="dori.jasper.engine.JRDefaultScriptlet” del elemento raíz<jasperReport>. También se puede hacer directamente con la herramienta iReport medianteProject # ProjectOptions # Pestaña Scriptlety configurando aquí la clase que se desea utilizar, oindicando que no se va a usar una clase Scriptlet.

“En la versión 0.4 de la herramienta iReports, estos bugs están resueltos, por lo tanto, no es necesariorealizar ninguna modificación al fichero XML generado.”

Compilación de Reports

Para poder utilizar los reports generados con la herramienta iReports, en las aplicaciones que utilizan elframework, es necesario que los ficheros XML obtenidos sean compilados al formato .jasper. Para ello,se puede utilizar la clase JasperCompilerManager o, mejor aún, el propio programa editor iReports.

Clases en el openFWPA para la renderización deinformes.

En el openFWPA se han incluido dos clases para la generación de informes: PrincastReport yPrincastMultiReport. La primera permite generar informes simples y la segunda de ellas, crearmulti-reports a partir de la agregación de varios informes sencillos.

Informes simples

Para crear un informe simple se usará la clase PrincastReport. Para poder crear unPrincastReport, es necesario indicarle, en su constructor, los parámetros:

Nombre del informe El informe debe tener un nombre. Este nombre puede ser el que se utilicepara generar un fichero PDF.

Page 66: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

58

Fuente de datos El origen de datos para cargar el informe también debe ser especificado. Pordefecto, se utilizará el origen de datos nulo: JREmptyDataSource. Elinforme no tomará datos de ninguna fuente.

Report compilado Se debe suministrar un stream de entrada (InputStream) que permita leerla definición compilada del informe. Este stream debe estar abierto sobre unfichero .jasper.

Además, si en el diseño del informe se han definido parámetros, sus valores deben ser especificados alPrincastReport utilizando el método setParams(Map).

Atención

Antes de obtener el informe es MUY recomendable pregenerarlo (para validar que no se producenerrores antes de exportar el informe). Para pregenerar un informe se debe utilizar el métodoprocess().

Para obtener un informe de un PrincastReport (correctamente creado y con los parámetros quenecesite asignados) se puede utilizar uno de los siguientes métodos:

a. getReportToPrint(). Devuelve el objeto jasper imprimible: JasperPrint. Este objeto puedeser manejado por las clases de Jasper Reports (se puede exportar a múltiples formatos, imprimir, etc.)

b. getPDFContent(). Devuelve el informe exportado a formato PDF, en un array de bytes, listo paraser volcado a un fichero (o enviado a un cliente, etc.)

c. exportPDF(OutputStream). Exporta el informe, en formato PDF, al OutputStream que sepasa como parámetro.

A continuación se muestra un ejemplo de construcción de un PrincastReport.

//Obtener el fichero del Report (.jasper)InputStream stream = loadReport(REPORT_NAME+".jasper"); //Obtener origen de datosList listaProducto = CarritoDelegateFactory.getCarritoDelegate().getListaProducto();JRDataSource dataSource = new JRBeanCollectionDataSource(listaProducto); //Obtener parametrosMap parameters = new HashMap();MessageResources messages = (MessageResources) request.getAttribute(Globals.MESSAGES_KEY); parameters.put("P_Titulo", messages.getMessage("report.title"));parameters.put("P_AmpliacionTitulo", messages.getMessage("report.description")); //Crear reportPrincastReport report = new PrincastReport(REPORT_NAME, dataSource, stream);report.setParams(parameters);report.process();

Multi Reports

Los Multi-reports son informes compuestos de informes simples. El objetivo de los multi-reports esagrupar un conjunto de informes para que puedan ser volcados en un mismo fichero PDF.

El openFWPA incluye la clase PrincastMultiReport para la generación de informes compuestos.Esta clase es muy simple de manejar: basta con especificarle un nombre (para el fichero PDF) y añadirtodos los informes simples que se quieran adjuntar.

Page 67: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

59

Los multi-reports también deben ser procesados, al igual que los informes simples, utilizando el métodoprocess().

El informe se puede obtener con el método getPDFContent() que, al igual que en la clasePrincastReport, devuelve un array de bytes con el informe en formato PDF, listo para ser volcado aun fichero. También se puede exportar con el método exportPDF(OutputStream).

Si, durante la composición de un multi-report, se produce un error en la generación de alguno de losinformes que componen, existen dos posibles políticas para su tratamiento:

a. Ignorar el error. Se trata del comportamiento por defecto. El multi-report se compondrá normalmente,se ignora el informe defectuoso.

b. Propagar el error. En este caso, se interrumpe la generación del multi-report y se eleva una excepciónPrincastReportException. Para activar esta política, el multi-report se debe crear utilizando elconstructor: PrincastMultiReport(String name, bolean failOnError), asignandoel valor true al parámetro failOnError.

Las fuentes de datos de los informes.

Los informes visualizarán contenido dinámico, provenientes de dos tipos diferentes de fuentes de datos.

1. DataSources JDBC Connection: conexión con una base de datos relacional.

2. JRDataSources: fuentes de datos JasperReports. La librería de clases de Jasper Reports proveede una serie de implementaciones de este tipo de fuentes de datos. Las más interesantes son aquellasque gestionan collecciones de Beans java (en forma de Collection, Array o Map de Beans). Esrecomendable el uso de estas fuentes de datos, en detrimento de las anteriores, ya que de esta forma nose rompe la encapsulación de las capas de la aplicación.

Utilizando JasperReports en Linux / Unix sin X11.ADVERTENCIA: No es posible generar informes con imágenes (escudos, marcas de agua, códigos debarras, etc.) en un entorno Unix/Linux sin las librerías X11.

Clase para la generación de tablas en PDF.

El openFWPA incluye la clase PDFTableExporter para la generación de informes PDF en forma detabla, de esta manera es posible realizar de una manera sencilla listados en PDF. Para usar esta clase se haráen combinación con una PrincastPDFReportAction, sobrescribiendo el método getReport().Un ejemplo de su uso es el siguiente:

// número de columnas de la tabla, las filas se ajusta automáticamenteint columnas = 3;// nombre del reportString REPORT_NAME = "tabla";

PDFTableExporter tableExporter = new PDFTableExporter(REPORT_NAME, columnas);// se establece el título del reporttableExporter.setTitle("Mi título");// se añaden celdas de cabecera (aparecen sombreadas)tableExporter.addCellHeader("Enero");tableExporter.addCellHeader("Febrero");tableExporter.addCellHeader("Marzo");// se añaden las celdas de la tablatableExporter.addCell("1");

Page 68: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Construccion deinformes con openFWPA

60

tableExporter.addCell("2");tableExporter.addCell("3");// se retorna el objetoreturn tableExporter;

Page 69: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

61

Capítulo 6. Operaciones

Sistema de Inicialización y ArranqueEl núcleo del openFWPA se construye sobre un sistema de inicialización que permite definir, de formadeclarativa, los componentes que deben ser creados, configurados e iniciados durante el período dearranque de las aplicaciones. Generalmente estos componentes son objetos de utilidad que tienen alcanceglobal a toda la aplicación, como el sistema de logging, de configuración, monitorización, contadores,consola de administración, etc.

A partir de la versión 1.3 del openFWPA el Sistema de Inicialización se basa en el framework IoC Spring(www.springframework.org).

Declaración de objetos inicializablesEl fichero de arranque de las aplicaciones que utilizan el openFWPA es: princast-init-script.xml. En este fichero se deben definir los objetos que serán accesibles durante el periodo deinicialización.

La estructura del fichero princast-init-script.xml se ajusta a la DTD de los ficheros de inicialización delframework Spring (http://www.springframework.org/dtd/spring-beans.dtd).

Para definir un objeto, se utilizará la etiqueta <bean>, indicando, como atributos un identificador paradicho objeto y su clase (opcionalmente se puede indicar si es un Singleton). Dentro de la etiqueta <bean>se pueden establecer propiedades de los objetos de forma declarativa utilizando la etiqueta <property>(se considera propiedad de un objeto a todo método que empiece por la cadena “set” y tenga un soloparámetro. Ver documentación oficial de Java Beans de Sun en: java.sun.com).

Este sistema para dar valor a propiedades de métodos se llama “Inversion Of Control (IoC)”.

Las propiedades pueden ser valores introducidos directamente (etiqueta <value>), o referencias a otrosobjetos definidos en el mismo fichero (etiqueta <ref id=”…”>).

<bean id="securityRulesPlugin" class="es.princast.framework.web.filter.security.corp.conf.SecurityRulesConfigurationPlugin"> <constructor-arg><value>security-rules</value></constructor-arg> <property name="file"><value>WEB-INF/princast-security-rules.xml</value></property> <property name="contexts"> <list> <value>SECURITY</value> </list> </property> </bean> <bean id="jmxBasePluginCap" class="es.princast.framework.core.management.configuration.ConfigurationPluginJMXAdapter"> <property name="plugin"><ref bean="baseConfigurationPlugin"/></property></bean>

Existen muchas otras etiquetas que permiten: especificar valores para los parámetros del constructor,asignar colecciones a propiedades de los objetos, definir variables al estilo ANT (${name}), etc.

Para mas información al respecto consulte la página web del framework Spring o consulte la guía dereferencia incluida en la documentación del openFWPA.

Page 70: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

62

Variables ${…} en el fichero de inicialización

En el fichero princast-init-script.xml es posible utilizar variables al “estilo ANT”: ${nombre}. Losvalores de estas variables se obtienen de un fichero de propiedades.

Para poder utilizar este tipo de variables, es necesario incluir, en el propio fichero princast-init.script.xml, el siguiente bean:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"><value>”ruta del fichero”</value></property></bean>

Donde “ruta del fichero” es el path del fichero de properties del que se cargarán los valores de las variables. La ruta de este fichero es relativa al classpath.Un ejemplo de uso de variables es el que sigue:

<bean id="myBean" class="es.princast.framework.examples.MyBean” lazy-init="false" singleton="true"> <property name="exampleProp"><value>${PROP}</value></property></bean>

Es posible utilizar rutas de fichero absolutas. Para ello, es necesario utilizar la construcción de Spring“FileSystemResources”, tal y como se indica en el siguiente ejemplo:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <bean class="org.springframework.core.io.FileSystemResource"> <constructor-arg><value>c:/deploy.properties</value></constructor-arg> </bean> </property></bean>

Desarrollo de objetos inicializablesEs posible implementar objetos para que sean arrancados de forma automática, durante la inicialización,por el openFWPA. Todos los objetos que están definidos en el fichero princast-init-script.xmlpueden ser inicializados y arrancados automáticamente. No es necesario que los objetos implementenningún interfaz específico pero se tendrá en cuenta:

a. Si los objetos implementan el interfaz Configurable, además de ser creados serán configurados deforma automática por el framework.

b. Si los objetos implementan el interfaz Launchable, serán creados de forma automática (ejecutándosecualquier tarea que tengan implementada bajo el método create()).

c. Si los objetos implementan el interfaz RegistrableMBean, éstos serán registrados bajo el sistemade gestión JMX.

El sistema de inicialización siempre arranca los siguientes objetos:

• Manager de logging.

• Factoría del sistema de control y gestión JMX. ManagementFactory.

• Sistema de condiguración. FrameworkConfigurator

Page 71: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

63

Arranque de aplicaciones webA partir de la versión 1.3 del openFWPA, el componente inicialziador de las aplicacioneses: PrincastStartupListener. El servlet de inicialziación: PrincastStartupServlet,utilizado en versiones anteriores, debido a problemas de estabilidad en los ClassLoaders del servidor deaplicaciones, ha quedado deprecado.

El startup-listener implementa el interfaz ServletContextListener y, por tanto, debe ser declaradoen el fichero web.xml. Además, también se debe declarar un parámetro de contexto (<context-param>) que indique la ruta del fichero de arranque princast-init-script.xml, tal y como semuestra en el siguiente ejemplo:

<context-param> <param-name>INIT.SCRIPT.FILE</param-name> <param-value>/WEB-INF/princast-init-script.xml</param-value> </context-param> <listener> <listener-class>es.princast.framework.web.startup.PrincastStartupListener</listener-class> </listener>

Arranque manualEl arranque automático de los servicios del openFWPA (configuracion, management, etc.) unicamenteestá operativo en aplicaciones web (al estar basado en un ServletContextListener). Para cualquierotro tipo de aplicaciones (consola, EJBs, etc.) en las cuales no hay una parte web disponible, para accedera los servicios del openFWPA es necesario lanzar la inicialziación de forma manual.

Para realizar esta tarea, se incluye la clase PrincastStandaloneInitializer. Esta clase se debeiniciar (utilizando el método estático init()) al arrancarse la aplicación. Durante la ejecución ponea disposición de la aplciación todos los beans declarados en el fichero de inicialización (princast-init.script.xml), a través del PrincastApplicationContext, que se obtiene con el métodogetInitScriptContext().

InputStream stream = this.getClass().getClassLoader().getResourceAsStream(INIT_SCRIPT);

//Default configuration propertiesProperties props = new Properties();props.put("myProp", "myValue");

PrincastStandaloneInitializer.init(stream, props);

Para finalizar la aplicación se debe llamar al método estático finish().

El método init() también permite tamabién cargar todos los ficheros XML de definición de beans(de Spring) que sean necesarios para la aplicación. Para especificar los ficheros a cargar se indicarán lospatrones correspondientes (siguiendo el convenio de nombrado habitual en Spring), teniendo en cuentaque se tomarán relativos al classpath.

Sistema de Configuración de AplicacionesOtro de los sistemas del núcleo del openFWPA es el Sistema de Configuración. El sistema de configuraciónpermite, tanto a los componentes de las aplicaciones como del propio framework, recibir parámetros de

Page 72: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

64

configuración de forma completamente transparente, sin necesidad de preocuparse por la forma o el lugaren que éstos están almacenados.

Figura 6.1. Estructura del sistema de configuración

El Sistema de Configuración actúa como un almacén centralizado de parámetros de configuración. En laconfiguración de aplicaciones intervienen los siguientes componentes:

• Plugins de configuración (ConfigurationPlugin). Se trata de objetos que pueden acceder a unalmacén de parámetros de configuración, ya sea para únicamente recuperar sus valores, o también paraactualizarlos.

• Contextos de configuración. Los contextos son conjuntos de parámetros agrupados por su funcionalidad.Los parámetros agrupados en un mismo contexto sirven, habitualmente, a objetos relacionados entre sí.

• Configurador de aplicaciones (FrameworkConfigurator). Es el componente central del sistemade configuración. Se encarga de cargar los parámetros y repartirlos a los objetos que los necesiten.

• Objetos configurables (Configurable). Este tipo de objetos escuchan eventos en el “Configuradorde Aplicaciones” y pueden actualizar su estado a medida que la configuración de la aplicación cambia.Los listeners son, generalmente, objetos de aplicación que necesitan acceder a los parámetros queproporciona el Sistema de Configuración del openFWPA.

Implementación de objetos configurablesPara que un objeto pueda acceder a los parámetros del sistema centralizado de configuración delopenFWPA debe:

1. Implementar el interface Configurable.

Atención

El interface ConfigurationListener ha sido marcado como "deprecated". En su lugar,se debe utilizar el interface Configurable.

2. Registrarse en el configurador: FrameworkConfigurator.

Implementando el interface Configurable

El interface Configurable define una serie de métodos para la gestión del ciclo de vida del objetoconfigurable. Los tres métodos definidos son, en realidad, métodos callback. Únicamente van a serutilizados por el sistema de configuración y no se deben invocar directamente desde objetos de laaplicación.

Estos métodos son:

configure() Se invoca cuando el objeto se configura por primera vez. Cuando se llama aeste método, se presupone que el objeto no ha sido configurado con anterioridad.Este método recibe como argumento un conjunto de parámetros de configuración(ConfigurationParameters).

reconfigure() Se invoca cuando el valor de algún parámetro que pueda ser relevante para laconfiguración del objeto, es actualizado. Este método recibe como parámetro unevento de configuración (ConfigurationEvent).

Page 73: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

65

En la implementación de estos métodos, el objeto configurable debe obtener, bien del conjunto deparámetros (ConfigurationParameters), bien del evento (ConfigurationEvent) los datosque necesite para su configuración.

Conjunto de Parámetros de Configuración (ConfigurationParameters)

Se trata de un almacén que contiene todos los parámetros de configuración de la aplicación, agrupadosen contextos. Es responsabilidad del objeto configurable seleccionar aquellos parámetros que le puedanser de utilidad.

Se pueden obtener todos los parámetros de un conjunto utilizando el método getKeys(). Este métododevuelve una enumeración (Enumeration) con los nombres (String) de todos los parámetrosexistentes en el sistema. Otra opción es buscar únicamente los parámetros clasificados bajo un contextodeterminado. En este caso, se utilizará el método getKeys(String), pasándo como argumento elnombre del contexto cuyos parámetros se quieren enumerar.

Para obtener un parámetro del conjunto ConfigurationParameters, se utilizará el métodogetParameter(String), si se sabe con certeza que el valor del parámetro es una cadena decaracteres, o el método getConfigurationObject(String) si se quiere recuperar un Objectgenérico. Ambos métodos reciben como argumento el nombre del parámetro a localizar.

Utilizando estos métodos, si dos parámetros coinciden en nombre, se devolverá el primero quese encuentre. Para evitar colisiones de nombrado, se pueden utilizar los métodos alternativos:getParameter(String, String) y getConfigurationObject(String, String). Alos cuales, se les especifica, como primer argumento, el nombre del contexto en el que se quiere buscarel parámetro.

public void configure(ConfigurationParameters params) {

String param = params.getParameter(”MY_FOO_KEY_1”) ; if (param != null) { this.fooKey = param; }

param = params.getParameter(“FOO_CONTEXT”, “MY_FOO_KEY_2”); if (param != null) { this.fooKey2 = param; }}

Eventos de Configuración (ConfigurationEvent)

Los eventos de configuración se disparan cuando se produce una actualización en los valores de losparámetros de configuración. Cuando un evento de configuración se dispara se invocan los métodosreconfigure() de los objetos configurables.

Es responsabilidad del desarrollador de los objetos configurables implementar la respuesta que tendránsus objetos ante determinados eventos.

En realidad, el método reconfigure(), son dos métodos:

• reconfigure(ConfigurationEvent). Recibe un evento de configuración de carácter general.No existe una causa concreta que peda disparar este evento: reinicio del sistema, recarga deun fichero de configuración, etc. El objeto event proporciona: nombre del contexto afectado

Page 74: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

66

por el evento (getContextName()) y conjunto de parámetros de configuración actualizados(getParameters()).

• reconfigure(ConfigurationParameterUpdatedEvent). Recibe un evento dereconfiguración más específico que indica la actualización de un único parámetro de configuración. Elobjeto event, además de la información suministrada por la superclase (ConfigurationEvent),proporciona,: nombre del parámetro actualizado (getParameterName()), valor anterior(getFormerValue()) y nuevo valor (getCurrentValue()).

public void reconfigure(ConfigurationParameterUpdatedEvent event) {

if (event.getContext().equals(this.configurationConetxtName)) { if (event.getParameterName().equals(“MY_FOO_KEY_1”)) { this.fooKey = event.getCurrentValue().toString(); } else if (event.getParameterName().equals(“MY_FOO_KEY_2”)) { this.fooKey2 = event.getCurrentValue().toString(); }}}

public void reconfigure(ConfigurationEvent event) {

if (event.getContext().equals(this.configurationConetxtName)) { this.configure(event.getParameters()); }}

Registro de un objeto en el FrameworkConfigurator.

Por último, para que un objeto (que implemente Configurable) pueda ser gestionado por el sistema deconfiguración, éste debe ser registrado en el configurador del openFWPA. Para registrar un objeto, bastacon llamar al método configureMe() del FrameworkConfigurator.

public MyClass() { FrameworkConfigurator.getConfigurator().configureMe(this, true);}

Este método recibe dos parámetros: el objeto configurable y un valor booleano que indica si el objetoquiere escuchar, o no, eventos de reconfiguración.

Plugins de ConfiguraciónLos plugins de configuración son los componentes que proporcionan los parámetros que maneja el Sistemade Configuración. Un plugin de configuración se encarga de gestionar un único almacén de datos (unfichero, una base de datos, etc.), sin embargo, un plugin puede servir parámetros a varios contextos deconfiguración. Los plugins de configuración, implementan el interface ConfigurationPlugin.

Añadir un plugin de configuración

Si una aplicación tiene alguna necesidad de configuración, la mejor opción suele ser definir plugins deconfiguración específicos. Para ello, es necesario seguir los siguientes pasos:

1. Seleccionar, de los tipos de plugins que contiene el openFWPA, el más adecuado.

Page 75: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

67

2. Si no se encuentra ninguno que se ajuste a las necesidades, implementar uno propio.

3. Escribir los parámetros de configuración en el almacén seleccionado.

4. Declarar el plugin en el fichero de arranque de la aplicación (princast-init-script.xml).

5. Opcionalmente, definir un bean manager para la gestión del plugin a través de la consola JMX.

6. En el propio fichero princast-init-script.xml, registrar el plugin en la declaración del beanFrameworkConfigurator.

Declaración de plugins en el fichero de inicailización.

Antes de poder utilizar un plugin de configuración es necesario definirlo en el fichero de arranque de laaplicación (princast-init-script.xml).

Los plugins de configuración son objetos inicializables del openFWPA (Ver “Declaración de objetosinicializables”) y se deben definir como beans en el fichero de arranque.

<bean id="baseConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesFileConfigurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> <property name="priority"><value>12</value></property> <property name="file"><value>${base.module.file}</value></property> <property name="contexts"> <list> <value>SECURITY</value> <value>ROOT.CONTEXT</value> <value>ACTION.CONTEXT</value> <value>JMX.CONTEXT</value> </list> </property></bean>

Los parámetros que se definen en el ejemplo anterior son:

• <constructor-arg><value>pluginId</value></constructor-arg>. (Obligatoriopara todos los plugins) Se debe definir, como argumento de constructor, el nombre identificativo delplugin (en este caso: “basePlugin”).

• <property name="priority">. Se trata de la prioridad del plug-in. En caso de que haya variosplugins que, para un mismo contexto ofrezcan parametros de igual nombre (es decir, en case de quehaya conflicto de nombres de los parámetros), se tomarán primero los parámetros ofertados por el plug-in de prioridad mas alta (numero más bajo). Si no se establece ningún valor, por defecto, la prioridadde todos los plug-ins es 10 (un valor intermedio).

La prioridad permite definir jerarquías de plug-ins de configuración. De esta forma, se permitedefinir plug-ins a nivel de contenedor (con parámetros comunes a todas las aplicaciones) que puedensobreescritos para definir parámetros específicos para cada aplicación.

• <property name="file">. Algunas propiedades se deben definir, o no, en función del tipo deplugin. En este caso, el plugin PropertiesFileConfigurationPlugin, exige la definición dela property “file”.

• <property name="contexts">. (Obligatorio para todos los plugins) Por ultimo, se debe definirla lista de contextos a los que el plugin sirve parámetros. En este caso, el plugin sirve parámetros al

Page 76: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

68

contexto raíz (ROOT.CONTEXT), al contexto de seguridad (SECURITY) , al contexto del sistema degestión JMX (JMX.CONTEXT) y al contexto de las Actions de la aplicación (ACTION.CONTEXT).

Opcionalmente, una vez definido el plugin, se le puede asignar un adaptador JMX de forma que éste puedaser manejado desde la consola HTML de la aplicación (Ver “Consola de gestión”).

Si se define un adaptador, será posible, a través de la consola, actualizar o referescar, en caliente, los valoresde los pa-rámetros de configuración del plugin (siempre y cuando el plugin soporte estas operaciones).

<bean id="jmxBasePluginCap" class="es.princast.framework.core.management.configuration.ConfigurationPluginJMXAdapter"> <property name="plugin"><ref bean="baseConfigurationPlugin"/></property></bean>

El adaptador JMX es otro bean, del tipo: ConfigurationPluginJMXAdapter. Únicamente esnecesario indicar la referencia del plugin (utilizando su identificador de bean: <bean id=…) que debegestionar, al definir la propiedad “plugin”, tal y como se muestra en el ejemplo anterior.

Para finalizar, es necesario registrar el plugin en la definición del FrameworkConfigurator (que noes más que otro bean en el fichero princast-init-script.xml). Se debe añadir la referencia alplugin en la propiedad: “plugins”.

<bean id="configurationManager" class="es.princast.framework.core.configuration.FrameworkConfigurator" factory-method="getConfigurator" lazy-init="false" singleton="true"> <property name="plugins"> <list> <ref bean="baseConfigurationPlugin"/> <ref bean="jaasConfigPlugin"/> <ref bean="securityRulesPlugin"/> </list> </property> </bean>

Plugins en openFWPA.

En el openFWPA se incluyen algunos plugins de configuración listos para ser utilizados. Además, seproporcionan clases para facilitar el desarrollo de plugins por parte de los usuarios.

Figura 6.2. Jerarquía de Plugins

Plugins basados en Properties

Los plugins basados en Properties almacenan parámetros como pares {atributo, valor}. En elopenFWPA se empaquetan dos clases para gestionar este tipo de parámetros:

• PropertiesConfigurationPlugin, para cargar los datos de configuración de un objetojava.util.Properties en memoria.

<bean id="myPropsConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesConfigurationPlugin"> <constructor-arg><value>propsPlugin</value></constructor-arg> <property name="priority"><value>12</value></property> <property name="properties"> <map> <entry key="PARAMETRO.UNO"> <value>Un valor</value>

Page 77: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

69

</entry> <entry key="PARAMETRO.DOS"> <value>Otro valor</value> </entry> </map> </property> <property name="contexts"> <list> <value>CONTEXTO.UNO</value> </list> </property> </bean>

En la propiedad “properties” se deben indicar todos los pares {clave, valor} que va a servir elplugin. Este plugin puede ser actualizado, pero no se pueden guardar los cambios realizados.

• PropertiesFileConfigurationPlugin, que obtiene los parámetros de un fichero deproperties.

<bean id="baseConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesFileConfigurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> <property name="priority"><value>12</value></property> <property name="file"><value>${base.module.file}</value></property> <property name="contexts"> <list> <value>SECURITY</value> <value>ROOT.CONTEXT</value> <value>ACTION.CONTEXT</value> <value>JMX.CONTEXT</value> </list> </property> </bean>

En la propiedad “file” se debe especificar la ruta del fichero .properties a cargar. Esta ruta puedeser absoluta, relativa al contexto de la aplicación web, o al classpath (siempre que empiece por la cadena“classpath://”).

Plugins basados en XML

Este tipo de plugins obtienen la configuración de un documento XML. Este documento puede estar enmemoria (en un String, por ejemplo), o en un fichero.

• XMLStringConfigurationProperties. Carga la configuración directamente de un String.

<bean id="xmlStringConfigurationPlugin" class="myapp.MyXMLStringConfigurationPlugin"> <constructor-arg><value>xmlStringPlugin</value></constructor-arg> <property name="priority"><value>12</value></property> <property name="xml"> <value> <![CDATA[ <elements> <element value='myId' label='bar'/> <element value='myId2' label='bar2'/> </elements> ]]> </value>

Page 78: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

70

</property> <property name="contexts"> <list> <value>EXAMPLE.CONTEXT</value> </list> </property> </bean>

Para configurar este plugin, debe escribirse el documento XML (que contiene la configuración) en lapropiedad “xml”. Es importante observar que debe escribirse en una sección CDATA, de lo contrario,el fichero de inicio no sería valido de acuerdo a su DTD. Este plugin es de solo-lectura.

• XMLFileConfigurationPlugin. Carga la configuración de un fichero xml.

<bean id="xmlFileConfigurationPlugin" class="myapp.MyXMLFileConfigurationPlugin"> <constructor-arg><value>xmlFilePlugin</value></constructor-arg> <property name="priority"><value>12</value></property> <property name="file"> <value>classpath://myfile.xml</value> </property> <property name="contexts"> <list> <value>EXAMPLE.CONTEXT</value> </list> </property></bean>

Para configurar este plugin, se debe indicar el fichero xml de configuración en la propiedad “file”. Elpath de este fichero puede ser absoluto, relativo respecto al contexto de la aplicación web, o respecto alclasspath (si empieza por la cadena “classpath://”, como ocurre en el ejemplo).

Debido a la flexibilidad del formato XML, este tipo de plugins delegan el análisis del documento, y lagestión de los parámetros de configuración, en un objeto auxiliar: XMLConfigContentHandler.

Para definir un plugin de configuración XML, es necesario extender la claseXMLConfigContentHandler. Se deben implementar métodos para:

• Analizar el documento XML. El análisis del documento se realiza utilizando el API Apache CommonsDigester. Los métodos a implementar son: setDigesterRules(Digester), en el que se definiránlas reglas del Digester para analizar el fichero y registerDTDs(Digester), para registrar lasDTD que se quieran utilizar para validar el documento XML proporcionado.

• Gestionar los parámetros de configuración. Además, se deben implementar métodos que permitanobtener un determinado parámetro (getParameter()), escribir un valor (setParameter()), etc.

/** * * El esquema de la configuración que acepta es: * * <elements> * <element value="myId" label="bar"/> * <element value="myId2" label="bar2"/> * </elements> */public class FooContentHandler extends XMLConfigContentHandler {

Page 79: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

71

public FooContentHandler(String name) { super(name); } protected void setDigesterRules(Digester digester) {

digester.addObjectCreate("elements", "java.util.ArrayList"); digester.addObjectCreate("elements/element", "es.princast.framework.core.vo.PropertyBean"); digester.addSetProperties("elements/element"); digester.addSetNext("elements/element", "add", "es.princast.framework.core.vo.PropertyBean");

} public void setParameter(String key, String value) {

throw new UnsupportedOperationException(); } public List getConfigurationObjects() {

return (List) getXMLObject(); } public String getParameter(String key) {

PropertyBean pb = (PropertyBean) getConfigurationObject(key);

return (pb != null) ? pb.getLabel() : null; } public Enumeration getKeys() {

List objects = getConfigurationObjects(); Vector keys = new Vector(objects.size());

Iterator it = objects.iterator(); while (it.hasNext()) { PropertyBean pb = (PropertyBean) it.next(); keys.add(pb.getValue()); }

return keys.elements(); } public Object getConfigurationObject(String key) {

List objects = getConfigurationObjects();

Iterator it = objects.iterator(); while (it.hasNext()) { PropertyBean pb = (PropertyBean) it.next(); if (key.equals(pb.getValue())) { return pb; } }

return null;

Page 80: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

72

} public void setConfigurationObject(String key, Object value) {

PropertyBean pb = (PropertyBean) getConfigurationObject(key);

if (pb != null) { if (value instanceof PropertyBean) { pb.setLabel(((PropertyBean) value).getLabel()); } else { pb.setLabel(value.toString()); } } else { List objects = (List) getXMLObject(); if (value instanceof PropertyBean) { objects.add(pb); } else { objects.add(new PropertyBean(key, value.toString())); } } } public String getParameter(String path, String key) { return getParameter(key); } public Object getConfigurationObject(String path, String key) { return getConfigurationObject(key); } protected void registerDTDs(Digester digester) { //No se registran DTDs }}

LoggingPara la gestión de las sentencias de log, en el openFWPA, se utiliza la librería: Log4j [18] (http://logging.apache.org/log4j/). Log4j tiene tres componentes principales: loggers, appenders ylayouts. Estos tres tipos de componentes trabajan juntos para permitir a los desarrolladores escribirmensajes de log de acuerdo a un tipo de mensaje y prioridad, y para controlar en tiempo de ejecución laforma en que estos mensajes se formatean y donde se escriben.

Log4J. Componentes

Loggers

Son entidades con nombre. Sus nombres son sensibles al contexto y siguen una regla denombrado jerárquica. Por ejemplo, el logger de nombre “com.foo” es padre del logger de nombre“com.foo.Bar”.

El logger root reside en la cima de la jerarquía de logres. Siempre existe y no puede ser recuperado pornombre. Para recuperarlo se ha de invocar a método estático Logger.getRootLogger().

Es posible asignar un nivel a un logger. Los niveles disponibles por orden de menor a mayor son:

DEBUG para mostrar mensajes de depuración

Page 81: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

73

INFO para mostrar información sobre lo que está haciendo la aplicación

WARN para mostrar mensajes de alerta sobre eventos de los que se desea mantener constancia, pero queno afectan al correcto funcionamiento del problema

ERROR para mostrar mensajes de error que afectan a la aplicación, pero que lo permiten seguirfuncionando (por ejemplo, algún error en un parámetro de configuración)

FATAL para mostrar mensajes críticos del sistema, generalmente después del mensaje la aplicaciónfinalizará

Si a un logger no se le asigna un nivel, lo hereda de su antecesor más cercano que tenga asignado uno.Para asegurarse que todos los loggers tienen un nivel, el logger raíz siempre tiene asignado un nivel.

Las escrituras se realizan invocando a alguno de los métodos de impresión del logger: debug, info, warn,error, fatal y log. El método de impresión define el nivel de la escritura. Una escritura se dice que estáhabilitada si su nivel es mayor o igual que el nivel de su logger. De otra forma se dice que la escritura estadeshabilitada. El orden de los niveles es: DEBUG < INFO < WARN < ERROR < FATAL.

Appenders

Log4j permite que los mensajes se impriman en múltiples destinos, a cada uno de los cuales se ledenomina Appender. Algunos de los Appenders disponibles son:

ConsoleAppender escribe los mensajes de log en la consola

FileAppender escribe los mensajes de log en un fichero

RollingFileAppender escribe los mensajes de log a un fichero al que se le pueden definirpolíticas de rotación para que no crezca indefinidamente

DailyRollingFileAppender escribe los mensajes de log en un fichero a que se le puede definirpolíticas de rotación basadas en la fecha

SocketAppender escribe los mensajes de log hacia un servidor remoto de log

SMTPAppender envía un correo electrónico con los mensajes de log, generalmentese utiliza para los niveles ERROR y FATAL

JDBCAppender escribe los mensajes de error a una base de datos

SyslogAppender escribe los mensajes de error hacia el daemon syslog de los sistemasoperativos Unix

NTEventLogAppender escribe los mensajes de log en los log del sistema de Windows NT

JMSAppenders serializa los eventos y los transmite como mensaje JMS de tipoObjectMessage

Para más información sobre las opciones de configuración de los Appenders, consultar ladocumentación de Log4j.

Layouts

El Layout es el responsable de formatear los mensajes de log de acuerdo a las definiciones deldesarrollador. Los tipos de Layouts disponibles son:

Page 82: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

74

SimpleLayout prioridad del mensaje seguida por “-“ y luego del mensaje de log

PatternLayout especifica el formato de salida de acuerdo a unos patrones de conversión similaresa los de la función printf del lenguaje C (para más información consultar ladocumentación)

HTMLLayout especifica que la salida será una tabla HTML

XMLLayout especifica que la salida será un fichero XML que cumple con el log4j.dtd

TTCCLayout consiste en la fecha, thread, categoría y NDC, cualquiera de estos cuatro campospuede ser deshabilitado y habilitado individualmente

ConfiguraciónEn el diagrama de la Figura 6.3, “Estados del Sistema de Logging” se puede ver el ciclo de vida de laconfiguración del Sistema de Log de una aplicación que utiliza openFWPA. Los estados por los que puedepasar son los que siguen:

1. No Log: En este estado no hay configuración alguna cargada. Si se lanza alguna sentencia de log,aparecerá un WARNING en la salida estándar del servidor OC4J. Este estado no debería producirse perose puede dar en caso de que se inicialicen componentes antes que el framework (listeners, ejbs, etc.)

2. Arranque: Se carga la configuración de arranque del openFWPA. Esta configuración se definirá enel fichero log4j.properties ubicado en el classpath de la aplicación. En este fichero se puededefinir una configuración de log, únicamente activa durante el proceso de arranque de la aplicación.

3. Runtime: En este estado se supone activa la configuración de log de la aplicación. Esta configuraciónse define en el fichero xml indicado en el parámetro de configuración LOGGING_XMLCONF, o en elfichero de properties, cuyo path se indica en el parámetro de configuración, LOGGING_PROPERTIES.

Figura 6.3. Estados del Sistema de Logging

Ejemplo de Configuración

El sistema de logging se configura a través de, por ejemplo, el fichero WEB-INF/log4j.xml (path quese especifica bajo la constante de configuración: LOGGING_XMLCONF). Este fichero debiera estar dentrodel fichero war de la aplicación. Puede verse un ejemplo de utilización en la aplicación en blanco (AppBlank) y en la de ejemplo (Sample App).

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>

<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> </layout> </appender> <appender name="HTML" class="org.apache.log4j.DailyRollingFileAppender">

Page 83: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

75

<param name="File" value="carrito-log.html" /> <layout class="org.apache.log4j.HTMLLayout"> </layout> </appender> <appender name="AUDIT" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="carrito-audit.html" /> <layout class="org.apache.log4j.HTMLLayout"> </layout> </appender>

<category name="es.princast.framework"> <appender-ref ref="HTML" /> </category> <category name="PISTA_AUDITORIA"> <priority value ="INFO" /> <appender-ref ref="AUDIT" /> </category> <root> <priority value ="info" /> <appender-ref ref="HTML" /> </root>

</log4j:configuration>

Atención

Obsérvese que a pesar de haberse definido el appender STDOUT, no se está utilizando en ningúncaso. Este "no uso" es intencionado para evitar que, por descuido o por accidente, se vuelqueindiscriminadamente el log de las aplicaciones en la consola de los servidores, ya que se puedeproducir un error por desbordamiento, en caso (muy probable) de que los administradores haganuna redirección a fichero de la salida.

En este fichero se definen varios Appenders (o destinos donde el logger va a escribir). La salida estándar,un fichero Html (que cambiará todos los dias) y otro Appender de nombre Audit similar al anterior. Paracada uno de ellos se define también el layout o plantilla que se utilizará para escribir la salida.

También se definen categorías, donde se especifica para cada logger el nivel que tendrá y los Appendersque utilizará.

A continuación se muestra la salida de un Appender que escribe en un fichero Html.

Para más información sobre la configuración del logger consultar la documentación del mismo en ladirección http://logging.apache.org/log4j/docs/manual.html

Componentes del openFWPA para Logging.

LoggingManager

En el núcleo (core) del openFWPA se define la clase LoggingManager, cuyo objeto es ser uncontrolador centralizado para todo el Sistema de Logging. Esta clase permite:

• Cargar configuraciones Log4J desde ficheros (xml o de properties)

Page 84: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

76

• Obtener los loggers estándar del framework: Pista de Auditoría y Pista de Rendimiento.

El LoggerManager se puede gestionar desde la consola JMX.

PrincastHTMLLayout

Además de los layouts incluidos en Log4J (Ver “Layouts”), las aplicaciones también pueden utilizarPrincastHTMLLayout. Este objeto es igual que el HTMLLayout estándar, con la diferencia de quemuestra la fecha y hora en que se escribe la sentencia de log.

Pista de auditoríaLas aplicaciones han de registrar todas las operaciones de usuario que realicen en la denominada pistade auditoria. Para cada entrada aparecerá el usuario y la dirección IP desde la que se realizó la operación(registro NDC), más una descripción de la operación realizada.

La salida es configurable, pudiendo ser de distintos formatos y en distintos soportes. Por defecto, sevuelca en un fichero en formato HTML. Cada día se mueve el contenido de este log a un fichero con laextensión .YYYY-MM-DD. El siguiente ejemplo se ha sacado de la aplicación de ejemplo, y muestra laejecución de dos operaciones marcadas como auditables:

Un ejemplo de código que escribe información en la pista de auditoria sería el siguiente:

LoggingManager.getLogging().getAuditingTrack().info("[" + NDC.peek() + "] Añadiendo producto al carrito");

Se hace uso de la clase LoggingManager de openFWPA, la cual se obtiene mediante el método estáticogetLogging(). El método getAuditingTrack() del LoggingManager devolverá la pista deauditoria, en la cual se escribirá como si se tratase de cualquier otro logger. Es necesario incluir lainformación sobre el usuario y la IP desde la que esta accediendo, información disponible mediante elmétodo peek de la clase NDC de Log4j.

La pista de auditoria es un logger (Log4J) sobre el que se escribe la traza de accesos. Este logger seconfigura, de igual forma que los loggers de aplicación, utilizando el fichero log4j.xml.

<appender name="AUDIT" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="example-audit.html" /> <layout class="es.princast.framework.core.logging.layouts.PrincastHTMLLayout"> </layout> </appender>

<category name="PISTA_AUDITORIA"> <priority value ="INFO" /> <appender-ref ref="AUDIT" /></category>

El nombre del logger de auditoría se puede configurar en el fichero de inicialización del openFWPA:princast-init-script.xml, como se indica en el ejemplo.

<bean id="loggingManager" class="es.princast.framework.core.logging.LoggingManager" factory-method="getLogging" lazy-init="false" singleton="true"> <property name="auditingTrack"><value>MY_AUDIT_TRACK</value></property> </bean>

El nombre por defecto del logger de auditoría es: “PISTA_AUDITORIA”.

Page 85: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

77

Pista de RendimientoLa Pista de Rendimiento es un logger, muy similar a la Pista de Auditoría, que permite escribir lasmediciones de rendimiento que se realicen en la aplicación. La configuración de la Pista de Rendimiento,se realizará en el fichero log4j.xml.

<appender name="PERFORMANCE" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="example-audit.html" /> <layout class="es.princast.framework.core.logging.layouts.PrincastHTMLLayout"> </layout> </appender>

<category name="PISTA_RENDIMIENTO"> <priority value ="INFO" /> <appender-ref ref="AUDIT" /></category>

El nombre del logger de rendimiento se puede configurar en la definición del LoggingManager en elfichero de arranque princast-init-script.xml.

<bean id="loggingManager" class="es.princast.framework.core.logging.LoggingManager" factory-method="getLogging" lazy-init="false" singleton="true"> <property name="performanceTrack"><value>MY_PERFORMANCE_TRACK</value></property> </bean>

El nombre por defecto del logger de la Pista de Rendimiento es: “PISTA_RENDIMIENTO”.

Ficheros de ConfiguraciónPara la implementación de aplicaciones web, utilizando el openFWPA, es necesario definir correctamentelos ficheros web.xml y struts-config.xml. En los apartados siguientes, se describe el contenidoque éstos deberían tener.

web.xml

Como ejemplo de fichero web.xml, se tomará el incluido en la aplicación en blanco (Blank App).

El primer bloque que se encuentra es el referente a la configuración de filtros. Se define el filtro Gzip(llamado GZIPFILTER), y se indica que todas las peticiones servlet de nombre action (que es elActionServlet) pasen por dicho filtro.

<!-- filtro GZip --><filter><filter-name>GZIPFILTER</filter-name> <filter-class>es.princast.framework.web.filter.gzip.GZIPFilter</filter-class></filter><!-- Mapear el filtro GZip con el ActionServlet --><filter-mapping> <filter-name>GZIPFILTER</filter-name> <servlet-name>action</servlet-name></filter-mapping>

Otro filtro que se proporciona en la aplicación en blanco es el de control de autenticación. Para su activaciónes necesario incluir los siguientes elementos XML:

Page 86: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

78

<filter> <filter-name>SecurityFilter</filter-name> <filter-class>es.princast.framework.web.filter.security.corp.PrincastSecurityFilter</filter-class> </filter><!-- Mapeo del filtro de seguridad --><filter-mapping> <filter-name>SecurityFilter</filter-name> <servlet-name>/action/*</servlet-name></filter-mapping>

El siguiente conjunto de elementos hacen referencia al arranque e inicialización de la aplicación. Setrata de la variable de contexto que indica la ubicación del fichero de arranque (princast-init-script.xml) y el listener de inicialización:

<context-param> <param-name>INIT.SCRIPT.FILE</param-name> <param-value>/WEB-INF/princast-init-script.xml</param-value> </context-param> <listener> <listener-class>es.princast.framework.web.startup.PrincastStartupListener</listener-class> </listener>

En el siguiente bloque se configura el ActionServlet. Se le pasan como parámetros:

config nombre del fichero de configuración.

debug el nivel de detalle de debug que controla cuanta información se escribe enel log. Acepta como valores: 0 apagado, y de 1 (menos detalle) a 6 (másdetalle).

detail el nivel de detalle del Digester que se usa para procesar los ficheros deconfiguración de la aplicación. Acepta los mismos valores que el parámetrodebug.

application el nombre del fichero de recursos que contendrá los mensajes de laaplicación.

validating especifica si se debe usar un parser validador de XML para procesar elfichero de configuración.

definitions-config nombre del fichero donde se especifican las definitions de pantallas.

<servlet> <servlet-name>action</servlet-name> <servlet-class>es.princast.framework.web.action.PrincastActionServlet</servlet-class><init-param><param-name>config</param-name><param-value>/WEB-INF/struts-config.xml</param-value></init-param><init-param><param-name>debug</param-name><param-value>3</param-value></init-param><init-param>

Page 87: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

79

<param-name>detail</param-name><param-value>3</param-value></init-param><init-param><param-name>application</param-name><param-value>resources.ApplicationResources</param-value></init-param><init-param><param-name>validating</param-name><param-value>true</param-value></init-param><init-param><param-name>definitions-config</param-name><param-value>/WEB-INF/tiles-defs.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

El siguiente bloque indica al contenedor que debe redirigir cualquier petición que contenga “/action/”al ActionServlet, es decir, al servlet configurado en el bloque anterior.

<servlet-mapping><servlet-name>action</servlet-name><url-pattern>/action/*</url-pattern></servlet-mapping>

En este bloque se indica que fichero servirá por defecto la aplicación. Tiene que ser un fichero físico, noes posible indicar una acción. Si se desea, en el propio fichero se puede redirigir a una acción como sehace en la aplicación de ejemplo (Sample App).

<welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list>

A continuación se configuran las librerías de tags que utiliza la aplicación. A partir de la versión delFramework 1.5, se utiliza la versión de Struts 1.2, por lo que no se necesitan configurar las tags de Struts.

<taglib><taglib-uri>/WEB-INF/princast.tld</taglib-uri><taglib-location>/WEB-INF/princast.tld</taglib-location></taglib>

struts-config.xml

En este apartado se explicará el formato del fichero struts-config.xml incluido en la aplicaciónen blanco (Blank App). Este fichero está dividido en varios bloques: form-beans, global-exceptions, global-forwards, action-mappings, message-resources y plug-ins.

<form-beans>

Aquí se definen los beans de formulario, es decir, clases que heredan dees.princast.framework.web.action.PrincastActionForm, y que sirven para almacenar

Page 88: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

80

las propiedades introducidas en formularios enviados mediante peticiones Http. Estos formularios seránutilizados por las acciones.

<form-bean name="loginForm" type="es.princast.framework.blankPA.forms.LoginForm" />

Otro tipo de beans de formulario con lases.princast.framework.web.action.PrincastDynaActionForm. Sirven para definir unbean de formulario a través del fichero de configuración sin tener que escribir una clase para ello. Suspropiedades son visibles con métodos get y set de la misma forma que un bean convencional.

<form-bean name="detalleProductoForm" dynamic="true"type="es.princast.framework.web.action.PrincastDynaActionForm"><form-property name="detalle" type="es.princast.framework.carrito.vo.ProductoVO" /> </form-bean>

<global-exceptions>

Se utilizan para tratar excepciones que no han sido tratadas en la Action.En el siguiente ejemplo se indica que las excepciones no capturadas del tipoes.princast.framework.exceptions.PrincastException, sean reenviadas a la páginaindicada en el atributo path (en este caso se trata de una definition). El mensaje de error quese mostrará en la página de error con el tag <html:errors/>, estará en el fichero de recursos(ApplicationResources.properties) con la clave cuyo valor se indique en el atributo key.Mediante el atributo handler se especifica qué manejador de errores se utilizará para tratar la excepción.En este caso se trata del manejador por defecto de Struts (que en el método execute no realiza nada), peropodría definirse un manejador que heredase de él y sobrescribiese el método execute.

Se recomienda utilizar el método catchException() de las acciones para tratar las excepciones y norecurrir a excepciones globales.

<exception key="global.princastexception" type="es.princast.framework.exceptions.PrincastException" path="blankPA.error" handler="org.apache.struts.action.ExceptionHandler" />

<global-forwards>

Son ActionForwards (asociaciones entre nombres lógicos y URIs) disponibles para todas las acciones.Cuando una acción finaliza, devuelve un ActionForward o null. Si la acción no devuelve null, elActionServlet redirige el control al path que sea devuelto por la ActionForward.

<forward name="welcome" path="/action/login" />

<action-mappings>

En esta sección se definen los mapeos de las acciones que manejará la aplicación. Los parámetros comunesa los diferentes tipos de acciones existentes son:

path Path relativo al módulo de la acción, comenzando por el carácter /, y sinla extensión del nombre de archivo si se utiliza ésta para el mapeo (porejemplo, para un mapeo del tipo accion.do, el path sería /accion).

type Nombre de la clase, totalmente calificado, de la acción que procesará laspeticiones para este mapeo. El atributo no es válido si se especifican losatributos forward o include.

scope El contexto (request o session) que se utiliza para acceder a los beans deformulario.

Page 89: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

81

validate (def. true) Poner a true si el método validate del bean de formulario debe ser llamadoantes de llamar a este mapeo.

A continuación se comentarán de forma más detallada los distintos tipos de acciones que maneja elframework.

PrincastAction Clase abstracta de la que deben heredar todas las acciones delopenFWPA. A continuación se muestra un ejemplo de mapeo:

<action path="/login" type="es.princast.framework.carrito.actions.LoginAction" scope="request" validate="false" input="carrito.login"><forward name="success" path="/action/viewperfil" redirect="true" /><forward name="failure" path="/action/login" redirect="true" /></action>

La acción lleva anidadas dos ActionForwards a los que seredirigirá en función de que la acción se haya ejecutado con éxitoo no. La sintaxis es similar a la de las Global forwards. En elcaso de no encontrar la Forward que devuelve el método executede la acción local a la propia acción, se buscará la misma a nivelglobal. Es necesario que a ámbito local o global haya definidos dosforwards success y failure.

Una acción también puede llevar anidadas excepciones. La sintaxises la misma que la de las Global Exceptions. En el caso de notener mapeada una excepción local a la acción, se intentaría buscara nivel global.

PrincastDispatchAction Encapsula diferentes métodos de ejecución de una acción en unamisma clase. Para ello, se especifica en el atributo parameter delmapeo de la acción en el fichero struts-config.xml, elnombre del parámetro cuyo valor será el nombre del método queejecutará la acción.

El siguiente ejemplo muestra el mapeo de una acción que heredade PrincastDispatchAction. La acción ejecutará el métodocuyo nombre se le pase en el parámetro method.

<action path="/carrito" type="es.princast.framework.carrito.actions.CarritoActions" scope="request" input="carrito.viewcarrito" validate="false" parameter="method"><forward name="success" path="carrito.viewcarrito" redirect="true" /></action>

PrincastCRUDAction Hereda de la PrincastDispatchAction. Está pensada paramanejar métodos de creado, consulta, actualizado y borrado (New,Create, Retrieve, List, Update y Delete). Los posiblesnombres de los métodos a ejecutar están defini-dos como constantesen la propia clase:

• CREATE_KEY: cuyo valor es create

• RETRIEVE_KEY: cuyo valor es retrieve

• UPDATE_KEY: cuyo valor es update

• DELETE_KEY: cuyo valor es delete

• NEW_KEY: cuyo valor es new

Page 90: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

82

• LIST_KEY: cuyo valor es list

PrincastForwardAction Redirecciona a la URI relativa al contexto que se especifique enla propiedad parameter del mapeo de la acción. Un ejemplo deutilización de una acción de este tipo es el siguiente:

<action path="/welcome" parameter="carrito.login" type="es.princast.framework.web.action.PrincastForwardAction" scope="request" validate="false" />

La acción redirecciona a la definition de nombre carrito.login.

PrincastParameterAction Busca un parámetro en la request de nombre dispatch y lo usa paraobtener un ActionForward. Una vez conseguido esto, va enbusca de un segundo parámetro en la request cuyo nombre debeser especificado en la propiedad parameter del mapeo de la acción.Este valor se concatena con la URI obtenida de la propiedad pathdel ActionForward que se buscó con el valor del parámetrodispatch, y se redirecciona a la URI resultante.

PrincastExistsAttributeAction Verifica la existencia de un atributo en alguno de los ámbitosposibles (request, session o application). En lapropiedad parameter del mapeo de la acción se le indicará el ámbitoy el nombre del atributo a buscar siguiendo la siguiente sintaxis:

Parameter=”ambito;ATRIBUTO” Si se quiere buscar encualquier ámbito, se especificará el valor *. Si no se especificaalguno de los dos parámetros se produce un error.

parameter="application;HOURS"parameter="*;HOURS"

PrincastRemoveAttributeActionTrata de eliminar un atributo en alguno de los ámbitosposibles (request, session o application). Si elatributo existe devuelve el control a un ActionForwardinstanciado con Tokens.SUCCESS y sino con uno instanciadocon Tokens.FAILURE. La sintaxis es idéntica a la de laPrincastExistsAttributeAction.

<controller>

En el siguiente bloque se configura elcontrolador (RequestProcessor). Se recomienda utilizarel controaldor proporcionado por el openFWPA(PrincastTilesRequestProcessor). El uso de estecontrolador permite añadir alguna funcionalidad extra que noimplementa el controaldor por defecto (por ejemplo, múltiples“input” por action, forwards a entradas de menú, etc.)

<controller processorClass="es.princast.framework.web.action.PrincastTilesRequestProcessor" />

El uso de este RequestProcessor limita los posiblesmapeos que se pueden hacer sobre el ActionServlet (ver acontinuación). Las Actions únicamente se pueden mapear a un pathdel tipo: “/path/*”. No se permiten mapeos del tipo: “/path/*.do”.

<message-resources>

Page 91: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

83

Indica que fichero contiene los mensajes quemostrará la aplicación. En este caso se tratadel ApplicationResources.properties del paqueteresources.

<message-resources parameter="resources.ApplicationResources" />

<plug-in>

Aquí se indica que plug-ins va a utilizar la aplicación. En primerlugar se indica que se utilizará el validador de Struts, y que las reglasde validación se encuentran en validator-rules.xml y losmapeos entre los formularios y las reglas en validation.xml.

<plug-in className="org.apache.struts.validator.ValidatorPlugIn"><set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml" /></plug-in>

A continuación se indica que se utilizara tiles, y que las definitionsse describirán en el fichero tiles-defs.xml.

<plug-in className="org.apache.struts.tiles.TilesPlugin"><set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /><set-property property="moduleAware" value="true" /><set-property property="definitions-parser-validate" value="true" /></plug-in>

Y por último se indica que se utilizara el Struts Menu [16] y que suconfiguración residirá en el fichero menu-config.xml.

<plug-in className="net.sf.navigator.menu.MenuPlugIn"><set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/></plug-in>

Filtros webCon vistas a poder acceder de forma sencilla a las peticiones y respuestas HTTP que circulan entre elnavegador del cliente y el servidor Web se proporciona una arquitectura de filtros extensible de formasencilla.

Filtros del openFWPA. PrincastFilter.Todos los filtros (Servlet Filters) proporcionados por el openFWPA extienden del filtro base:PrincastFilter. Este filtro extiende la jerarquía básica de Servlet Filters proporcionada porla plataforma J2EE y ofrece algunas funcionalidades extra.

En caso de que las aplicaciones vayan a implementar sus propios filtros, es recomendable siempre extenderla clase PrincastFilter.

Implementación de un nuevo filtro

El proceso de implementación de un nuevo filtro que haga uso de la infraestructura facilitada por elopenFWPA es muy sencillo. A continuación se detallan los pasos a seguir:

Page 92: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

84

1. Crear una clase Java que extienda la clase PrincastFilter.

2. Sobrescribir el método filter() para añadir la lógica de negocio del filtro.

Atención

El método que se debe sobreescribir es filter() y no doFilter() como se haría paraescribir un filtro normal. El método doFilter() es final en la clase PrincastFilter.

3. Crear un interfaz java que extienda el interfaz PrincastFilterMBean. Este interfaz debe tenercomo nombre el de la clase Java que define el nuevo filtro seguido de MBean. Esto es necesario parapoder hacer uso de las facilidades JMX [19] aportadas por el openFWPA para la gestión de los filtros.No es necesario que el nuevo interfaz tenga métodos.

4. Si se quiere implementar alguna lógica al inicializar el filtro, se debe extender el métodoinitFilter().

Atención

A partir de la versión 1.5, el método init() de la clase PrincastFilter es final, por lotanto ya no se puede extender dicho método.

Gestión de los filtros de forma dinámica

Una de las características más novedosas aportadas por el openFWPA en cuanto a la gestión de losfiltros es que estos pueden ser conectados y desconectados “en caliente” utilizando para ello una consolaJMX. De esta forma, el administrador de la aplicación, en base a determinadas métricas de rendimiento,puede decidir en un momento dado si desea tener un filtro que comprima la respuesta enviada al cliente,arrancándolo o apagándolo sin necesidad de tener que para la aplicación para aplicar la nueva configuraciónen el fichero web.xml.

Filtros activables por reglas

A partir de la versión 1.4 del openFWPA, los filtros que extienden PrincastFilter pueden serdesactivados para un conjunto determinado de URLs.

Este tipo de filtros se pueden mapear sobre un patrón de URL (como cualquier otro filtro) pero es posibledefinir un subconjunto de patrones de URL, para las cuales el filtro no entrará en acción.

Cada uno de los patrones de URL excluidos de la acción del filtro se definirá utilizando un “initparam”, en el fichero web.xml. Estos parámetros deben cumplir el siguiente convenio de nombrado:“EXCLUDED.URL.<Identificador descriptivo del patrón>”.

<filter> <filter-name>ExampleByRuleFilter</filter-name> <filter-class> es.princast.example.web.filter.ExampleFilter </filter-class> <init-param> <param-name>EXCLUDED.URL.SELECCION</param-name> <param-value> /action/seleccionaExplotacion, </param-value> </init-param> <init-param>

Page 93: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

85

<param-name>EXCLUDED.URL.LOGOUT </param-name> <param-value> /action/logout </param-value> </init-param> </filter>

Además, utilizando la consola JMX, es posible actualizar, “en caliente”, los patrones de URLs que seexcluirán del efecto del filtro.

Configuración del filtro GZIPFilteropenFWPA tiene integrado un filtro (GZIPFilter) que se encarga de comprimir la respuesta usandocompresión gzip siempre que está esté soportada por el navegador.

Para poder hacer uso del filtro que viene integrado con el openFWPA es necesario definirlo en el ficherode configuración web.xml. Un ejemplo de la configuración a añadir en este fichero se presenta acontinuación:

<!-- filtro GZip --> <filter> <filter-name>GZIPFILTER</filter-name> <filter-class>es.princast.framework.web.filter.GZIPFilter</filter-class> </filter>

<!-- Mapear el filtro GZip con el ActionServlet --> <filter-mapping> <filter-name>GZIPFILTER</filter-name> <servlet-name>carrito</servlet-name> </filter-mapping>

Configuración del filtro SecurityFilterPara el control de acceso de las aplicaciones que usen el openFWPA se facilita un filtro al efecto. Parapoder hacer uso de este filtro es necesario definirlo en el fichero de configuración web.xml. Un ejemplode la configuración a añadir en este fichero se presenta a continuación:

<filter> <filter-name>SecurityFilter</filter-name> <filter-class> es.princast.framework.web.filter.security.corp.PrincastSecurityFilter </filter-class> </filter>

El filtro de seguridad se enmarca dentro del sistema de seguridad del openFWPA. Para mas información,refiérase al apartado: “Seguridad”, de este mismo documento.

Filtro de navegaciónEl Filtro de Navegación (clase NavigationFilter) tiene dos objetivos:

1. Mantener siempre el conocimiento de las entradas de menú (y submenu) activas, evitando la necesidadde utilizar tecnologías del lado del cliente como JavaScript y Cookies.

Page 94: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

86

2. Mantener el estado de la barra de navegación (Ver ???).

<filter> <filter-name>NavigationFilter</filter-name> <filter-class>es.princast.framework.web.filter.navigation.NavigationFilter</filter-class> </filter><filter-mapping> <filter-name>NavigationFilter</filter-name> <url-pattern>/action/*</url-pattern> </filter-mapping>

El Filtro de Navegación debe estar mapeado a todas las URLs referenciadas por alguna entrada de menúo submenu.

El Filtro de Navegación, extrae de la Request los valores de los parámetros GET que indican la entradade menú y submenu activas y, a partir de ellas, se encarga de mantener el estado del menú y de la barrade navegación, manteniendolos siempre sincronizados.

Para conocer los nombres de los parámetros GET a los que tiene que acceder, el Filtro de Navegaciónimplementa el interface ConfigurationListener (es decir, es un objeto configurable por el sistemade configuración del openFWPA) que escucha el contexto del menú (MENU.CONTEXT), lo que significa,que no se requiere ninguna configuración especial para el Filtro de Navegación, siempre y cuando, seconfigure convenientemente el menú.

Filtro de activaciónEl Filtro de Activación (clase AppActivationFilter) permite definir, cuando una aplicación estáactiva y cuando no. Este filtro tiene dos funciones:

1. Implementar lógica para activar/desactivar las aplicaciones en función de la fecha actual.

2. Servir como clase base para la implementación de otros filtros que permitan determinar nuevascondiciones para la activación/desactivación de aplicaciones.

Activación de aplicaciones en función de fechas

El Filtro de Activación permite determinar cuando, en función de la fecha actual, y de unos determiandosintervalos de actividad, está activa la aplicación. Si la fecha actual no encaja en ninguno de los intervalosdeterminados, se redireccionará a una URL de advertencia y no se dejará entrar a la aplicación.

El filtro acepta los siguientes parámetros de inicialización (init-params en web.xml):

CONTEXT Nombre del contexto de configuración que se utilzará para especificar los intervalos deactividad de la aplicación. Por defecto vale: ACTIVATION.FILTER.

DATE.FORMAT Formato (aceptado por SimpleDateFormat) que se utiliza para especificar elformato de fechas de los intervalos. Por defecto vale: "dd/MM/yyyy HH:mm:ss".

ERROR.URL URL a la que se redirigirá en caso de no estar activa la aplicación para la fecha actual.Este atributo es obligatorio.

Los intervalos de actividad de la aplicación se definirán en el contexto de configuración (ver Sistemade Configuración del openFWPA) cuyo nombre se indica en el parámetro de inicialización del filtro denombre: CONTEXT (por defecto: ACTIVATION.FILTER).

El nombre de cada parámetro de configuración para estos intervalos debe seguir el patrón:ACTIVE.INTERVAL.X. Donde X es un identificador que diferencia a cada intervalo del resto.

Page 95: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

87

Los intervalos se definen utilizando dos fechas separadas por el caracter "-". El formato de las fechas sedefine con el parámetro de inicialización: DATE.FORMAT.

Si el extremo de un intervalo está indeterminado, se puede utilizar el caracter "?".

Por ejemplo:

ACTIVE.INTERVAL.1: 22/10/2005 11:11:11 - 22/10/2006 11:11:15ACTIVE.INTERVAL.2: 22/10/2005 11:11:11 - ?ACTIVE.INTERVAL.3: ? - 22/10/2006 11:11:15ACTIVE.INTERVAL.4: ?- ?

Extensión del Filtro de Activación

Es posible extender el Filtro de Activación para definir nuevas condiciones de activación y/o un nuevotratamiento para el caso en que la aplicación no esté activa. Basta con extender los métodos:

isActive() Determina si la aplicación está activa. Recibe como parámetro la request.

handleInactive() Se encarga de realizar la lógica adecuada en caso de que la aplicación no estéactiva.

Filtros de ActivaciónEn ocasiones, es necesario poder activar (o desactivar) una aplicación o determinadas partes de la misma,de forma declarativa. El openFWPA, desde la versión 1.5, incluye una clase base (ActivacionFilter)que facilita la implementación de filtros web para la gestión de la activación de las aplicaciones.

Básicamente este tipo de filtros tienen dos responsabilidades: a) determinar cuando la aplicación está activa(o no) y b) en caso de que la aplicación esté inactiva ejecutar alguna operación (generalmente redireccionara una página de aviso).

Para implementar filtros de activación es necesario extender la clase ActivationFilter. Los métodosmás relevantes de esta clase son:

isActive() Determina cuando el recurso solicitado (URL) está activo. Este métododevolverá true si el recurso está activo. Se trata de un método abstracto y, portanto, es de implementación obligatoria por parte de las subclases.

handleInactive() Realiza la lógica correspondiente en caso de que el recurso solicitado por elcliente no esté activo. La implementación por defecto redireccionará a unapágina previamente configurada.

configure() Los filtros de activación pueden ser configurados mediante el Sistema deConfiguración de openFWPA. Por defecto, todos los filtros de activación tomanparámetros del contexto "ACTIVATION.FILTER". Es posible cambiar elnombre del contexto utilizando el parámetro de inicialización del filtro (init-param en web.xml) de nombre: "CONTEXT".

Dentro de este contexto, se puede definir una página de redirección (en caso deque el recurso no esté activo), utilizando el parámetro "ERROR.URL".

Filtro de Temporalidad

El Filtro de Temporalidad permite activar/desactivar una aplicación (o partes de la misma) en función dela fecha en la que se produzca el acceso.

Page 96: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

88

El Filtro de Temporalidad (clase TimingFilter) es un filtro de activación y, por tanto, tiene todas suscaracterísticas.

El Filtro de Temporalidad permite que los recursos que protege estén activos únicamente durantedeterminados intervalos temporales, definidos como parámetros de configuración. Estos parámetrosseguirán el siguiente convenio de nombrado: ACTIVE.INTERVAL.<id del intervalo>. El valordel intervalo, se especificará siguiendo el convenio: <inicio intervalo> - <fin intervalo>,pudiendo especificarse el inicio o el fin del intervalo de las siguientes formas:

a. Con una fecha, especificada según cualquier formato estándar válido (SimpleDateFormat). Elformato concreto a utilizar se define con el parámetro de configuración: DATE.FORMAT. Por defectovale: ."dd/MM/yyyy HH:mm:ss".

b. Utilizando el caracter "?". En este caso, se supone que se trata de un intervalo abierto, cuyo extremoestá inespecificado.

Algunos ejemplos de intervalos pueden ser:

ACTIVE.INTERVAL.1 = 22/10/2002 11:11:11 - 22/10/2004 11:11:15ACTIVE.INTERVAL.2 = 22/01/2005 11:11:11 - ?ACTIVE.INTERVAL.3 = ? - 22/10/2106 11:11:15ACTIVE.INTERVAL.4 = ? - ?

Consola de gestiónLos componentes del openFWPA, están diseñados para que puedan ser monitorizados y gestionados, entiempo de ejecución, utilizando una consola de gestión. A través de la consola de gestión, el administradorde sistemas puede arrancar, detener y reconfigurar “en caliente” los componentes del framework queestime conveniente.

Para la implementación de la consola de administración se utiliza el API JMX (Java ManagementExtensions). Este API permite la gestión y configuración de objetos de la aplicación.

El sistema de gestión JMX del openFWPA se configura utilizando el contexto de configuración de nombreJMX.CONTEXT. Los parámetros necesarios para activar la consola de gestión son los siguientes:

MBEAN.SERVER.NAME Nombre del servidor JMX. Por defecto, su valor es “frameworkpa”. Cadaaplicación debería poner su propio nombre como nombre de servidor.

JMX.SERVER.ADAPTOR Existen varias implementaciones del estándar JMX y no todas soncompletamente compatibles entre sí. Cada implementación tiene susparticularidades (criterio de nombrado de MBeans, consola HTMLintegrada, etc.). Por esta razón, es necesario incluir una capa de abstracciónque permita a las aplicaciones, sin necesidad de recompilar, ejecutarsecorrectamente con cualquier implementación de JMX. Esta capa deabstracción es un “adaptador” para el servidor de MBeans. En función de laimplementación JMX del entorno de ejecución, se debe utilizar un adaptadoru otro. En el parámetro de configuración JMX.SERVER.ADAPTOR, seindicará el nombre completamente cualificado de la clase que actuará comoadaptador para el servidor JMX. Los posibles valores son:

• es.princast.framework.core.management.adapters.NullMBeanServerAdapter.Para no utilizar ningún servidor. La consola de administración JMXaparecerá desconectada.

Page 97: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Operaciones

89

• es.princast.framework.core.management.adapters.JMXRIMBeanServerAdapter.Para conectar con la implementación JMX de referencia (JMX-RI). Esteadaptador se debe utilizar con el contenedor OC4J 9.0.3. Si se va a utilizareste adaptador, se deben definir, además, los siguientes parámetros deconfiguración.

• JMX.HTTP.PORT. Puerto donde escuchará la consola HTML.

• JMX.HTTP.USERNAME. Nombre de usuario válido para acceder a laconsola.

• JMX.HTTP.PASSWORD. Contraseña válida para acceder a la consola.

• es.princast.framework.core.management.adapters.OC4JMBeanServerAdapter.Para conectar con la implementación incluida en el servidor OC4J 10g. Sise utiliza este adaptador, todo el sistema de operación de las aplicacionesestará integrado en la consola de administración del contenedor.

Para conocer más detalles a cerca de la consola de gestión, consultar el Manual de Operaciones.

Page 98: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

90

Capítulo 7. Seguridad en aplicacionescon openFWPASeguridad

En el apartado Seguridad se contemplan todas las políticas y herramientas que permiten, tanto controlar(y monitorizar) el acceso a determinadas partes de una aplicación, como aquellas que permiten garantizarla integridad y confidencialidad de los datos que se transmiten.

Los conceptos más importantes dentro del control de acceso a una aplicación son la Autentificación y laAutorización.

La Autentificación es el proceso mediante el cual los privilegios de acceso de un usuario son comprobadosantes de que pueda tener acceso a un área protegida de la aplicación. Dentro de la autentificación se puedendistinguir diferentes alternativas, siendo las más recurridas la básica (Basic authentication) y la basada enformulario (Form-based authentication).

Autentificación básicaLa autentificación básica delega el control de acceso al servidor Web. El usuario podrá navegar librementepor el sitio Web sin necesidad de facilitar una contraseña. Sin embargo, cuando se intente acceder a unapágina protegida será el propio navegador el que solicite al usuario un nombre de usuario (username) yuna contraseña (password) mostrándole una ventana de dialogo.

Tanto el nombre de usuario como la contraseña son enviados sin encriptar al servidor Web, quien seencarga de validar-los contra un fichero plano, un base de datos o servidor de directorios.

Si el usuario consigue validarse se comprueba que se tenga privilegio suficiente para acceder al recursobasándose en ciertas políticas definidas, por ejemplo, en un fichero tipo http.conf. Si la comprobaciónes positiva se sirve la página al cliente. En caso contrario, se le solicita de nuevo la combinación usuario/contraseña o se le muestra una página de error denegando el acceso.

Autentificación basada en formularioSuele ser la alternativa más usada por los sitios Web dada su sencillez. Al igual que en la autentificaciónbásica, el usuario puede navegar libremente por los recursos desprotegidos. En el momento que se intentaacceder a un área protegida se redirecciona al usuario a una página de login con un formulario con loscampos nombre de usuario y contraseña.

Para distinguir qué áreas están protegidas y cuáles no se emplean patrones de URLs. Dado que lasaplicaciones desarrolladas sobre el openFWPA deben correr en el servidor de aplicaciones OC4J [7] elcontrol de acceso se deja en manos de un proveedor de seguridad que viene integrado con el servidorllamado XMLUserManager. Esta integración está implementada como un filtro de URLs y se integracon las APIs Java de Seguridad (JAAS).

Autentificación basada en el Filtro de Seguridad delopenFWPA

La autentificación basada en el Filtro de Seguridad, es la opción estándar propuesta por openFWPA parael control de acceso. Este tipo de autenticación se basa en un Servlet Filter que se encarga degestionar tanto el control de acceso como los protocolos de seguridad (http/https) a utilizar.

Page 99: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

91

Figura 7.1. Esquema del sistema de autenticación

Como resultado del proceso de autenticación y autorización, el Filtro de Seguridad del openFWPA, ponea disposición de las aplicaciones todos los datos obtenidos en dicho proceso.

Para utilizar la autenticación del openFWPA , se tendrán que tener en cuenta los siguientes puntos:

1. Configuración del Contenedor. El contenedor debe estar configurado convenientemente para permitirel acceso a las aplicaciones bajo el protocolo HTTPS y utilizando sockets SSL-3 (https con certificadodigital).

2. Uso de la seguridad web j2ee estándar. En la clase HttpServletRequest, se puede interrogar porlas credenciales del usuario para realizar la autenticación.

3. Configuración. Es necesario definir los plugins de configuración adecuados.

4. Paso de credenciales. Usando una extensión de la clase Principal, donde se proporcionan a laaplicación todos los parámetros que se recogieron durante el proceso de autenticación.

5. Diseño de páginas de login. Las aplicaciones necesitan mostrar a los usuarios páginas que lespermitan introducir todos los datos necesarios para su autentificación. Estas páginas deben tener unascaracterísticas determinadas para poder integrarse con la seguridad del openFWPA.

Integración con seguridad web J2EE

En la clase javax.servlet.http.HttpServletRequest hay tres métodos para controlar elacceso a recursos de la aplicación. Los siguientes párrafos se han extraído de la documentación de laversión 1.3 (JDK 1.3):

java.lang.String getRemoteUser() Returns the login of the user making this request, if the user has been authenticated, or null if the user has not been authenticated. java.security.Principal getUserPrincipal() Returns a java.security.Principal object containing the name of the current authenticated user. boolean isUserInRole(java.lang.String role) Returns a boolean indicating whether the authenticated user is included in the specified logical "role".

El método getRemoteUser devuelve el nombre del usuario que realiza la HttpRequest. Esequivalente a ((PrincastIdentifier)getUserPrincipal()).getId()(ver abajo).

El método getUserPrincipal devuelve el Principal más importante (DNI) – en este caso,como una instancia de la clase PrincastIdentifier. Soporta el método getName() (definido enPrincipal) y getId() (definido en PrincastIdentifier). getName() devuelve la cadena “DNI”y getId() devuelve el DNIdel usuario.

Para la gestión de roles (isUserInRole()) se emplean los roles asignados en el esquema de empleadopúblico. Los roles son tanto para ciudadanos como para empleados públicos.

Configuración

El sistema de seguridad estándar se configura utilizando el Sistema de Configuración del openFWPA.Todo el sistema tomará parámetros del contexto de seguridad llamado “SECURITY”, por lo tanto, esimportante recordar que cualquier plugin que sirve parámetros al sistema de seguridad debe registrarsepara el contexto “SECURITY”.

Page 100: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

92

Parámetros básicos

Los parámetros básicos son:

app-config Bajo este parámetro debe indicarse el nombre de la aplicación. Este es el nombrede la configuración JAAS que se utilizará para realizar la autenticación.

http.PORT Puerto http que se utiliza para acceder a la aplicación. Si no se especifica esteparámetro, se utilizará el puerto por defecto (80).

https.PORT Puerto https que se utiliza para acceder a la aplicación. Si no se especifica esteparámetro se utilizará el puerto SSL (443) por defecto.

https/cert.PORT Puerto https (SSL-3) con certificado digital de cliente, que se utiliza para accedera la aplicación. Si no se especifica este parámetro se utilizará el puerto SSL (443)por defecto.

http.IP Dirección IP (o nombre DNS) del servidor que atiende peticiones http. Si nose especifica este parámetro, la redirección se realizará sobre la IP del propiocontenedor.

https.IP Dirección IP (o nombre DNS) del servidor que atiende peticiones https (SSL v2),con certificado de servidor. Si no se especifica este parámetro, la redirección serealizará sobre la IP del propio contenedor.

https/cert.IP Dirección IP (o nombre DNS) del servidor que atiende peticiones https (SSL v3),con certificado de cliente. Si no se especifica este parámetro, la redirección serealizará sobre la IP del propio contenedor.

Por ejemplo:

<bean id="baseConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesFileConfigurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> <property name="file"><value>ejemplo.properties</value></property> <property name="contexts"> <list> <value>SECURITY</value> <value>ROOT.CONTEXT</value> <value>ACTION.CONTEXT</value> <value>JMX.CONTEXT</value> </list> </property> </bean>

Siendo el contenido del fichero ejemplo.properties, el que sigue:

##Fri Jan 07 10:08:36 CET 2005HIT.COUNTER=es.princast.framework.core.management.mcounters.historic.HistoricalCounterACTION_MGMT=es.princast.framework.web.action.monitoring.PrincastActionMgmtInterfaceImplLOGGING_XMLCONF=/WEB-INF/log4j.xmlapp-config=EjemploApphttp.PORT=8888https.PORT=4443https/cert.PORT=8844https/cert.IP=192.168.7.7JMX.SERVER.ADAPTOR = es.princast.framework.core.management.adapters.OC4JMBeanServerAdapter

Page 101: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

93

Configuración JAAS

El sistema de autenticación del openFWPA está basada en el estándar JAAS (Java Authentication andAuthorization Service). En realidad, el Filtro de Seguridad, no realiza ninguna tarea de autenticación.Estas funciones son delegadas en un Módulo de Login JAAS (LoginModule). La configuración delmódulo a utilizar para autenticar y autorizar usuarios se realiza a través del plug-in de configuración JAAS(JAASConfigurationPlugin), que debe ser declarado en el fichero de inicialización princast-init-script.xml. Por ejemplo:

<bean id="jaasConfigPlugin" class="es.princast.framework.facilities.security.jaas.config.JAASConfigurationPlugin"> <constructor-arg><value>jaas-config</value></constructor-arg> <property name="file"><value>WEB-INF/jaas-config.xml</value></property> <property name="contexts"> <list> <value>SECURITY</value> </list> </property> </bean>

Los módulos JAAS se configuran, en el openFWPA, mediante el fichero jaas-config.xml:

<!DOCTYPE jaas PUBLIC "-//Framework PA - Team//DTD JAAS Configuration 1.3F//ES" "jaas-config.dtd"><jaas> <application name="Carrito" controlFlag="required"> <module>es.princast.framework.modules.security.standalone.StandaloneLoginModule</module> <options> <option> <name>USERS.FILE</name> <value>/WEB-INF/authorized-users.xml</value> </option> </options> </application></jaas>

En este fichero se definen los módulos de configuración a utilizar. Cada módulo se define con la etiqueta<application ..>. Cuando se vaya a autenticar a un usuario, se utilizará el módulo cuyo valor delatributo “name” coincida con el parámetro app-config (de los parámetros básicos de autenticación,ver sección anterior).

Eventualmente, el atributo “controlFlag” está desactivado. No se tiene en consideración el valorespecificado para realizar la autenticación.

Para cada módulo, es obligatorio indicar el nombre de la clase que implementa la lógica de autenticación,utilizando la etiqueta anidada: <module>. Esta clase debe implementar el interface: LoginModule,definido en el paquete JAAS. Por último, en la etiqueta <options> se pueden definir todas las opcionesde configuración específicas del módulo JAAS que se vaya a utilizar.

Reglas de Seguridad

Por último, es necesario configurar las reglas de seguridad que definirán que recursos de la aplicaciónestán protegidos, y de que forma.

Un recurso sólo puede estar protegido una vez. Si una URL encaja en varios patrones definidos en elfichero de reglas de seguridad, se tendrá en cuenta el patrón más restrictivo.

Page 102: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

94

Para configurar las reglas de seguridad, se debe definir, en el fichero princast-init-script.xml,un plug-in de configuración de tipo: SecurityRulesConfigurationPlugin. Por ejemplo:

<bean id="securityRulesPlugin" class="es.princast.framework.web.filter.security.corp.conf.SecurityRulesConfigurationPlugin"> <constructor-arg><value>security-rules</value></constructor-arg> <property name="file"><value>WEB-INF/princast-security-rules.xml</value></property> <property name="contexts"> <list> <value>SECURITY</value> </list> </property> </bean>

Las reglas de seguridad se definirán, por su lado, en el fichero princast-security-rules.xml.

<!DOCTYPE resources PUBLIC "-//Framework PA - Team//DTD Security Rules Configuration 1.3F//ES" "princast-security-rules.dtd">

<resources> <!-- Acceso para ciudadanos --> <resource actor="CITIZEN" level="0" protocol=”https”> <url-pattern>/*</url-pattern> </resource> <resource actor="CITIZEN" level="1"> <url-pattern>/action/*</url-pattern> <forwards> <forward name="login" path="/pages/login.jsp"/> <forward name="no-login" path="/pages/login.jsp"/> <forward name="no-roles" path="/pages/login.jsp"/> <forward name="error" path="/pages/login.jsp"/> </forwards> <roles> <role>EXAMPLE.ROLE.1</role> <role>EXAMPLE.ROLE.2</role> </roles> <options> <option> <option-name>example option</option-name> <option-value>example value</option-value> </option> <options> </resource></resources>

En este fichero, cada etiqueta <resource> define un recurso protegido. Un recurso tiene tres atributos:actor, nivel de seguridad y protocolo.

El atributo “actor” define el usuario-tipo que va a acceder al recurso. En función de este valor, el procesode autenticación puede ser diferente. La semántica exacta de este atributo la establece el módulo JAASque realice la autenticación.

El atributo “level”, define el nivel de protección establecido para el recurso. Generalmente, hay 3: nivel0 para recursos sin protección, nivel 1 para recursos protegidos bajo par usuario/contraseña y nivel 2 pararecursos protegidos con certificado digital.

Page 103: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

95

Generalmente, un nivel de seguridad permite el login por los mecanismos para sí definidos y tambiénmediante los definidos para niveles superiores. Por ejemplo, un recurso protegido bajo usuario/ contraseñatambién será accesible con certificado digital. La semántica exacta del nivel de seguridad, la define elmódulo JAAS.

El protocolo (atributo “protocol”) define el tipo de acceso deseado al recurso. Puede tomar dos valores:“http” si no se quiere ninguna encriptación del recurso o “https” si se desea garantizar la privacidad delos datos transmitidos entre el cliente y la aplicación. Este atributo puede omitirse, en tal caso, se permiteel acceso al recurso bajo cualquier protocolo.

Un recurso protegido es, exactamente, un patrón URL que se aplica sobre el espacio de direcciones de laaplicación. Este patrón URL se define en la etiqueta anidada <url-pattern>. Si, en un mismo ficherode reglas, varios patrones, de varios recursos, coinciden, el recurso que se utiliza para autenticar es el quedefina el patrón más restrictivo de todos.

Cuando se produce un error autenticando al usuario, en función de la naturaleza de dicho error, el Filtrode Seguridad, actuará redireccionando a un path determinado. Por ejemplo, si no se puede autenticar alusuario porque la contraseña es incorrecta, se le redirigirá a una página donde se muestra el formulario deentrada. Sin embargo, si no se puede autenticar al usuario porque no tiene privilegios suficientes (roles)para acceder a un recurso, se le puede redireccionar a otra página distinta informándole de dicha situación.

Las redirecciones se especifican bajo la etiqueta “<forwards>”. Cada redirección tendrá un nombre quela identifica (“name”) y una url a la que se redirigirá al usuario (“path”).

Los nombres de forwards válidos son:

login Se redirige a login la primera vez que un usuario trata de acceder a un recurso protegido.

no-login Esta redirección se ejecuta cuando no se puede autenticar al usuario por cualquier motivo(por ejemplo, contraseña equivocada)

no-roles Se redirige a este forward cuando el usuario, que está correctamente autenticado, no tieneautorización para acceder al recurso.

error Esta redirección está reservada a situaciones de autenticación no controladas por losforwards definidos para el recurso. Si no se define forward de error, cuando se de un fallode autenticación, se devolverá un error http 515.

En cada recurso también se definirán los nombres de los roles que tienen acceso. Si no se incluye laetiqueta <roles> , no se realizará chequeo de roles.

Por último, se pueden definir las opciones de configuración (<options>) que se estimen convenientespara el recurso. La semántica de estas opciones depende exclusivamente del módulo JAAS que realizala operación de login.

Paso de Credenciales

Durante el proceso de autenticación, se recaba una serie de datos del usuario. Estos datos seguardan en la sesión http en un objeto de tipo javax.security.auth.Subject, bajo la claveSecurityGlobals.SUBJECT.

Este objeto tiene los siguientes métodos:

1. java.util.Set getPrincipals(). Devuelve un Set con los Principals (datos deusuario) conocidos durante el proceso de autenticación.

Page 104: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

96

2. java.util.Set getPrincipals(PrincastIdentifier.class). Devuelve un Set conel Principal más importante, el DNI. Es equivalente a request.getUserPrincipal().

3. java.util.Set getPrincipals(PrincastCompositePrincipal.class). Devuelveun Set con los Principal secundarios, de tipo java.util.Properties. Este objeto contienetodas las propiedades capturadas en cada escenario en concreto, que son específicas a él (por ejemplo,puede haber o no un IDTercero en función del escenario).

4. java.util.Set getPrincipals(Group.class). Devuelve un Set con todos los roles quesoporta un usuario.

5. java.util.Set getPrincipals(ScenarioPrincipal.class). Devuelve un Set quecontiene el Principal con los datos del escenario. Este Principal es un mapa que contienelos parámetros que definen el escenario bajo el que se realizó el proceso de autenticación (nivel deseguridad, canal de acceso, tipo de actor, etc.)

Recuperación de credenciales de usuario

Una vez que el usuario ha sido autenticado, sus credenciales son almacenadas en su sesión bajo la claveSecurityGlobals.SUBJECT. Éste es un objeto de tipo javax.security.auth.Subject.

Para recuperar las diferentes credenciales almacenadas en este objeto basta con emplear las diferentesconstantes definidas en la clase SecurityConstants como parámetros para las llamadas al métodogetPrincipal() de la clase Principals contenida en el objeto Subject .

Atención

No es recomendable obtener los datos de autenticación (j_username y j_password)directamente de los parámetros de la request. No se garantiza que estos datos vayan a ser loscorrectos. Además, desde la versión 1.5, cualquier parámetro que se utilice como contraseña seráenmascarado (reemplazado por la cadena "*********") por el filtro de autenticación.

A continuación se muestran diferentes ejemplos de recuperación de credenciales.

Recuperación del identificador de tercero de un usuario

Subject subject = (Subject)request.getSession(true).getAttribute(SecurityGlobals.SUBJECT);Set data = subject.getPrincipals(PrincastCompositePrincipal.class);PrincastCompositePrincipal principals = (PrincastCompositePrincipal) data.iterator().next();String idTercero = principals.getPrincipal(SecurityConstants.ID_THIRD_PARTY);

Recuperación de las Unidades Organizativas asociadas a un usuario

...

PrincastCompositePrincipal principal = //Obtener el principal normalemente...Map uniOrgs = new HashMap();

String id = principal.getPrincipal(SecurityConstants.ORGANIZATIONAL_UNIT_ID);String name = principal.getPrincipal(SecurityConstants.ORGANIZATIONAL_UNIT_NAME);

if (id != null) { uniOrgs.put(id, name);}

Page 105: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

97

int index = 1;do {

id = princpal.getPrincipal(SecurityConstants.ORGANIZATIONAL_UNIT_ID+"_"+index); name = princpal.getPrincipal(SecurityConstants.ORGANIZATIONAL_UNIT_NAME+"_"+index); if (id != null) { uniOrgs.put(id, name); }

} while (id != null);

...

Recuperación de los dominios de usuario.

Subject subject = (Subject) request.getSession(true).getAttribute( SecurityGlobals.SUBJECT); Set principals = subject.getPrincipals(PrincastRole.class); Iterator it = principals.iterator();

while (it.hasNext()) { PrincastRole pr = (PrincastRole) it.next(); Iterator itDom = pr.getDomains().iterator(); while (itDom.hasNext()) { PrincastDomain pd = (PrincastDomain) itDom.next(); if (!pd.isProcedure()) { logger.info("Nombre del dominio: " + pd.getName()) } } }

Diseño de páginas de login

En las aplicaciones que utilicen el Filtro de Seguridad del openFWPA, deben escribirse páginas JSP parala obtención de los datos de autentificación. Estas páginas varían en función del tipo de autenticación quese quiera realizar.

A partir de la versión 1.4 del openFWPA se dispone de un nuevo diseño para las páginas de login enaplicaciones de tramitación (no de portal).

Con el fin de facilitar el diseño e implementación de estas páginas, se ha incluido una etiqueta,<ui:login>, en el fichero princast-ui.tld, así como un conjunto de clases CSS definidas en elfichero login.css.

Los atributos permitidos por la etiqueta esta descritos en la ???

El filtro de autenticación deja, en el scope session, la URL del recurso protegido solicitado, al que se estabaintentando acceder. El nombre del atributo bajo el que se almacena esta URL está definido por la constante:SecurityGlobals.REQUESTED_URL. Este atributo se puede utilizar para crear formularios de loginsin utilizar la etiqueta <ui:login>. Por ejemplo, como sigue:

Page 106: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

98

<form action="<%=session.getAttribute(SecurityGlobals.REQUESTED_URL)%>" method="post"> //Añadir aquí los inputs necesarios para leer el username, contraseña, etc…</form>

Login de empleado público

En las páginas de login para el escenario de empleado público (tipo de actor: EMPLOYEE), deben incluise,al menos, un campo de texto para obtener el nombre de usuario del empleado (j_username, por defecto)y un campo de tipo “password” para leer su contraseña (j_password por defecto).

<?xml version="1.0" encoding="iso-8859-1"?>

<!--No se puede poner DTD. Si se pone DTD no funciona.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">-->

<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %><%@ taglib uri="/WEB-INF/princast.tld" prefix="princast" %><%@ page import="es.princast.framework.web.filter.security.common.SecurityGlobals" %>

<html:html locale="es" xhtml="true" > <head> <html:base /> <title> Bienvenido al Principado de Asturias </title> <link type="text/css" rel="stylesheet" href="css/general.css" /> <link type="text/css" rel="stylesheet" href="css/components.css" /> <link type="text/css" rel="stylesheet" href="css/login.css" /> </head>

<body onload="document.forms[0].j_username.focus()">

<princast:login> <princast:panel layouterClass="es.princast.framework.web.view.layout.DivLayouter"> <princast:panel-legend>Login</princast:panel-legend> <princast:block cssClass="login_message"> <p>Bienvenido a la aplicación de ejemplo SampleApp</p> </princast:block> <princast:block cssClass="login_inputs"> <label for="j_username">Usuario:</label> <input type="text" name="j_username" id="j_username" /> </princast:block> <princast:block cssClass="login_inputs"> <label for="j_password">Contraseña:</label> <input type="password" name="j_password" id="j_password" /> </princast:block> <princast:block cssClass="login_submit"> <p> <input type="image" src="images/botones/enter-button.gif" alt="Entrar"/> <input type="image" src="images/botones/right-arrow.gif" alt="Entrar"/> </p>

Page 107: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

99

</princast:block> </princast:panel> <princast:login-footer> Por favor, si no va a utilizar la aplicación cierre la sesion para evitar que otros entren con su perfil </princast:login-footer> </princast:login>

</body></html:html>

Login de ciudadano a través del portal.

Para el control de acceso de ciudadanos (tipo de actor: CITIZEN), con nivel de seguridad 1 (usuario /contraseña), se requieren los siguientes campos:

• Input de tipo “text” para recoger el identificador de usuario (NIF) del ciudadano. Por defecto, elnombre de este campo será “j_username”.

• Input de tipo “password” para recoger la contraseña del ciudadano. Por defecto, el nombre de estecampo será “j_password”.

• Input de tipo “text” para recoger un CIF, en el caso de que el cuidadano represente a una personajurídica. El nombre de este campo será “cif”

Login de ciudadano a través de agente telefónico

En este tercer escenario, un ciudadano accede a una aplicación a través de un agente telefónico. Eneste caso, la página de login no pedirá la contraseña completa. En su lugar, se solicitarán caracterescorrespondientes a posiciones de la contraseña del ciudadano, seleccionados de forma aleatoria.

Los campos de entrada que se deben definir en este caso son:

• Input de tipo “text” para recoger el identificador de usuario (NIF) del ciudadano. Por defecto, elnombre de este campo será “j_username”.

• Input de tipo “text” para recoger un CIF, en el caso de que el cuidadano represente a una personajurídica. El nombre de este campo será “cif”

• Un campo de tipo “hidden” para indicar las posiciones de la contraseña que se solicitan. El nombrepara estos campos será PASSWORD_POSITIONS.

• Un campo de tipo “text” para recoger cada una de las posiciones de la contraseñaque se deben suministrar. Por defecto, el nombre para cada uno de estos campos será,“PASSWORD_POSITIONS”+posición.

A continuación se muestra un ejemplo de cómo generar los campos de solicitud de posiciones de clavecon etiquetas JSP:

<table><logic:iterate id="pos" name="PASSWORD_POSITIONS"> <tr> <td>Introduzca la posicion <%=pos%><input type="hidden" name="PASSWORD_POSITIONS" value="<%=pos%>"/></td> <td><input type="text" name="PASSWORD_POSITIONS_<%=pos%>" size="1" maxlength="1"/> </td> </tr> </logic:iterate></table>

Page 108: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

100

Login de ciudadano a través de colaborador

En este escenario (tipo actor COLABORADOR), un ciudandao accede a un recurso a través de un agenteintermediario. Este escenario requiere la autenticación tanto del ciudadano como del propio agenteintermediario. La autenticación del agente colaborador se realiza con un par usuario/contraseña. Laautenticación del ciudadano, se realiza con el par NIF / posiciones de la contraseña.

Los campos requeridos en este caso son:

• Identificador (username) del colaborador. Se trata de un input de tipo “text” cuyo nombre pordefecto será “j_username”.

• Contraseña del colaborador. Debe ser un input de tipo “password”. Su nombre por defecto es:“proxyPassword”.

• NIF/NIE del ciudadano. Será un input de tipo “text” cuyo nombre por defecto es “nif”.

• CIF. Si el ciudadano representa a una persona jurídica, este campo recogerá si CIF. Será un campo inputde tipo “text” y nombre: “cif”.

• Posiciones de la contraseña. Las posiciones de la contraseña del ciudadano se recogerán en n camposinput text de nombre “j_password_x”, siendo x la posición solicitada de la contraseña.

Para facilitar la implementación de este tipo de páginas, el filtro de autenticación deja los siguientesatributos en el scope session:

• SecurityGlobals.LOGIN_EXCEPTION. Excepción disparada que provoca la aparición de lapágina de login.

• SecurityConstants.USERNAME. Nombre de usuario del colaborador introducido en intentos delogin anteriores, erroneos, (si hubo).

• SecurityConstants.PASSWORD. Contraseña del colaborador introducida en intentos anteriores,erroneos, de login (si hubo).

• SecurityConstants.NIFNIE. NIF del ciudadano introducido en intentos anteriores, erroneos, delogin (si hubo).

• “PASSWORD_POSITIONS”. Lista de objetos de tipo PropertyBean que contienen, como valor(getValue()) el numeor de posición que se debe solicitar y como label (getLabel()) el valorintroducido en intentos anteriores, erróneos, si hubo.

<?xml version="1.0" encoding="iso-8859-1"?>

<!--No se puede poner DTD. Si se pone DTD no funciona.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">-->

<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %><%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %><%@ taglib uri="/WEB-INF/princast.tld" prefix="princast" %><%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %><%@ page import="es.princast.framework.web.filter.security.common.SecurityGlobals" %><%@ page import="es.princast.framework.facilities.security.SecurityConstants" %><%@ page import="es.princast.framework.core.vo.PropertyBean" %>

Page 109: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

101

<%@ page import="es.princast.framework.web.filter.security.common.SecurityGlobals" %>

<html:html locale="es" xhtml="true" > <head> <html:base /> <title> Bienvenido al Principado de Asturias </title> <link type="text/css" rel="stylesheet" href="css/general.css" /> <link type="text/css" rel="stylesheet" href="css/components.css" /> <link type="text/css" rel="stylesheet" href="css/login.css" /> </head>

<body onload="document.forms[0].j_username.focus()">

<princast:login> <princast:panel layouterClass="es.princast.framework.web.view.layout.DivLayouter"> <princast:panel-legend>Login</princast:panel-legend> <princast:block> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions.WrongPasswordException"> <div id="errors"> <ul> <li>Validaci&oacute;n incorrecta de NIF/NIE y Clave personal</li> </ul> </div> </princast:instanceOf> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions.InsufficientDataException"> <div id="errors"> <ul> <li>Introduzca todos los datos que se solicitan</li> </ul> </div> </princast:instanceOf> </princast:block> <princast:block cssClass="login_message"> <p>Bienvenido a la página de login de colaborador de ejemplo</p> </princast:block> <princast:block cssClass="login_inputs"> <p>Identificador de Colaborador</p> <input type="text" name="j_username" id="j_username" value="<%=session.getAttribute(SecurityConstants.USERNAME)!=null ? session.getAttribute(SecurityConstants.USERNAME) :""%>"/> </princast:block> <princast:block cssClass="login_inputs"> <p>Contraseña del Colaborador</p> <input type="password" name="proxyPassword" id="proxyPassword" value= "<%=session.getAttribute(SecurityConstants.PASSWORD) != null ? session.getAttribute(SecurityConstants.PASSWORD) : "" %>"/> </princast:block> <princast:block cssClass="login_inputs"> <p>NIF/NIE del ciudadano</p> <input type="text" id="nif" name="nif" value= "<%=session.getAttribute(SecurityConstants.NIFNIE)!=null ? session.getAttribute(SecurityConstants.NIFNIE) : "" %>"/> </princast:block> <princast:block cssClass="login_inputs"> <p>Si representa a una persona jurídica, introduzca su CIF</p>

Page 110: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

102

<input type="text" id="cif" name="cif"/> </princast:block> <princast:block cssClass="login_inputs"> <p>Introduzca las siguientes posiciones de la contraseña del ciudadano:</p> <logic:iterate id="pos" name="PASSWORD_POSITIONS"> <table> <tr> <td style="text-align: left; width:90%"><span class="textos">Introduzca la posicion <%= new Integer(((PropertyBean) pos).getValue()).intValue()+1%><input type="hidden" name="j_password" value="<%=((PropertyBean) pos).getValue()%>"/></span></td> <td style="text-align: right; width:10%"><input type="text" name="j_password_<%=((PropertyBean) pos).getValue()%>" value="<%=((PropertyBean) pos).getLabel()%>" size="1" maxlength="1"/> </td> </tr> </table> </logic:iterate> </princast:block> <princast:block cssClass="login_submit"> <p> <input type="image" src="images/botones/enter-button.gif" alt="Entrar"/> <input type="image" src="images/botones/right-arrow.gif" alt="Entrar"/> </p> </princast:block> </princast:panel> <princast:login-footer> Por favor, si no va a utilizar la aplicación cierre la sesion para evitar que otros entren con su perfil </princast:login-footer> </princast:login>

</body></html:html>

La página HTML correposndiente al JSP del ejemplo anterior se puede ver en la imagen siguiente:

Excepciones durante el proceso de autenticación

En caso de que durante el proceso de autenticación se lance alguna excepción, ésta es almacenadaen la sesión del usuario bajo la clave LOGIN_EXCEPTION, que se puede encontrar en la claseSecurityGlobals.

Pudiera darse el caso de querer mostrar un determinado mensaje de error en la capa de presentaciónen función de la excepción que se haya producido. Para ello se proporciona un tag JSP denominada<instanceOf> y que recibe los siguientes parámetros:

variableName Nombre de la clave bajo la cual se busca la excepción.

className Nombre cualificado de la excepción con la que se realiza la comprobación.

Un ejemplo de uso de esta etiqueta se presenta a continuación:

<princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions.WrongPasswordException"> <div id="errores"> <ul> <li>Validaci&oacute;n incorrecta de NIF/NIE y Clave personal</li> </ul> </div></princast:instanceOf>

Page 111: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Seguridad en aplicacionescon openFWPA

103

Como se puede apreciar se comprueba que la excepción que seencuentra almacenada en la clave SecurityGlobals.LOGIN_EXCEPTION seade la misma clase que es.princast.framework.facilities.security.exceptions.WrongPasswordException. En caso afirmativose incluyen los elementoscontenidos entre <princast:instanceOf></princast:instanceOf>. Las excepcionesque se pueden disparar durante el proceso de autenticación, se encuentran en el paquete:es.princast.framework.facilities.security.exceptions.

Niveles de Seguridad

Para la autenticación se contemplan tres niveles de seguridad:

Nivel 0 Con este nivel de seguridad no se requiere autenticación.

Nivel 1 Con este nivel es necesaria la validación mediante usuario y contraseña. Para ello, debenespecificarse dos parámetros denominados j_username y j_password con dichainformación.

Nivel 2 Este nivel de seguridad se corresponde a la autenticación utilizando certificado digital.Debe tratarse de un certificado digital válido de la FNMT (Fábrica Nacional de la Moneday Timbre). Para forzar a esta validación debe incluirse en la petición http un parámetrodenominado forceLevel con el valor 2.

Un ejemplo de uso de este nivel de seguridad se presenta a continuación:

http://localhost:8888/prueba/action/suscribe?forceLevel=2

Para mayor información acerca de los niveles de seguridad soportados acúdase a la documentación delMódulo de Login que esté utilizando para su aplicación.

Single Sign OnCon el fin de facilitar la integración de aplicaciones en un mismo portal, el Principado de Asturias disponede una solución Single Sign On (SSO), basada en el producto Novell iChain.

El sistema Single Sign On permite a un usuario, acceder a varias aplicaciones autenticándose una únicavez, valiendo esta autenticación para todas ellas.

Actualmente, se pueden autenticar contra el sistema SSO todos los usuarios dados de alta en el LDAPcorporativo del Principado de Asturias.

El filtro de seguridad del openFWPA soporta, a partir de la versión 1.5 , de forma automática, autenticaciónbasada en el SSO del Principado de Asturias para todos los escenarios de autenticación de Nivel 1 (salvolos que utilizan posiciones de contraseña).

Atención

Para que el filtro de seguridad se pueda integrar correctamente con SSO, éste debe enviar, en lacabecera de la petición http (header) el par usuario/contraseña codificado en Base64, utilizandoes esquema básico (Basic) de autenticación http.

Para utilizar el SSO en versiones anteriores a la 1.5, se debe utilizar el "parche para compatibilidad conSSO".

Page 112: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

104

Capítulo 8. Integración de Sistemas

Tecnologías de IntegraciónFrecuentemente, las aplicaciones desarrolladas con el openFWPA se deben integrar con los sistemascorporativos del Principado de Asturias (bases de datos, módulos comunes, etc). El mecanismo estándarpara la interconexión de sistemas, en el Principado de Asturias, es el intercambio de documentos XMLsobre el protocolo HTTP 1.1.

La estructura de los documentos a intercambiar, se ajusta a la DTD de XML Genérico (para másinformación solicítese documento correspondiente al Principado de Asturias).

<?xml version="1.0" encoding="ISO-8859-1" ?> <SERVICIO siscliente="POR_DEFINIR" sisservidor="BDGENERICOS" nomservicio="CONS_COD_PAIS" fecha="25/06/2003 13:46:46"> <ENTRADAS> <entrada dato="CN_PAIS">108</entrada> </ENTRADAS> <SALIDAS> <ERROR> <salida dato="COD">0</salida> <salida dato="DESC">OK</salida> </ERROR> <salida dato="PAIS">ESPAÑA</salida> </SALIDAS></SERVICIO>

En el recuadro superior, se muestra un ejemplo de XML Genérico recibido como respuesta de la consultadel país, cuyo código es 108. La respuesta, sin errores, es: “ESPAÑA”.

El openFWPA ofrece soporte para la integración de aplicaciones con otros sistemas del Principado deAsturias, utilizando tanto intercambio de XML Genérico, como servicios web (allá donde se apliquen).

Los componentes del openFWPA que facilitan la integración entre sistemas, implementan el interface:es.princast.framework.facilities.backends.BackEndProxy.

Todos los clientes, que proporciona el openFWPA para la integración con los sistemas corporativosdel Principado de Asturias son configurables. Es decir, pueden ser configurados en caliente a travésdel sistema de configuración del openFWPA o, dicho de otra forma, implementan el interfaceConfigurationListener.

XML Genérico: ConfiguraciónLa mayoría de los sistemas del Principado de Asturias se comunican entre sí mediante el intercambio deXML Genérico, por esta razón, los componentes del openFWPA que trabajan con este tipo de documentosXML son habituales.

Los clientes de sistemas corporativos que utilizan intercambio de XMLGenérico se configuran siguiendoel mismo proceso:

1. Cada cliente dispone de un nombre de contexto identificativo. Por ejemplo: “GENERICOS”, “SMS”,“CLAVES”, etc.

Page 113: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Integración de Sistemas

105

2. Se debe definir un plug-in de configuración de tipo PropertiesFileConfigurationPluginen el fichero princast-init-script.xml, que accede a un fichero de configuración (un fichero.properties) específico para el cliente. Este plugin, se debe registrar para el contexto identificativodel cliente.

3. Definir en el fichero de properties los siguientes parámetros:

• CLIENT_SYS. Identificador de la aplicación cliente. Se trata de una cadena que identifique a laaplicación cliente que solicita el servicio.

• SERVER_SYS. Identificador del sistema servidor. Es una cadena identificativa del sistema que recibela petición. El identificador de cada sistema concreto debe solicitarse al Principado de Asturias.

• DTD. URL donde está publicada la DTD del documento XML Genérico que se envía. Se debe solicitaral Principado de Asturias la URL de la DTD que se debe utilizar para cada proyecto concreto.

• URL. Dirección del servicio al que se debe enviar el XML Genérico.

• USER. Nombre de usuario. Se utiliza para autenticar la petición de servicio.

• PASSWORD. Contraseña de acceso. Junto al parámetro USER, se utiliza para la autenticación de lapetición.

• CONNECTION. Nombre completamente cualificado de la clase que se utilizará para establecer laconexión con el sistema. Este parámetro es opcional. Las conexiones existentes actualmente son:

XML_generico.ConexionXML Establece una conexión HTTP 1.1. con el sistema servidor. Esel tipo de conexión por defecto.

es.princast.framework.facilities.backends.genericXML.connection.FrameworkConnectionXML

Establece una conexión con el sistema, sin efectuar validaciónde la DTD. Se recomienda su uso únicamente en entornos conconectividad limitada.

Realiza URL encode del contenido a enviar al sistema XMLGenérico con el que interactua.

es.princast.framework.facilities.backends.genericXML.connection.FrameworkConnectionXMLNoEncoding

Establece una conexión con el sistema, sin efectuar validaciónde la DTD. Se recomienda su uso únicamente en entornos conconectividad limitada.

Esta conexión es igual a la anterior con la salvedad de que nose realiza el URL encoding. Es el tipo de conexión a utilizarcuando el sistema a conectar sea BDGENERICOS.

es.princast.framework.facilities.backends.genericXML.connection.MockConnectionXML

Si se utiliza este tipo de conexión, no se producirá ningunacomunicación real con el sistema cliente. La conexión seráúnicamente simulada. Este tipo de conexión se debe utilizarúnicamente para pruebas de desarrollo.

Page 114: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

106

Capítulo 9. Pruebas

Pruebas unitariasToda aplicación J2EE desarrollada utilizando el openFWPA debería tener presente, en todo momento, larealización de pruebas unitarias de todos sus componentes relevantes. De esta forma se tiene una bateríade pruebas que pueden ejecutarse en cualquier momento como pruebas de regresión. Debiera disponersede un proceso en el servidor que se encargue de lanzar tales pruebas un número determinado de veces ygenere un informe con los resultados de la ejecución de las pruebas.

Todo esto se ha tenido en cuenta a la hora de desarrollar el openFWPA y se proporciona una infraestructurabasada en JUnit [11] que se puede utilizar a tal efecto. Esta infraestructura base para la implementaciónde pruebas unitarias se empaqueta en la librería de herramientas del openFWPA: fwpa-toos.jar

Se proporciona un conjunto de clases plantilla para facilitar la implementación de pruebas unitarias. Estasclases de prueba se pueden clasificar en dos grandes grupos:

• Pruebas locales. Se ejecutan en el propio PC de desarrollo.

• Pruebas en contenedor. Se ejecutan en una aplicación web, sobre el servidor de aplicaciones. ElopenFWPA proporciona soporte para la implementación de pruebas unitarias en contenedor, utilizandola librería Cactus, de Apache.

Jerarquía de clases para las pruebas unitariasEn función del tipo de prueba unitaria que se desee realizar (prueba de bases de datos, de Actions Struts,etc.) se dispone de una clase base específica que debe ser extendida. Las clases base para pruebas unitariassuministradas en openFWPA se muestran en la figura que sigue:

Figura 9.1. Set de pruebas unitarias disponibles

• PrincastTestCase. Se utiliza para la implementación de pruebas unitarias para clases estándar.

• PrincastContextTestCase. Se utiliza para probar beans de Spring.

• PrincastJMXComponentTestCase. Para la implementación de pruebas de MBeans JMX.

• PrincastDatabaseTestCase. Se debe extender para implementar pruebas unitarias decomponentes que acceden a bases de datos.

• PrincastDAOTestCase. Se debe extender para implementar pruebas unitarias de DAOs (DataAccess Objects).

• PrincastReportTestCase. Se utiliza para probar generación de reports.

• PrincastMockStrutsTestCase. Se utiliza para definir pruebas unitarias de Actions fuera delcontenedor.

• PrincastActionTestCase. Esta clase se utiliza para implementar pruebas unitarias de ActionsStruts que se ejecuten en contenedor.

Page 115: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

107

• PrincastJspTestCase. Se utiliza para implementar pruebas para páginas JSP o para etiquetas(Custom Tags). Este tipo de pruebas se ejecuta en contenedor.

• PrincastFilterTestCase. Se utiliza para pruebas unitarias de filtros (Servlet Filter). Este tipode pruebas se ejecuta en contenedor.

• PrincastServletTestCase. Se utiliza para probar Servlets. Este tipo de pruebas se ejecutaen contenedor.

• PrincastWebTestCase. Se utiliza para probar la navegación Web, envío y respuesta deformularios, etc. Este tipo de pruebas se ejecuta en contenedor.

Convenciones a seguir

A la hora de implementar las diferentes pruebas unitarias se recomienda que las clasessigan el convenio de nombrado masivamente seguido que establece que éstas deben llamarse<nombre_de_la_clase>Test.java.

En cuanto a la signatura de los métodos que implementen los diferentes casos de pruebas, se suele emplear:

public void test<nombre_caso_prueba> () {};

JUnit reconoce como casos de prueba todos aquellos métodos que sigan esta convención de nombrado.

Ejecución de las pruebas unitarias

Con el fin de facilitar la labor de ejecución de los diferentes tests, en el fichero build.xml de laaplicación en blanco que se proporciona con el openFWPA existe un target de Ant [10] que se encargade esto.

Su nombre es test.unit y se encarga de ejecutar todos los tests cuyo nombre de clase termina en Test ygenera una serie de informes HTML en la carpeta test\reports con los resultados de la ejecuciónde los mismos.

Para la ejecución de los tests tan sólo es necesario el siguiente comando en línea de comandos:

ant test.unit

o bien, la forma análoga desde el entorno de desarrollado integrado que se use.

Consultando los resultados de los tests

Una vez ejecutadas las pruebas unitarias, se pueden consultar los resultados de las mismas en una serie deinformes HTML que son guardados en la carpeta test\reports.

En la captura siguiente, se puede ver un ejemplo de informe del resultado de la ejecución de una bateríade tests.

En la imagen siguiente se puede ver el informe detallado de una de las pruebas pertenecientes a la bateríade pruebas anterior.

Page 116: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

108

Pruebas unitarias de objetos que acceden a bases dedatos.

Para una aplicación web es muy recomendable realizar pruebas unitarias para todos los objetos de accesoa bases de datos (DAOs), de esta forma se garantiza que el back-end de la aplicación está correctamenteimplementado y se agiliza en muy buena medida el desarrollo de la capa web.

Las pruebas unitarias de acceso a bases de datos, se realizan siempre fuera del contendor. Esto es lógico yaque en un buen diseño nunca un componente de acceso a BD tendrá dependencias respecto al contendordonde se ejecuta.

Para realizar pruebas de clases que deben acceder a una BD, se utilizará la clase de testPrincastDatabaseTestCase. Si estos objetos son DAOs de una aplicación, siempre se puedeutilizar la subclase PrincastDAOTestCase que ofrece más funcionalidad para la prueba de este tipode objetos.

Las pruebas unitarias de objetos que acceden a bases de datos, están basadas en el framework Spring.Spring puede simular el acceso a la base de datos utilizando transacciones serializables que son canceladasuna vez termina la prueba. Por este motivo, es importante que las tablas involucradas en las pruebas quese implementan, dispongan de soporte para transacciones (por ejemplo, las tablas de tipo MyISAM, pordefecto en MySQL, no soportan transacciones, sin embargo las tablas de Oracle o las tablas InnoDB deMySQL sí que las soportan).

Atención

Desde la versión 1.5 del openFWPA, las pruebas unitarias de acceso a base de datos están basadasen el framework Spring.

El hecho de que este tipo de pruebas unitarias esté basado en Spring supone que:

1. Es necesario declarar los DataSources que se van a utilizar en un fichero de definición de beansde Spring.

2. No es necesario controlar las transacciones que se realicen durante el test.

3. La clase de prueba es, a su vez, un bean de Spring. La clase de pruebas realizará autowiring por tipopara todas sus propiedades setter. Es decir, para todos los setter que tenga la clase de pruebas busca, enlos ficheros de definición de beans, un bean que encaje en el tipo de la propiedad y se lo asigna.

Atención

Es muy importante tener en cuenta que el autowiring de beans con la clase de test se realizapor tipo. Si más de un bean encaja en el tipo de la propiedad puede producirse un error. ElDataSource a utilizar también se asigna a la clase de test utilizando autowiring, por lo tanto,únicamente se puede declarar un DataSource en los ficheros de definición de beans.

Para implementar tests extendiendo PrincastDatabaseTestCase, se seguirán todas las normasy convenciones definidos para implementar casos de prueba estándar (Ver “Convenciones a seguir”).Además, se debe implementar el método:

• getDataSourceFile(), que devuelve el path donde se encuentra el fichero de definción de beansen el que se declara el DataSource a utilizar. Por ejemplo:

protected String getDataSourceFile() { return "classpath*:/es/princast/framework/facilities/dao/datasource-beans.xml";

Page 117: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

109

}

La clase base PrincastDatabaseTestCase proporciona las siguientes utilidades:

• logger. Como todos los tests, se dispone de un atributo, de nombre logger, que permite accederal log de la clase de prueba.

• getDataSource(). Este método permite obtener la fuente de datos (DataSource) que conectacon la base de datos. Este origen de datos se puede asignar, posteriormente, a los objetos que se vayana probar.

• jdbcTemplate. La clase base para pruebas en base de datos dispone de un atributo, de la claseorg.springframework.jdbc.core.JdbcTemplate, que permite ejecutar, directamenteconsultas contra la base de datos. Para más información sobre cómo usar esta clase, consúltese el manualde referencia del framework Spring.

• onSetUpInTransaction(). Extendiendo este método, se pueden realizar todo tipo deactualizaciones (inserciones, borrados, etc.) que sean necesarios para la posterior ejecución de la prueba.Todas las actualizaciones que se realicen estarán disponibles, únicamente durante la ejecución del test.No quedará rastro de estas operaciones en la base de datos.

Este método sustituye al fichero DataSet XML de dbUnit. Si se utiliza este método, y se quiere utilziartambién dbUnit, se debe hacer una llamada a super.onSetUpInTransaction().

• onSetUpBeforeTransaction(). Este método es análogo al anterior con al diferencia de que lasactualizaciones realizadas en este método sí que son persistentes en la base de datos.

Un ejemplo de uso de estos elementos es el que sigue:

protected void onSetUpInTransaction() throws Exception {

jdbcTemplate.execute("insert into foo values ('1', 'foo foo')"); }

Utilizando dbUnit con PrincastDatabaseTestCase.

Es posible utilizar la librería dbUnit (http://www.dbUnit.org) con la clasePrincastDatabaseTestCase. Para ello, se deben realizar los siguientes pasos:

1. Escribir el fichero XML (DataSet XML) de datos para la prueba. El fichero DataSet XML estádefinido por la librería DBUnit y tiene el formato que se muestra en el siguiente ejemplo:

<dataset><PROPS id='1' value='pepe'/><PROPS id='2' value='ramon'/></dataset>

Cada registro de la base de datos se escribirá en una etiqueta independiente, indicando, el valor de cadacampo, como atributos de dicha etiqueta.

En estos test siempre se puede suponer que la base de datos está cargada con los registros definidosen el fichero DataSet XML. Al finalizar los tests, la base de datos, siempre volverá a su estado inicial(antes de lanzar los test).

2. Extender el método getDataSetXML() para especificar el path de dicho fichero. Este path siempredebe ser relativo al classpath.

Page 118: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

110

3. Indicar la operación a realizarse por defecto para incializar los tests. Se debe extender el métodogetDatabaseSetupOperation(). Las operaciones soportadas son:

DatabaseOperation.CLEAN_INSERTElimina todo lo que haya en la tabla e inserta los datos del ficheroXML obtenido con el metodo getDataSetXMLFile().

DatabaseOperation.INSERT Inserta los datos del fichero XML

DatabaseOperation.UPDATE Actualiza los datos de la base de datos con los proporcionadospor el fichero XML.

DatabaseOperation.REFRESH Inserta los registros del fichero XML que no existan en la basede datos. Actualiza los que ya existan previamente.

DatabaseOperation.NONE No se realiza ninguna operación con la base de datos.Estas operaciones solamente tienen efecto durante la ejecución del test. No se realizarán cambiospermanentes en la base de datos.

Por defecto, este método devuelve la operación: DatabaseOperation.CLEAN_INSERT.

4. Configurar las conexiones (DataSource) como cualquier otro tipo de prueba unitaria de base dedatos.

Pruebas unitarias de DAOs

Por habitual, un caso especial de las pruebas unitarias que acceden a bases de datos, es el de las pruebasunitarias de DAOs.

Para este tipo específico de pruebas, se proporciona una clase (que extiende dePrincastDataBaseTestCase) llamada: PrincastDAOTestCase.

Atención

La clase PrincastIBatisTestCase, existente en versiones del openFWPA anterioresa la 1.5, cuyo objetivo era probar DAOs implementados con iBatis, ha sido eliminada delopenFWPA. El uso de la clase PrincastDAOTestCase permite probar cualquier tipo deDAOs, independientemente de la tecnología con que se implementen (por supuesto incluyendoiBatis).

Todas las características propias de la superclase (PrincastDataBaseTestCase) se aplican aPrincastDAOTestCase, incluyendo, por supuesto, la gestión y obtención de DataSources.

Los objetos DAO a probar se deben declarar en un fichero de definción de beans de Spring. La ubicaciónde este ficho se proprocionará extendiendo el método: getDaoFile().

Figura 9.2. Autowiring de DAOs y DataSources

Para obtener instancias de los DAOs a probar, la clase de pruebas debe definir un método setterpara cada uno de ellos. La clase base (PrincastDatabaseTestCase) se encargará de asignar aestas propiedades los DAOs, declarados en el fichero de definción de beans, cuyo tipo sea compatible(autowiring por tipo).

Es importante tener en consideración que, si más de un bean es compatible con el tipo de una propiedaddel test, se puede producir un error.

/**

Page 119: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

111

* Clase para probar DAOs (interface <code>PrincastDAO</code>). * * @see PrincastDAO * */public class PrincastDAOTest extends PrincastDAOTestCase {

/** * El objeto a probar */ protected FooDAO2 dao;

/** * Establece el DAO que se va a probar. * No hace falta llamar directmente a este método. Spring se encargará, al lanzar la prueba * de asignar el DAO, utilziando autowiring por tipo * * @param dao el DAO a probar */ public void setFooDAO2(FooDAO2 dao) { this.dao = dao; }

protected String getDaoFile() { return "classpath*:/es/princast/framework/facilities/dao/mock/mock-dao-beans.xml" ; }

protected String getDataSourceFile() { return "classpath*:/es/princast/framework/facilities/dao/datasource-beans.xml"; }

protected void onSetUpInTransaction() throws Exception { jdbcTemplate.execute("insert into foo values ('10', 'foo1')"); jdbcTemplate.execute("insert into foo values ('20', 'foo2')"); }

/** * Prueba una consulta. * * @throws Exception */ public void testSelect() throws Exception {

assertNotNull(dao);

List l = dao.getFooList("20"); assertNotNull(l); assertEquals(1, l.size());

Map m = (Map) l.get(0); assertEquals("foo2", m.get("nombre")); }

}

Page 120: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

112

Con solamente implementar el método setFooDAO2(), al ejecutar el test se asignaráautomáticamente cualquier bean de la clase FooDAO2.En el método getDaoFile() se debe definir la ubicación del fichero de definición de beans dondese declaran los DAOs. En este caso, se llama mock-dao-beans.xml, está ubicado en el classpathy su contenido es el siguiente:

<beans> <bean id="foo2" autowire="byType" class="es.princast.framework.facilities.dao.mock.FooDAO2" /></beans>Con el método getDataSourceFile() se define la ubicación del fichero de declaración de beansdonde se especifica el DataSource a utilizar. En este ejemplo, el fichero se llama datasource-beans.xml, está ubicado en el classpath y su contenido es el que sigue:

<beans> <!-- DataSource para Test --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property> <property name="url"><value>jdbc:mysql://localhost/foo</value></property> </bean> <!-- Es necesario también definir un TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"><ref bean="dataSource"/></property> </bean></beans>Todas las operaciones realizadas sobre la base de datos en este método será unicamente visibles alo largo del test.Finalmente, se puede realizar el test normalmente utilizando los beans declarados en los ficheroscorrespondientes.

Pruebas unitarias de Spring BeansSe ha incluido una clase base (PrincastContextTestCase) para la implementación de pruebasunitarias de objetos definidos como beans de Spring.

Este tipo de pruebas unitarias son de utilidad siempre que se quieran probar objetos (o estructuras deobjetos), creados, inicializados y construidos por Spring a través de uno o varios ficheros de definiciónde beans.

Para definir el path (o paths) donde se realizará la búsqueda de los ficheros de definición de beans(estos paths siempre serán relativos al classpath, involucrados en la prueba, se debe sobreescribir elmétodo:getDefinitionsPath(). Si este método no se sobreescribe, el path de búesqueda por defectoserá: "/beans/**/*-beans.xml".

En cualquiera de los métodos de la prueba unitaria, es posible acceder a cualquier bean (definido encualquiera de los ficheros de configuración) utilizando el método: getBean(), especificando comoparámetro el nombre del bean a buscar.

Pruebas unitarias de objetos ConfigurablesUn tipo especial de objetos son los que implementan el interface Configurable. Este tipo de objetosrequiere que, para poder ser utilizados, esté activo todo el sistema de configuración del openFWPA. Enlas pruebas unitarias, el runtime del openFWPA no suele estar activo, por este motivo, es necesario,si se quieren hacer pruebas unitarias de objetos Configurables, tener preconfigurado el objetoFrameworkConfigurator.

Page 121: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

113

Con el fun de facilitar la pre-configuración de objetos Configurables, de cara a su prueba unitaria,se ha incluido en el openFWPA una clase (ConfigurableObjectTestingHelper) que permiterealizar esta preconfiguración de una forma fácil.

Esta clase dispone de las siguientes operaciones:

loadParameters Carga en el FrameworkConfigurator un conjunto de parámetros. Estemétodo se utiliza para establecer un estado inicial (pre-configurar) el Sistemade Configuración.

loadPropertiesFile Esta familia de métodos es similar a la anterior con la diferencia de que losparámetros se cargan de un fichero de properties.

loadPlugin Pre-configura el Sistema de Configuración con un plug-in.

configureObject Configura un objeto con un conjunto de parámetros. Estos métodos,pre-cargan el Sistema de Configuración y, posteriormente, configuran elobjeto. La configuración unicamente se puede hacer indicando un conjuntode parámetros (como Properties), para realizar configuraciones mássofisticadas (varios contextos, plugins espcíficos, etc.) deberían utilizanrselos métodos anteriores.

Properties props = new Properties(); props .put("HIT.COUNTER", "es.princast.framework.core.management.mcounters.simple.SimpleCounter"); props .put("EXCEPTION.COUNTER", "es.princast.framework.core.management.mcounters.simple.SimpleCounter"); props .put( "[email protected]", "es.princast.framework.core.management.mcounters.historic.HistoricalCounter");

ConfigurableObjectTestingHelper.configureObject(props, CounterFactory.getFactory());

Pruebas unitarias en contenedorEn el openFWPA, las pruebas unitarias en contenedor se realizan utilizando la librería Cactus. Las pruebasunitarias con Cactus se ejecutan, parte en el cliente y parte en el servidor. Un breve resumen de los pasosnecesarios para ejecutar pruebas unitarias con Cactus es:

1. Escribir el fichero cactus.properties.

2. En el fichero web.xml declarar y mapear el objeto [Servlet|Filter|Jsp]Redirector.

Antes de escribir los tests, es importante recordar que los métodos que se ajustan al patrón testXXX() seejecutan en el servidor (por lo tanto, todas las sentencias de log que en ellos se escriban aparecerán en el logdel servidor y no en la consola) y los método que se ajustan a los patrones beforeXXX() y endXXX(),se ejecutan en el cliente (las sentencias de log aparecerán en la consola y no en el log del servidor).

Pruebas de filtros

Para facilitar las pruebas unitarias de filtros, la clase base PrincastFilterTestCase, ofrece los siguientesmétodos de utilidad:

Page 122: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

114

assertPassingThruFilter() Sirve para verificar que el flujo que pasa por una cadena de filtrosno se ha interrumpido. No se ha realizado ni redirect, ni forward.

assertForwardTo() Verifica que alguno de los filtros en la cadena ha realizado unaredirección a través de un forward.

Pruebas de validadores

Se ha incluido una clase base para facilitar la implementación de pruebas de validadores de formularios.Este tipo de pruebas se ejecutan en contenedor.

La clase base que debe ser extendida es PrincastValidatorTestCase. Esta clase pone a disposición de susclases hijas los siguientes atributos:

request La petición http que se está utilizando

errors Un conjunto de ActionErros de prueba

va Una instancia de la clase ValidatorAction para probar

Estos tres atributos se pueden utilizar para realizar llamadas a los métodos de validación (sin necesidadde disponer de instancias con valores reales).

Si se necesitan unos valores específicos para estos atributos, se pueden extender los métodoscreateValidatorAction() y createErrors(), de la clase base.

public void testValidateTwoFieldsOk() throws Exception {

FooForm form = new FooForm(); form.setDate1("21/11/2005"); form.setDate2("21/11/2005");

Field field = configureField();

assertTrue(FieldValidations.validateTwoFields(form, va, field, errors, request)); }

Pruebas unitarias de informesLas pruebas unitarias para informes permiten generar y previsualizar reports (utilizando JasperReports ylas clases del openFWPA), sin necesidad de que éstos se ejecuten en el contenedor.

La clase base para este tipo de pruebas unitarias es PrincastReportTestCase. Define los siguientesmétodos:

getReportName() Este método abstracto debe ser extendido por las subclases para especificarel nombre del informe. El nombre del informe se utilizará para encontrar elfichero de definción del mismo (con el convenio de nombrado <nombre delinforme>.xml) y para nombrar el fichero PDF resultante (con el convenio<nombre del informe>.pdf).

getOutputDir() Las subclases pueden extender este método para especificar el directoriodonde se dejará el fichero PDF generado. Por defecto, vale "" (cadenavacía) que dejará el informe en el directorio raíz del proyecto.

Page 123: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

115

getInputDir() Las subclases deberían extender este método para especificar el directoriodonde se encuentra el fichero XML que define el informe. Por defecto,este directorio será "reports", siendo el path relativo al directorio raíz delproyecto. Es posible especificar paths absolutos o relativos al classpathutilizando el prefijo "classpath://".

generateReport() Gener el PDFProvider correspondiente al informe para ser utilizado en laspruebas.

generatePDFReport() Genera el informe y lo exporta al fichero PDF.

public class ProductosReportTest extends PrincastReportTestCase {

protected String getReportName() { return "productosReport3"; }

protected String getInputDir() { return "extras/sampleapp/src/reports"; }

/** * Prueba la generación de un informe sencillo * @throws Exception */ public void testReportGen() throws Exception {

//Obtener origen de datos List data = new LinkedList();

data.add(new ProductoVO(1, "fooName1", "fooDescr1", 1.0, "url1")); data.add(new ProductoVO(2, "fooName2", "fooDescr2", 2.0, "url2")); data.add(new ProductoVO(3, "fooName3", "fooDescr3", 3.0, "url3"));

//Obtener parametros HashMap parameters = new HashMap(); parameters.put("P_Titulo", "Titulo Fooo"); parameters.put("P_AmpliacionTitulo", "Ampliacion Titulo Fooo");

generatePDFReport(data, parameters); }

}

El código superior se puede encontrar en la aplicación de ejemplo SampleApp.

Más información sobre la implementación de pruebasunitarias

Dado que los diferentes tipos de pruebas unitarias que se han presentado con anterioridad están basadosen proyectos de terceros, la mejor forma de conseguir información detallada de los mismos es acudiendoa los sitios web de estos.

Por ello, a continuación se presentan los enlaces a las guías de desarrollo de cada uno de los proyectos:

Page 124: Manual de desarrollo de aplicaciones J2EE (0.5Mb)

Pruebas

116

• Proyecto JUnit [http://junit.sourceforge.net/]

• Proyecto StrutsTestCase [http://jtestcase.sourceforge.net/]

• Proyecto DbUnit [http://dbunit.sourceforge.net/howto.html]

• Cactus [http://jakarta.apache.org/cactus]

Pruebas de RendimientoLas pruebas de rendimiento de aplicaciones web, se deben realizar, de forma automatizada, utilizandoherramientas inyectoras de carga. La herramienta recomendada para aplicaciones desarrolladas con elopenFWPA es OpenSta[22].

Modo de Prueba de RendimientoLa herramienta OpenSta no soporta la automatización de tests sobre el protocolo https. Es ncesario que todala aplicación corra sobre http. Debido a que el protocolo utilizado (http/https) es ajeno a la programación dela aplicación (en realidad, su uso está gestionado por el framework), para realizar pruebas de rendimiento,basta con poner el parámetro de configuración PERFORMANCE.TEST.MODE al valor ON. Este parámetrose puede definir en cualquier plug-in de configuración que sirva parámetros al contexto de seguridad:SECURITY.